diff --git a/README.md b/README.md index 5fa6150d2be02..91f2ed19fb453 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ [![Open Source Helpers](https://www.codetriage.com/magento/magento2/badges/users.svg)](https://www.codetriage.com/magento/magento2) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/magento/magento2?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/magento-2/localized.svg)](https://crowdin.com/project/magento-2) -

Welcome

+ +## Welcome Welcome to Magento 2 installation! We're glad you chose to install Magento 2, a cutting-edge, feature-rich eCommerce solution that gets results. ## Magento System Requirements @@ -30,7 +31,7 @@ To suggest documentation improvements, click [here][4]. [4]: https://devdocs.magento.com

Community Maintainers

-The members of this team have been recognized for their outstanding commitment to maintaining and improving Magento. Magento has granted them permission to accept, merge, and reject pull requests, as well as review issues, and thanks these Community Maintainers for their valuable contributions. +The members of this team have been recognized for their outstanding commitment to maintaining and improving Magento. Magento has granted them permission to accept, merge, and reject pull requests, as well as review issues, and thanks to these Community Maintainers for their valuable contributions. diff --git a/app/code/Magento/AdminAnalytics/registration.php b/app/code/Magento/AdminAnalytics/registration.php index 65c9955d396a8..7d45f9f2b82e5 100644 --- a/app/code/Magento/AdminAnalytics/registration.php +++ b/app/code/Magento/AdminAnalytics/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_AdminAnalytics', __DIR__); diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml b/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml index 3c35f1937783b..fcd1c4ebbbcdf 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/ui_component/admin_usage_notification.xml @@ -85,7 +85,7 @@ Help us improve Magento Admin by allowing us to collect usage data.

All usage data that we collect for this purpose cannot be used to individually identify you and is used only to improve the Magento Admin and related products and services.

-

You can learn more and opt out at any time by following the instructions in merchant documentation.

+

You can learn more and opt out at any time by following the instructions in merchant documentation.

]]> diff --git a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js index bc09890d0d0b4..fac71870603c3 100644 --- a/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js +++ b/app/code/Magento/AdminAnalytics/view/adminhtml/web/js/modal/component.js @@ -20,16 +20,7 @@ define([ enableLogAction: '${ $.provider }:data.enableLogAction', disableLogAction: '${ $.provider }:data.disableLogAction' }, - options: { - keyEventHandlers: { - /** - * Prevents escape key from exiting out of modal - */ - escapeKey: function () { - return; - } - } - }, + options: {}, notificationWindow: null }, @@ -41,11 +32,32 @@ define([ this._super(); }, + /** + * Configure ESC and TAB so user can't leave modal + * without selecting an option + * + * @returns {Object} Chainable. + */ + initModalEvents: function () { + this._super(); + //Don't allow ESC key to close modal + this.options.keyEventHandlers.escapeKey = this.handleEscKey.bind(this); + //Restrict tab action to the modal + this.options.keyEventHandlers.tabKey = this.handleTabKey.bind(this); + + return this; + }, + /** * Once the modal is opened it hides the X */ onOpened: function () { - $('.modal-header button.action-close').hide(); + $('.modal-header button.action-close').attr('disabled', true).hide(); + + this.focusableElements = $(this.rootSelector).find('a[href], button:enabled'); + this.firstFocusableElement = this.focusableElements[0]; + this.lastFocusableElement = this.focusableElements[this.focusableElements.length - 1]; + this.firstFocusableElement.focus(); }, /** @@ -104,11 +116,70 @@ define([ * Allows admin usage popup to be shown first and then new release notification */ openReleasePopup: function () { - var notifiModal = registry.get('release_notification.release_notification.notification_modal_1'); + var notificationModalSelector = 'release_notification.release_notification.notification_modal_1'; if (analyticsPopupConfig.releaseVisible) { - notifiModal.initializeContentAfterAnalytics(); + registry.get(notificationModalSelector).initializeContentAfterAnalytics(); } + }, + + /** + * Handle Tab and Shift+Tab key event + * + * Keep the tab actions restricted to the popup modal + * so the user must select an option to dismiss the modal + */ + handleTabKey: function (event) { + var modal = this, + KEY_TAB = 9; + + /** + * Handle Shift+Tab to tab backwards + */ + function handleBackwardTab() { + if (document.activeElement === modal.firstFocusableElement || + document.activeElement === $(modal.rootSelector)[0] + ) { + event.preventDefault(); + modal.lastFocusableElement.focus(); + } + } + + /** + * Handle Tab forward + */ + function handleForwardTab() { + if (document.activeElement === modal.lastFocusableElement) { + event.preventDefault(); + modal.firstFocusableElement.focus(); + } + } + + switch (event.keyCode) { + case KEY_TAB: + if (modal.focusableElements.length === 1) { + event.preventDefault(); + break; + } + + if (event.shiftKey) { + handleBackwardTab(); + break; + } + handleForwardTab(); + break; + default: + break; + } + }, + + /** + * Handle Esc key + * + * Esc key should not close modal + */ + handleEscKey: function (event) { + event.preventDefault(); } } ); diff --git a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php index 82f70d92e4930..f74f62ef000e6 100644 --- a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php +++ b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Actions.php @@ -1,4 +1,6 @@ _urlHelper = $urlHelper; parent::__construct($context, $data); } @@ -40,7 +43,7 @@ public function __construct( * @param \Magento\Framework\DataObject $row * @return string */ - public function render(\Magento\Framework\DataObject $row) + public function render(DataObject $row) { $readDetailsHtml = $row->getUrl() ? '' . __( 'Mark as Read' ) . '' : ''; @@ -63,8 +66,8 @@ public function render(\Magento\Framework\DataObject $row) '*/*/remove/', [ '_current' => true, - 'id' => $row->getId(), - \Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED => $encodedUrl + 'id' => $row->getNotificationId(), + ActionInterface::PARAM_NAME_URL_ENCODED => $encodedUrl ] ), __('Are you sure?'), diff --git a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Notice.php b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Notice.php index ccc1b98a228ce..4aa5d90e08014 100644 --- a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Notice.php +++ b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Notice.php @@ -1,4 +1,6 @@ ' . $this->escapeHtml($row->getTitle()) . diff --git a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Severity.php b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Severity.php index 033fa52c55081..fd3d5c584d6fa 100644 --- a/app/code/Magento/AdminNotification/Block/Grid/Renderer/Severity.php +++ b/app/code/Magento/AdminNotification/Block/Grid/Renderer/Severity.php @@ -1,4 +1,6 @@ _notice = $notice; } @@ -36,12 +42,14 @@ public function __construct( * @param \Magento\Framework\DataObject $row * @return string */ - public function render(\Magento\Framework\DataObject $row) + public function render(DataObject $row) { $class = ''; $value = ''; - switch ($row->getData($this->getColumn()->getIndex())) { + $column = $this->getColumn(); + $index = $column->getIndex(); + switch ($row->getData($index)) { case MessageInterface::SEVERITY_CRITICAL: $class = 'critical'; $value = $this->_notice->getSeverities(MessageInterface::SEVERITY_CRITICAL); @@ -59,6 +67,7 @@ public function render(\Magento\Framework\DataObject $row) $value = $this->_notice->getSeverities(MessageInterface::SEVERITY_NOTICE); break; } + return '' . $value . ''; } } diff --git a/app/code/Magento/AdminNotification/Test/Mftf/ActionGroup/AdminSystemMessagesActionGroup.xml b/app/code/Magento/AdminNotification/Test/Mftf/ActionGroup/AdminSystemMessagesActionGroup.xml new file mode 100644 index 0000000000000..a498c95c65a30 --- /dev/null +++ b/app/code/Magento/AdminNotification/Test/Mftf/ActionGroup/AdminSystemMessagesActionGroup.xml @@ -0,0 +1,23 @@ + + + + + + + Check warning system message exists. + + + + + + + + + + diff --git a/app/code/Magento/AdminNotification/Test/Mftf/Section/AdminSystemMessagesSection.xml b/app/code/Magento/AdminNotification/Test/Mftf/Section/AdminSystemMessagesSection.xml index 8a73968edb9a6..e3b2ea7e24c83 100644 --- a/app/code/Magento/AdminNotification/Test/Mftf/Section/AdminSystemMessagesSection.xml +++ b/app/code/Magento/AdminNotification/Test/Mftf/Section/AdminSystemMessagesSection.xml @@ -11,5 +11,9 @@
+ + + +
diff --git a/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/ActionsTest.php b/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/ActionsTest.php new file mode 100644 index 0000000000000..781734186ce6b --- /dev/null +++ b/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/ActionsTest.php @@ -0,0 +1,73 @@ +getMockBuilder(Escaper::class)->disableOriginalConstructor()->getMock(); + $escaperMock->expects($this->once())->method('escapeUrl')->willReturn('https://magento.com'); + + /** @var UrlInterface | \PHPUnit_Framework_MockObject_MockObject $urlBuilder */ + $urlBuilder = $this->getMockBuilder(UrlInterface::class)->getMock(); + $urlBuilder->expects($this->once())->method('getUrl')->willReturn('http://magento.com'); + + /** @var Context | \PHPUnit_Framework_MockObject_MockObject $contextMock */ + $contextMock = $this->getMockBuilder(Context::class)->disableOriginalConstructor()->getMock(); + $contextMock->expects($this->once())->method('getEscaper')->willReturn($escaperMock); + $contextMock->expects($this->once())->method('getUrlBuilder')->willReturn($urlBuilder); + + /** @var Data | \PHPUnit_Framework_MockObject_MockObject $urlHelperMock */ + $urlHelperMock = $this->getMockBuilder(Data::class)->disableOriginalConstructor()->getMock(); + $urlHelperMock->expects($this->once())->method('getEncodedUrl')->willReturn('http://magento.com'); + + $this->sut = new Actions($contextMock, $urlHelperMock); + } + + public function testShouldRenderMessageWhenUrlIsGiven() : void + { + $dataObject = new DataObject(); + $dataObject->setdata('url', 'https://magento.com'); + $dataObject->setdata('is_read', true); + $dataObject->setdata('id', 1); + + $actual = $this->sut->render($dataObject); + + // Ignoring Code Style at this point due to the long HEREDOC + // phpcs:disable + $expected = <<Read DetailsRemove +HTML; + // phpcs:enable + + $this->assertEquals($actual, $expected); + } +} diff --git a/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/NoticeTest.php b/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/NoticeTest.php new file mode 100644 index 0000000000000..7b4b0a0f66e96 --- /dev/null +++ b/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/NoticeTest.php @@ -0,0 +1,55 @@ +getMockBuilder(Escaper::class)->disableOriginalConstructor()->getMock(); + $escaperMock->expects($this->exactly(2))->method('escapeHtml')->willReturn('
Some random html
'); + + /** @var Context | \PHPUnit_Framework_MockObject_MockObject $contextMock */ + $contextMock = $this->getMockBuilder(Context::class)->disableOriginalConstructor()->getMock(); + $contextMock->expects($this->once())->method('getEscaper')->willReturn($escaperMock); + + $this->sut = new Notice($contextMock); + } + + public function testShouldRenderNotice() : void + { + $dataObject = new DataObject(); + $dataObject->setData('title', 'A great Title'); + $dataObject->setData('description', 'Handy description right here'); + + $actual = $this->sut->render($dataObject); + $expected = '
Some random html

Some random html
'; + + $this->assertEquals($actual, $expected); + } +} diff --git a/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/SeverityTest.php b/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/SeverityTest.php new file mode 100644 index 0000000000000..2a30be02f173b --- /dev/null +++ b/app/code/Magento/AdminNotification/Test/Unit/Block/Grid/Renderer/SeverityTest.php @@ -0,0 +1,90 @@ +getMockBuilder(Inbox::class)->disableOriginalConstructor()->getMock(); + + /** @var Context | \PHPUnit_Framework_MockObject_MockObject $contextMock */ + $contextMock = $this->getMockBuilder(Context::class)->disableOriginalConstructor()->getMock(); + + $this->sut = new Severity($contextMock, $inboxMock); + } + + public function testShouldRenderSeverity() : void + { + /** @var Column | \PHPUnit_Framework_MockObject_MockObject $columnMock */ + $columnMock = $this->getMockBuilder(Column::class) + ->disableOriginalConstructor() + ->setMethods(['getIndex']) + ->getMock(); + $columnMock->expects($this->exactly(5))->method('getIndex')->willReturn('index'); + $this->sut->setColumn($columnMock); + $dataObject = new DataObject(); + + // Test critical severity + $dataObject->setData('index', 1); + $actual = $this->sut->render($dataObject); + $expected = ''; + + $this->assertEquals($actual, $expected); + + // Test major severity + $dataObject->setData('index', 2); + $actual = $this->sut->render($dataObject); + $expected = ''; + + $this->assertEquals($actual, $expected); + + // Test minor severity + $dataObject->setData('index', 3); + $actual = $this->sut->render($dataObject); + $expected = ''; + + $this->assertEquals($actual, $expected); + + // Test notice severity + $dataObject->setData('index', 4); + $actual = $this->sut->render($dataObject); + $expected = ''; + + $this->assertEquals($actual, $expected); + + // Test default severity + $dataObject->setData('index', 5); + $actual = $this->sut->render($dataObject); + $expected = ''; + + $this->assertEquals($actual, $expected); + } +} diff --git a/app/code/Magento/AdminNotification/registration.php b/app/code/Magento/AdminNotification/registration.php index 8d427a458c18d..de8995efdb137 100644 --- a/app/code/Magento/AdminNotification/registration.php +++ b/app/code/Magento/AdminNotification/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_AdminNotification', __DIR__); diff --git a/app/code/Magento/AdvancedPricingImportExport/registration.php b/app/code/Magento/AdvancedPricingImportExport/registration.php index 7a1d2feeccd45..8429e82b27fda 100644 --- a/app/code/Magento/AdvancedPricingImportExport/registration.php +++ b/app/code/Magento/AdvancedPricingImportExport/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_AdvancedPricingImportExport', __DIR__); diff --git a/app/code/Magento/AdvancedSearch/Test/Unit/Model/Recommendations/DataProviderTest.php b/app/code/Magento/AdvancedSearch/Test/Unit/Model/Recommendations/DataProviderTest.php new file mode 100644 index 0000000000000..c62c906914fd7 --- /dev/null +++ b/app/code/Magento/AdvancedSearch/Test/Unit/Model/Recommendations/DataProviderTest.php @@ -0,0 +1,189 @@ +scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + $this->layerResolverMock = $this->getMockBuilder(Resolver::class) + ->disableOriginalConstructor() + ->setMethods(['get']) + ->getMock(); + + $this->searchLayerMock = $this->createMock(SearchLayer::class); + + $this->layerResolverMock->expects($this->any()) + ->method('get') + ->will($this->returnValue($this->searchLayerMock)); + + $this->recommendationsFactoryMock = $this->getMockBuilder(RecommendationsFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->recommendationsMock = $this->createMock(Recommendations::class); + + $this->queryResultFactory = $this->getMockBuilder(QueryResultFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->model = $this->objectManagerHelper->getObject( + DataProvider::class, + [ + 'scopeConfig' => $this->scopeConfigMock, + 'layerResolver' => $this->layerResolverMock, + 'recommendationsFactory' => $this->recommendationsFactoryMock, + 'queryResultFactory' => $this->queryResultFactory + ] + ); + } + + /** + * Test testGetItems() when Search Recommendations disabled. + * + * @return void + */ + public function testGetItemsWhenDisabledSearchRecommendations() + { + $isEnabledSearchRecommendations = false; + + /** @var $queryInterfaceMock QueryInterface */ + $queryInterfaceMock = $this->createMock(QueryInterface::class); + + $this->scopeConfigMock->expects($this->any()) + ->method('isSetFlag') + ->with('catalog/search/search_recommendations_enabled', ScopeInterface::SCOPE_STORE) + ->willReturn($isEnabledSearchRecommendations); + + $result = $this->model->getItems($queryInterfaceMock); + $this->assertEquals([], $result); + } + + /** + * Test testGetItems() when Search Recommendations enabled. + * + * @return void + */ + public function testGetItemsWhenEnabledSearchRecommendations() + { + $storeId = 1; + $searchRecommendationsCountConfig = 2; + $isEnabledSearchRecommendations = true; + $queryText = 'test'; + + /** @var $queryInterfaceMock QueryInterface */ + $queryInterfaceMock = $this->createMock(QueryInterface::class); + $queryInterfaceMock->expects($this->any())->method('getQueryText')->willReturn($queryText); + + $this->scopeConfigMock->expects($this->any()) + ->method('isSetFlag') + ->with('catalog/search/search_recommendations_enabled', ScopeInterface::SCOPE_STORE) + ->willReturn($isEnabledSearchRecommendations); + + $this->scopeConfigMock->expects($this->any()) + ->method('getValue') + ->with('catalog/search/search_recommendations_count', ScopeInterface::SCOPE_STORE) + ->willReturn($searchRecommendationsCountConfig); + + $productCollectionMock = $this->createMock(ProductCollection::class); + $productCollectionMock->expects($this->any())->method('getStoreId')->willReturn($storeId); + + $this->searchLayerMock->expects($this->any())->method('getProductCollection') + ->willReturn($productCollectionMock); + + $this->recommendationsFactoryMock->expects($this->any())->method('create') + ->willReturn($this->recommendationsMock); + + $this->recommendationsMock->expects($this->any())->method('getRecommendationsByQuery') + ->with($queryText, ['store_id' => $storeId], $searchRecommendationsCountConfig) + ->willReturn( + [ + [ + 'query_text' => 'a', + 'num_results' => 3 + ], + [ + 'query_text' => 'b', + 'num_results' => 2 + ] + ] + ); + $queryResultMock = $this->createMock(QueryResult::class); + $this->queryResultFactory->expects($this->any())->method('create')->willReturn($queryResultMock); + + $result = $this->model->getItems($queryInterfaceMock); + $this->assertEquals(2, count($result)); + } +} diff --git a/app/code/Magento/AdvancedSearch/etc/adminhtml/system.xml b/app/code/Magento/AdvancedSearch/etc/adminhtml/system.xml index 63c78e07c7bfc..905dd3e7d1819 100644 --- a/app/code/Magento/AdvancedSearch/etc/adminhtml/system.xml +++ b/app/code/Magento/AdvancedSearch/etc/adminhtml/system.xml @@ -60,7 +60,7 @@ 1 - + Magento\Config\Model\Config\Source\Yesno When you enable this option your site may slow down. diff --git a/app/code/Magento/AdvancedSearch/registration.php b/app/code/Magento/AdvancedSearch/registration.php index c82ffa8e7e4d6..2c600f550dcf5 100644 --- a/app/code/Magento/AdvancedSearch/registration.php +++ b/app/code/Magento/AdvancedSearch/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_AdvancedSearch', __DIR__); diff --git a/app/code/Magento/Amqp/registration.php b/app/code/Magento/Amqp/registration.php index 17d8382c698e8..2e4a4f362f1a3 100644 --- a/app/code/Magento/Amqp/registration.php +++ b/app/code/Magento/Amqp/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Amqp', __DIR__); diff --git a/app/code/Magento/AmqpStore/registration.php b/app/code/Magento/AmqpStore/registration.php index 4922879bfbf16..22ce677dc8302 100644 --- a/app/code/Magento/AmqpStore/registration.php +++ b/app/code/Magento/AmqpStore/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_AmqpStore', __DIR__); diff --git a/app/code/Magento/Analytics/Model/ExportDataHandler.php b/app/code/Magento/Analytics/Model/ExportDataHandler.php index 72a8e4ea00347..4dbc316d0901a 100644 --- a/app/code/Magento/Analytics/Model/ExportDataHandler.php +++ b/app/code/Magento/Analytics/Model/ExportDataHandler.php @@ -89,7 +89,7 @@ public function __construct( public function prepareExportData() { try { - $tmpDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::VAR_DIR); + $tmpDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::SYS_TMP); $this->prepareDirectory($tmpDirectory, $this->getTmpFilesDirRelativePath()); $this->reportWriter->write($tmpDirectory, $this->getTmpFilesDirRelativePath()); diff --git a/app/code/Magento/Analytics/Test/Mftf/Data/UserRoleData.xml b/app/code/Magento/Analytics/Test/Mftf/Data/UserRoleData.xml index 099cc71321b84..3b198644fcc9b 100644 --- a/app/code/Magento/Analytics/Test/Mftf/Data/UserRoleData.xml +++ b/app/code/Magento/Analytics/Test/Mftf/Data/UserRoleData.xml @@ -9,6 +9,7 @@ + 0 noreport 123123q diff --git a/app/code/Magento/Analytics/Test/Unit/Model/ExportDataHandlerTest.php b/app/code/Magento/Analytics/Test/Unit/Model/ExportDataHandlerTest.php index 493fe71c9fbfc..cf00556cfe590 100644 --- a/app/code/Magento/Analytics/Test/Unit/Model/ExportDataHandlerTest.php +++ b/app/code/Magento/Analytics/Test/Unit/Model/ExportDataHandlerTest.php @@ -13,7 +13,7 @@ use Magento\Framework\Archive; use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\WriteInterface; -use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Filesystem\DirectoryList; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; class ExportDataHandlerTest extends \PHPUnit\Framework\TestCase @@ -137,7 +137,7 @@ public function testPrepareExportData($isArchiveSourceDirectory) $this->filesystemMock ->expects($this->once()) ->method('getDirectoryWrite') - ->with(DirectoryList::VAR_DIR) + ->with(DirectoryList::SYS_TMP) ->willReturn($this->directoryMock); $this->directoryMock ->expects($this->exactly(4)) @@ -238,7 +238,7 @@ public function testPrepareExportDataWithLocalizedException() $this->filesystemMock ->expects($this->once()) ->method('getDirectoryWrite') - ->with(DirectoryList::VAR_DIR) + ->with(DirectoryList::SYS_TMP) ->willReturn($this->directoryMock); $this->reportWriterMock ->expects($this->once()) diff --git a/app/code/Magento/Analytics/etc/adminhtml/system.xml b/app/code/Magento/Analytics/etc/adminhtml/system.xml index c7da840b7e665..2a04128099345 100644 --- a/app/code/Magento/Analytics/etc/adminhtml/system.xml +++ b/app/code/Magento/Analytics/etc/adminhtml/system.xml @@ -15,7 +15,7 @@ For more information, see our + "Go to Advanced Reporting" link.
For more information, see our
terms and conditions.]]>
@@ -29,7 +29,7 @@ Magento\Analytics\Block\Adminhtml\System\Config\CollectionTimeLabel Magento\Analytics\Model\Config\Backend\CollectionTime - + Industry Data In order to personalize your Advanced Reporting experience, please select your industry. diff --git a/app/code/Magento/AsynchronousOperations/Model/BulkStatus.php b/app/code/Magento/AsynchronousOperations/Model/BulkStatus.php index c37ae0d23dd25..6290ac00ce87f 100644 --- a/app/code/Magento/AsynchronousOperations/Model/BulkStatus.php +++ b/app/code/Magento/AsynchronousOperations/Model/BulkStatus.php @@ -87,22 +87,6 @@ public function getFailedOperationsByBulkId($bulkUuid, $failureType = null) */ public function getOperationsCountByBulkIdAndStatus($bulkUuid, $status) { - if ($status === OperationInterface::STATUS_TYPE_OPEN) { - /** - * Total number of operations that has been scheduled within the given bulk - */ - $allOperationsQty = $this->getOperationCount($bulkUuid); - - /** - * Number of operations that has been processed (i.e. operations with any status but 'open') - */ - $allProcessedOperationsQty = (int)$this->operationCollectionFactory->create() - ->addFieldToFilter('bulk_uuid', $bulkUuid) - ->getSize(); - - return $allOperationsQty - $allProcessedOperationsQty; - } - /** @var \Magento\AsynchronousOperations\Model\ResourceModel\Operation\Collection $collection */ $collection = $this->operationCollectionFactory->create(); return $collection->addFieldToFilter('bulk_uuid', $bulkUuid) diff --git a/app/code/Magento/AsynchronousOperations/Model/Operation.php b/app/code/Magento/AsynchronousOperations/Model/Operation.php index dbe6ecc1b6b1f..00f33d10a1e1b 100644 --- a/app/code/Magento/AsynchronousOperations/Model/Operation.php +++ b/app/code/Magento/AsynchronousOperations/Model/Operation.php @@ -6,13 +6,33 @@ namespace Magento\AsynchronousOperations\Model; use Magento\AsynchronousOperations\Api\Data\OperationInterface; +use Magento\AsynchronousOperations\Model\OperationStatusValidator; use Magento\Framework\DataObject; /** - * Class Operation + * Class Operation encapsulates methods for Operation Model Object */ class Operation extends DataObject implements OperationInterface { + /** + * @var OperationStatusValidator + */ + private $operationStatusValidator; + + /** + * Operation constructor. + * + * @param OperationStatusValidator $operationStatusValidator + * @param array $data + */ + public function __construct( + OperationStatusValidator $operationStatusValidator, + array $data = [] + ) { + $this->operationStatusValidator = $operationStatusValidator; + parent::__construct($data); + } + /** * @inheritDoc */ @@ -106,6 +126,7 @@ public function getStatus() */ public function setStatus($status) { + $this->operationStatusValidator->validate($status); return $this->setData(self::STATUS, $status); } diff --git a/app/code/Magento/AsynchronousOperations/Model/OperationProcessor.php b/app/code/Magento/AsynchronousOperations/Model/OperationProcessor.php index 6826c34fd35f0..453f786bdf47b 100644 --- a/app/code/Magento/AsynchronousOperations/Model/OperationProcessor.php +++ b/app/code/Magento/AsynchronousOperations/Model/OperationProcessor.php @@ -8,25 +8,24 @@ namespace Magento\AsynchronousOperations\Model; -use Magento\Framework\Serialize\Serializer\Json; use Magento\AsynchronousOperations\Api\Data\OperationInterface; -use Magento\Framework\Bulk\OperationManagementInterface; use Magento\AsynchronousOperations\Model\ConfigInterface as AsyncConfig; -use Magento\Framework\MessageQueue\MessageValidator; -use Magento\Framework\MessageQueue\MessageEncoder; -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Framework\MessageQueue\ConsumerConfigurationInterface; -use Psr\Log\LoggerInterface; -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Exception\TemporaryStateExceptionInterface; +use Magento\Framework\Bulk\OperationManagementInterface; +use Magento\Framework\Communication\ConfigInterface as CommunicationConfig; use Magento\Framework\DB\Adapter\ConnectionException; use Magento\Framework\DB\Adapter\DeadlockException; use Magento\Framework\DB\Adapter\LockWaitException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\MessageQueue\ConsumerConfigurationInterface; +use Magento\Framework\MessageQueue\MessageEncoder; +use Magento\Framework\MessageQueue\MessageValidator; +use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\Webapi\ServiceOutputProcessor; -use Magento\Framework\Communication\ConfigInterface as CommunicationConfig; +use Psr\Log\LoggerInterface; /** - * Class OperationProcessor + * Proccess operation * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -80,9 +79,9 @@ class OperationProcessor * @param ConsumerConfigurationInterface $configuration * @param Json $jsonHelper * @param OperationManagementInterface $operationManagement - * @param LoggerInterface $logger * @param \Magento\Framework\Webapi\ServiceOutputProcessor $serviceOutputProcessor * @param \Magento\Framework\Communication\ConfigInterface $communicationConfig + * @param LoggerInterface $logger */ public function __construct( MessageValidator $messageValidator, @@ -137,7 +136,9 @@ public function process(string $encodedMessage) $result = $this->executeHandler($callback, $entityParams); $status = $result['status']; $errorCode = $result['error_code']; + // phpcs:disable Magento2.Performance.ForeachArrayMerge $messages = array_merge($messages, $result['messages']); + // phpcs:enable Magento2.Performance.ForeachArrayMerge $outputData = $result['output_data']; } } @@ -174,8 +175,8 @@ public function process(string $encodedMessage) /** * Execute topic handler * - * @param $callback - * @param $entityParams + * @param callable $callback + * @param array $entityParams * @return array */ private function executeHandler($callback, $entityParams) @@ -187,7 +188,9 @@ private function executeHandler($callback, $entityParams) 'output_data' => null ]; try { + // phpcs:disable Magento2.Functions.DiscouragedFunction $result['output_data'] = call_user_func_array($callback, $entityParams); + // phpcs:enable Magento2.Functions.DiscouragedFunction $result['messages'][] = sprintf('Service execution success %s::%s', get_class($callback[0]), $callback[1]); } catch (\Zend_Db_Adapter_Exception $e) { $this->logger->critical($e->getMessage()); @@ -206,9 +209,7 @@ private function executeHandler($callback, $entityParams) } } catch (NoSuchEntityException $e) { $this->logger->error($e->getMessage()); - $result['status'] = ($e instanceof TemporaryStateExceptionInterface) ? - OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED : - OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; + $result['status'] = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; $result['error_code'] = $e->getCode(); $result['messages'][] = $e->getMessage(); } catch (LocalizedException $e) { diff --git a/app/code/Magento/AsynchronousOperations/Model/OperationStatusPool.php b/app/code/Magento/AsynchronousOperations/Model/OperationStatusPool.php new file mode 100644 index 0000000000000..890eb8c1c8c75 --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Model/OperationStatusPool.php @@ -0,0 +1,37 @@ +statuses = $statuses; + } + + /** + * Retrieve statuses that require validate + * + * @return array + */ + public function getStatuses() + { + return $this->statuses; + } +} diff --git a/app/code/Magento/AsynchronousOperations/Model/OperationStatusValidator.php b/app/code/Magento/AsynchronousOperations/Model/OperationStatusValidator.php new file mode 100644 index 0000000000000..f2ae135e4303c --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Model/OperationStatusValidator.php @@ -0,0 +1,47 @@ +operationStatusPool = $operationStatusPool; + } + + /** + * Validate method + * + * @param int $status + * @throws \InvalidArgumentException + * @return void + */ + public function validate($status) + { + $statuses = $this->operationStatusPool->getStatuses(); + + if (!in_array($status, $statuses)) { + throw new \InvalidArgumentException('Invalid Operation Status.'); + } + } +} diff --git a/app/code/Magento/AsynchronousOperations/Test/Unit/Model/OperationStatusValidatorTest.php b/app/code/Magento/AsynchronousOperations/Test/Unit/Model/OperationStatusValidatorTest.php new file mode 100644 index 0000000000000..b93d9701a7a69 --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Test/Unit/Model/OperationStatusValidatorTest.php @@ -0,0 +1,155 @@ +operationStatusPool = $this->getMockBuilder(OperationStatusPool::class) + ->disableOriginalConstructor() + ->getMock(); + + $objectManager = new ObjectManager($this); + + $this->operationStatusValidator = $objectManager->getObject( + OperationStatusValidator::class, + [ + 'operationStatusPool' => $this->operationStatusPool + ] + ); + + $this->operation = $objectManager->getObject( + Operation::class, + [ + 'operationStatusValidator' => $this->operationStatusValidator + ] + ); + } + + /** + * @param string $status + * @param array $statusPool + * @param string $expectedResult + * @dataProvider dataProviderForTestSetStatus + */ + public function testSetStatus( + string $status, + array $statusPool, + string $expectedResult + ) { + $this->operationStatusPool + ->expects($this->any()) + ->method('getStatuses') + ->willReturn($statusPool); + + try { + $this->operation->setStatus($status); + $this->assertEquals($expectedResult, $this->operation->getStatus()); + } catch (\Exception $exception) { + $this->assertEquals($expectedResult, $exception->getMessage()); + } + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function dataProviderForTestSetStatus() + { + return [ + [ + 'status' => 0, + 'statusPool' => [ + 'complete' => 1, + 'retriablyFailed' => 2, + 'notRetriablyFailed' => 3, + 'open' => 4, + 'rejected' => 5 + ], + 'expectedResult' => 'Invalid Operation Status.' + ], + [ + 'status' => 1, + 'statusPool' => [ + 'complete' => 1, + 'retriablyFailed' => 2, + 'notRetriablyFailed' => 3, + 'open' => 4, + 'rejected' => 5 + ], + 'expectedResult' => 1 + ], + [ + 'status' => 2, + 'statusPool' => [ + 'complete' => 1, + 'retriablyFailed' => 2, + 'notRetriablyFailed' => 3, + 'open' => 4, + 'rejected' => 5 + ], + 'expectedResult' => 2 + ], + [ + 'status' => 3, + 'statusPool' => [ + 'complete' => 1, + 'retriablyFailed' => 2, + 'notRetriablyFailed' => 3, + 'open' => 4, + 'rejected' => 5 + ], + 'expectedResult' => 3 + ], + [ + 'status' => 4, + 'statusPool' => [ + 'complete' => 1, + 'retriablyFailed' => 2, + 'notRetriablyFailed' => 3, + 'open' => 4, + 'rejected' => 5 + ], + 'expectedResult' => 4 + ], + [ + 'status' => 5, + 'statusPool' => [ + 'complete' => 1, + 'retriablyFailed' => 2, + 'notRetriablyFailed' => 3, + 'open' => 4, + 'rejected' => 5 + ], + 'expectedResult' => 5 + ] + ]; + } +} diff --git a/app/code/Magento/AsynchronousOperations/etc/di.xml b/app/code/Magento/AsynchronousOperations/etc/di.xml index 94a4c56c19cea..171a01cedf289 100644 --- a/app/code/Magento/AsynchronousOperations/etc/di.xml +++ b/app/code/Magento/AsynchronousOperations/etc/di.xml @@ -80,6 +80,17 @@ + + + + 1 + 2 + 3 + 4 + 5 + + + diff --git a/app/code/Magento/AsynchronousOperations/registration.php b/app/code/Magento/AsynchronousOperations/registration.php index d384df583fb5a..748f9d35aad5c 100644 --- a/app/code/Magento/AsynchronousOperations/registration.php +++ b/app/code/Magento/AsynchronousOperations/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_AsynchronousOperations', __DIR__); diff --git a/app/code/Magento/AsynchronousOperations/view/adminhtml/ui_component/bulk_listing.xml b/app/code/Magento/AsynchronousOperations/view/adminhtml/ui_component/bulk_listing.xml index 512b08d6a8de2..87dc0525eb1c0 100644 --- a/app/code/Magento/AsynchronousOperations/view/adminhtml/ui_component/bulk_listing.xml +++ b/app/code/Magento/AsynchronousOperations/view/adminhtml/ui_component/bulk_listing.xml @@ -92,7 +92,7 @@ id - + diff --git a/app/code/Magento/Authorization/Model/Role.php b/app/code/Magento/Authorization/Model/Role.php index 042e95806ae18..fc32fbcaa2e98 100644 --- a/app/code/Magento/Authorization/Model/Role.php +++ b/app/code/Magento/Authorization/Model/Role.php @@ -52,9 +52,6 @@ public function __construct( //phpcs:ignore Generic.CodeAnalysis.UselessOverridi /** * @inheritDoc - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __sleep() { @@ -64,9 +61,6 @@ public function __sleep() /** * @inheritDoc - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __wakeup() { diff --git a/app/code/Magento/Authorization/registration.php b/app/code/Magento/Authorization/registration.php index 0007aeba9a1ba..a4520bdcdb26b 100644 --- a/app/code/Magento/Authorization/registration.php +++ b/app/code/Magento/Authorization/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Authorization', __DIR__); diff --git a/app/code/Magento/Authorizenet/Test/Mftf/Test/StorefrontVerifySecureURLRedirectAuthorizenetTest.xml b/app/code/Magento/Authorizenet/Test/Mftf/Test/StorefrontVerifySecureURLRedirectAuthorizenetTest.xml deleted file mode 100644 index 5db903f0ed54a..0000000000000 --- a/app/code/Magento/Authorizenet/Test/Mftf/Test/StorefrontVerifySecureURLRedirectAuthorizenetTest.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - <description value="Verify that the Secure URL configuration applies to the Authorizenet pages on the Storefront"/> - <severity value="MAJOR"/> - <testCaseId value="MC-15610"/> - <group value="authorizenet"/> - <group value="configuration"/> - <group value="secure_storefront_url"/> - </annotations> - <before> - <createData entity="Simple_US_Customer" stepKey="customer"/> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefront"> - <argument name="Customer" value="$$customer$$"/> - </actionGroup> - <executeJS function="return window.location.host" stepKey="hostname"/> - <magentoCLI command="config:set web/secure/base_url https://{$hostname}/" stepKey="setSecureBaseURL"/> - <magentoCLI command="config:set web/secure/use_in_frontend 1" stepKey="useSecureURLsOnStorefront"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - </before> - <after> - <magentoCLI command="config:set web/secure/use_in_frontend 0" stepKey="dontUseSecureURLsOnStorefront"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> - <deleteData createDataKey="customer" stepKey="deleteCustomer"/> - </after> - <executeJS function="return window.location.host" stepKey="hostname"/> - <amOnUrl url="http://{$hostname}/authorizenet" stepKey="goToUnsecureAuthorizenetURL"/> - <seeCurrentUrlEquals url="https://{$hostname}/authorizenet" stepKey="seeSecureAuthorizenetURL"/> - </test> -</tests> diff --git a/app/code/Magento/Authorizenet/registration.php b/app/code/Magento/Authorizenet/registration.php index b96b8227bbbf8..cb3bedaaee27d 100644 --- a/app/code/Magento/Authorizenet/registration.php +++ b/app/code/Magento/Authorizenet/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Authorizenet', __DIR__); diff --git a/app/code/Magento/AuthorizenetAcceptjs/Block/Form.php b/app/code/Magento/AuthorizenetAcceptjs/Block/Form.php index 9f10b2df40e9f..f669ead967c59 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Block/Form.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Block/Form.php @@ -18,6 +18,8 @@ * Block for representing the payment form * * @api + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class Form extends Cc { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Block/Info.php b/app/code/Magento/AuthorizenetAcceptjs/Block/Info.php index ea476eaa55716..1876685998643 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Block/Info.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Block/Info.php @@ -15,6 +15,8 @@ * Translates the labels for the info block * * @api + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class Info extends ConfigurableInfo { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Block/Payment.php b/app/code/Magento/AuthorizenetAcceptjs/Block/Payment.php index 059bba0c805e8..b1c79e9e51426 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Block/Payment.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Block/Payment.php @@ -18,6 +18,8 @@ * Represents the payment block for the admin checkout form * * @api + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class Payment extends Template { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/AcceptPaymentStrategyCommand.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/AcceptPaymentStrategyCommand.php index a72435644d23c..d59edde212760 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/AcceptPaymentStrategyCommand.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/AcceptPaymentStrategyCommand.php @@ -14,6 +14,9 @@ /** * Chooses the best method of accepting the payment based on the status of the transaction + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class AcceptPaymentStrategyCommand implements CommandInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/CaptureStrategyCommand.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/CaptureStrategyCommand.php index a4d895d4daae0..4318441014ad7 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/CaptureStrategyCommand.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/CaptureStrategyCommand.php @@ -20,6 +20,9 @@ /** * Chooses the best method of capture based on the context of the payment + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class CaptureStrategyCommand implements CommandInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/FetchTransactionInfoCommand.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/FetchTransactionInfoCommand.php index bb9e7c26a45b1..d0c1ceac81378 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/FetchTransactionInfoCommand.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/FetchTransactionInfoCommand.php @@ -17,6 +17,9 @@ /** * Syncs the transaction status with authorize.net + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class FetchTransactionInfoCommand implements CommandInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/GatewayQueryCommand.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/GatewayQueryCommand.php index f8975ef38eed1..7185639936fa4 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/GatewayQueryCommand.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/GatewayQueryCommand.php @@ -20,6 +20,9 @@ /** * Makes a request to the gateway and returns results + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class GatewayQueryCommand implements CommandInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/RefundTransactionStrategyCommand.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/RefundTransactionStrategyCommand.php index 3cdfcf23ba607..de3ded6515ae0 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/RefundTransactionStrategyCommand.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Command/RefundTransactionStrategyCommand.php @@ -14,6 +14,9 @@ /** * Chooses the best method of returning the payment based on the status of the transaction + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class RefundTransactionStrategyCommand implements CommandInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Config.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Config.php index 2a28945d98359..f41eb1660da55 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Config.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Config.php @@ -13,6 +13,9 @@ /** * Houses configuration for this gateway + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class Config extends \Magento\Payment\Gateway\Config\Config { @@ -33,19 +36,6 @@ class Config extends \Magento\Payment\Gateway\Config\Config private const SOLUTION_ID_SANDBOX = 'AAA102993'; private const SOLUTION_ID_PRODUCTION = 'AAA175350'; - /** - * @param ScopeConfigInterface $scopeConfig - * @param null|string $methodCode - * @param string $pathPattern - */ - public function __construct( - ScopeConfigInterface $scopeConfig, - $methodCode = null, - $pathPattern = self::DEFAULT_PATH_PATTERN - ) { - parent::__construct($scopeConfig, $methodCode, $pathPattern); - } - /** * Gets the login id * diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Client.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Client.php index 1b2efbb85721a..ebd4240108a09 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Client.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Client.php @@ -21,6 +21,9 @@ /** * A client that can communicate with the Authorize.net API + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class Client implements ClientInterface { @@ -109,10 +112,12 @@ public function placeRequest(TransferInterface $transferObject) try { $data = $this->json->unserialize($responseBody); } catch (InvalidArgumentException $e) { + // phpcs:ignore Magento2.Exceptions.DirectThrow throw new \Exception('Invalid JSON was returned by the gateway'); } return $data; + // phpcs:ignore Magento2.Exceptions.ThrowCatch } catch (\Exception $e) { $this->logger->critical($e); diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/Filter/RemoveFieldsFilter.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/Filter/RemoveFieldsFilter.php index a23397c09189a..cce878cfbbb16 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/Filter/RemoveFieldsFilter.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/Filter/RemoveFieldsFilter.php @@ -12,6 +12,9 @@ /** * Removes a set of fields from the payload + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class RemoveFieldsFilter implements FilterInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/FilterInterface.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/FilterInterface.php index 35e563eacb0cd..dade4bd4ee1f3 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/FilterInterface.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/Payload/FilterInterface.php @@ -10,6 +10,9 @@ /** * Describes a filter for filtering content after all the builders have finished + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ interface FilterInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/TransferFactory.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/TransferFactory.php index a4cdeba77492c..238b65fb8af37 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/TransferFactory.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Http/TransferFactory.php @@ -14,6 +14,9 @@ /** * Can create a transfer object + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class TransferFactory implements TransferFactoryInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AcceptFdsDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AcceptFdsDataBuilder.php index 6883d63397be0..4a673112e6a5f 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AcceptFdsDataBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AcceptFdsDataBuilder.php @@ -14,6 +14,9 @@ /** * Adds the meta transaction information to the request + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class AcceptFdsDataBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AddressDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AddressDataBuilder.php index e9c42e864440c..07a4921b7d60b 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AddressDataBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AddressDataBuilder.php @@ -13,6 +13,9 @@ /** * Adds the basic payment information to the request + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class AddressDataBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AmountDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AmountDataBuilder.php index 601c329fe4f76..07fae5e536a28 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AmountDataBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AmountDataBuilder.php @@ -14,6 +14,9 @@ /** * Adds the amount of the transaction to the Request + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class AmountDataBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthenticationDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthenticationDataBuilder.php index 2387ab0ab89f3..dec6626dc7524 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthenticationDataBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthenticationDataBuilder.php @@ -14,6 +14,9 @@ /** * Adds the stored credentials to the request + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class AuthenticationDataBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthorizeDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthorizeDataBuilder.php index 226175f74d55a..c440da3ca9f4f 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthorizeDataBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/AuthorizeDataBuilder.php @@ -15,6 +15,9 @@ /** * Adds the meta transaction information to the request + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class AuthorizeDataBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CaptureDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CaptureDataBuilder.php index 0b17d10fb0d68..1e2a8617907a0 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CaptureDataBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CaptureDataBuilder.php @@ -15,6 +15,9 @@ /** * Adds the meta transaction information to the request + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class CaptureDataBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomSettingsBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomSettingsBuilder.php index e5b4472c098c8..31246497fca92 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomSettingsBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomSettingsBuilder.php @@ -14,6 +14,9 @@ /** * Adds the custom settings to the request + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class CustomSettingsBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomerDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomerDataBuilder.php index 7cd0426e93dd7..cfdaa31552960 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomerDataBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/CustomerDataBuilder.php @@ -14,6 +14,9 @@ /** * Adds the basic payment information to the request + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class CustomerDataBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/OrderDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/OrderDataBuilder.php index b0e33c9ca9615..bf0a15f552e6c 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/OrderDataBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/OrderDataBuilder.php @@ -13,6 +13,9 @@ /** * Adds the basic payment information to the request + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class OrderDataBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PassthroughDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PassthroughDataBuilder.php index 0301d08ad42c5..6e6ef04972c78 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PassthroughDataBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PassthroughDataBuilder.php @@ -13,6 +13,9 @@ /** * Adds data to the request that can be used in the response + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class PassthroughDataBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PaymentDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PaymentDataBuilder.php index 1ad73f6236616..99955e9724577 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PaymentDataBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PaymentDataBuilder.php @@ -14,6 +14,9 @@ /** * Adds the basic payment information to the request + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class PaymentDataBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PoDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PoDataBuilder.php index ad8f8c2b05d91..9b56e0852af01 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PoDataBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/PoDataBuilder.php @@ -14,6 +14,9 @@ /** * Adds the basic payment information to the request + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class PoDataBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundPaymentDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundPaymentDataBuilder.php index 96f3e67720fea..ac5bcb08cb04a 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundPaymentDataBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundPaymentDataBuilder.php @@ -14,6 +14,9 @@ /** * Adds the basic refund information to the request + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class RefundPaymentDataBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundReferenceTransactionDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundReferenceTransactionDataBuilder.php index b8cb5f858d05d..65842354b7e2a 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundReferenceTransactionDataBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundReferenceTransactionDataBuilder.php @@ -14,6 +14,9 @@ /** * Adds the reference transaction to the request + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class RefundReferenceTransactionDataBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundTransactionTypeDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundTransactionTypeDataBuilder.php index 752be05f6b576..0f74299ebf5bd 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundTransactionTypeDataBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RefundTransactionTypeDataBuilder.php @@ -12,6 +12,9 @@ /** * Adds the meta transaction information to the request + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class RefundTransactionTypeDataBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RequestTypeBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RequestTypeBuilder.php index 16c3f9556de27..d20add70846b8 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RequestTypeBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/RequestTypeBuilder.php @@ -12,6 +12,9 @@ /** * Adds the type of the request to the build subject + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class RequestTypeBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SaleDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SaleDataBuilder.php index 6ec27b105615b..4402fb5af8c82 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SaleDataBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SaleDataBuilder.php @@ -15,6 +15,9 @@ /** * Adds the meta transaction information to the request + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class SaleDataBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/ShippingDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/ShippingDataBuilder.php index 390714579f0b3..ea2cb89971fb5 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/ShippingDataBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/ShippingDataBuilder.php @@ -15,6 +15,9 @@ /** * Adds the shipping information to the request + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class ShippingDataBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SolutionDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SolutionDataBuilder.php index 0c89a0116defe..8734c0ab454ce 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SolutionDataBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/SolutionDataBuilder.php @@ -14,6 +14,9 @@ /** * Adds the appropriate solution ID to the request + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class SolutionDataBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StoreConfigBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StoreConfigBuilder.php index f44b1e5de9a28..396ad143466cd 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StoreConfigBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StoreConfigBuilder.php @@ -12,6 +12,9 @@ /** * This builder is used for correct store resolving and used only to retrieve correct store ID. + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class StoreConfigBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StubDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StubDataBuilder.php index 794c120f94451..a2766d97d9299 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StubDataBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/StubDataBuilder.php @@ -15,6 +15,9 @@ * * Since the order of params is matters for Authorize.net request, * this builder is used to reserve a place in builders sequence. + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class StubDataBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/TransactionDetailsDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/TransactionDetailsDataBuilder.php index e3a17e9636846..9365347df7a60 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/TransactionDetailsDataBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/TransactionDetailsDataBuilder.php @@ -14,6 +14,9 @@ /** * Adds the reference transaction to the request + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class TransactionDetailsDataBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/VoidDataBuilder.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/VoidDataBuilder.php index ef0cb96774e62..c830f1f23d17c 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/VoidDataBuilder.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Request/VoidDataBuilder.php @@ -14,6 +14,9 @@ /** * Adds the meta transaction information to the request + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class VoidDataBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseParentTransactionHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseParentTransactionHandler.php index 30b1ce88b083a..60c5bb21c0865 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseParentTransactionHandler.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseParentTransactionHandler.php @@ -14,6 +14,9 @@ /** * Processes payment information from a void transaction response + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class CloseParentTransactionHandler implements HandlerInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/ClosePartialTransactionHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/ClosePartialTransactionHandler.php index fd8af3d28c4d4..5279df56b5e28 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/ClosePartialTransactionHandler.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/ClosePartialTransactionHandler.php @@ -11,6 +11,9 @@ /** * Determines that parent transaction should be close for partial refund operation. + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class ClosePartialTransactionHandler extends CloseTransactionHandler { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseTransactionHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseTransactionHandler.php index fa9bf55462111..2cccf255ab8e9 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseTransactionHandler.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/CloseTransactionHandler.php @@ -14,6 +14,9 @@ /** * Processes payment information from a void transaction response + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class CloseTransactionHandler implements HandlerInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentResponseHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentResponseHandler.php index 16e8fbabb214a..e0b192205012f 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentResponseHandler.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentResponseHandler.php @@ -14,6 +14,9 @@ /** * Processes payment information from a response + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class PaymentResponseHandler implements HandlerInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentReviewStatusHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentReviewStatusHandler.php index 9f7c62873669f..41c2ddd2b3271 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentReviewStatusHandler.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/PaymentReviewStatusHandler.php @@ -14,6 +14,9 @@ /** * Processes payment information from a void transaction response + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class PaymentReviewStatusHandler implements HandlerInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionDetailsResponseHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionDetailsResponseHandler.php index 0dab641452136..81bb9c92b15ed 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionDetailsResponseHandler.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionDetailsResponseHandler.php @@ -16,6 +16,9 @@ /** * Adds the details to the transaction that should show when the transaction is viewed in the admin + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class TransactionDetailsResponseHandler implements HandlerInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionIdHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionIdHandler.php index bf5257f95dad6..f3a9a0a1c4466 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionIdHandler.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/TransactionIdHandler.php @@ -14,6 +14,9 @@ /** * Processes transaction id for the payment + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class TransactionIdHandler implements HandlerInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/VoidResponseHandler.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/VoidResponseHandler.php index 06b16b37278ba..7bcb8c6c8dba1 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/VoidResponseHandler.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Response/VoidResponseHandler.php @@ -14,6 +14,9 @@ /** * Processes payment information from a void transaction response + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class VoidResponseHandler implements HandlerInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/SubjectReader.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/SubjectReader.php index 855d48e27968e..b5f1cef94ea46 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/SubjectReader.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/SubjectReader.php @@ -13,6 +13,9 @@ /** * Helper for extracting information from the payment data structure + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class SubjectReader { @@ -42,6 +45,7 @@ public function readStoreId(array $subject): ?int $storeId = (int)$this->readPayment($subject) ->getOrder() ->getStoreId(); + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock } catch (\InvalidArgumentException $e) { // No store id is current set } diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/GeneralResponseValidator.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/GeneralResponseValidator.php index 7ad4647b421a1..47065ed96c240 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/GeneralResponseValidator.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/GeneralResponseValidator.php @@ -15,6 +15,9 @@ /** * Validates that the request was successful + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class GeneralResponseValidator extends AbstractValidator { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionHashValidator.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionHashValidator.php index 0d1c2ad033d87..c11e22110d952 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionHashValidator.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionHashValidator.php @@ -17,6 +17,9 @@ /** * Validates the transaction hash + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class TransactionHashValidator extends AbstractValidator { @@ -171,6 +174,7 @@ private function generateMd5Hash( $amount, $transactionId ) { + // phpcs:disable Magento2.Security.InsecureFunction return strtoupper(md5($merchantMd5 . $merchantApiLogin . $transactionId . $amount)); } diff --git a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionResponseValidator.php b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionResponseValidator.php index 326f4fb29ac84..8238aa37dcc0a 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionResponseValidator.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Gateway/Validator/TransactionResponseValidator.php @@ -15,6 +15,9 @@ /** * Validates the status of an attempted transaction + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class TransactionResponseValidator extends AbstractValidator { @@ -85,9 +88,7 @@ private function isResponseCodeAnError(array $transactionResponse): bool ?? $transactionResponse['errors'][0]['errorCode'] ?? null; - return !in_array($transactionResponse['responseCode'], [ - self::RESPONSE_CODE_APPROVED, self::RESPONSE_CODE_HELD - ]) + return !in_array($transactionResponse['responseCode'], [self::RESPONSE_CODE_APPROVED, self::RESPONSE_CODE_HELD]) || $code && !in_array( $code, diff --git a/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/Cctype.php b/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/Cctype.php index 046907ebb88cc..cdd1745a6bc1e 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/Cctype.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/Cctype.php @@ -12,6 +12,9 @@ /** * Authorize.net Payment CC Types Source Model + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class Cctype extends PaymentCctype { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/Environment.php b/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/Environment.php index f2eca8e143916..6f8e4394a589f 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/Environment.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/Environment.php @@ -10,6 +10,9 @@ /** * Authorize.net Environment Dropdown source + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class Environment implements \Magento\Framework\Data\OptionSourceInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/PaymentAction.php b/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/PaymentAction.php index 907a1b2a51b85..953841604bfee 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/PaymentAction.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Model/Adminhtml/Source/PaymentAction.php @@ -10,6 +10,9 @@ /** * Authorize.net Payment Action Dropdown source + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class PaymentAction implements \Magento\Framework\Data\OptionSourceInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Model/PassthroughDataObject.php b/app/code/Magento/AuthorizenetAcceptjs/Model/PassthroughDataObject.php index b49ef7e622506..145d8c000e8f7 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Model/PassthroughDataObject.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Model/PassthroughDataObject.php @@ -12,6 +12,9 @@ /** * Contains all the accumulated data from the request builders that should be passed through to the handlers + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class PassthroughDataObject extends DataObject { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Model/Ui/ConfigProvider.php b/app/code/Magento/AuthorizenetAcceptjs/Model/Ui/ConfigProvider.php index b24c101a3f792..108f18f393641 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Model/Ui/ConfigProvider.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Model/Ui/ConfigProvider.php @@ -14,6 +14,9 @@ /** * Retrieves config needed for checkout + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class ConfigProvider implements ConfigProviderInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Observer/DataAssignObserver.php b/app/code/Magento/AuthorizenetAcceptjs/Observer/DataAssignObserver.php index c7490ad0c80c3..0f989bb032175 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Observer/DataAssignObserver.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Observer/DataAssignObserver.php @@ -14,6 +14,9 @@ /** * Adds the payment info to the payment object + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class DataAssignObserver extends AbstractDataAssignObserver { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Setup/Patch/Data/CopyCurrentConfig.php b/app/code/Magento/AuthorizenetAcceptjs/Setup/Patch/Data/CopyCurrentConfig.php index 0675bd94b6200..aa699569c61f6 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Setup/Patch/Data/CopyCurrentConfig.php +++ b/app/code/Magento/AuthorizenetAcceptjs/Setup/Patch/Data/CopyCurrentConfig.php @@ -18,6 +18,9 @@ /** * Copies the Authorize.net DirectPost configuration values to the new Accept.js module. + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class CopyCurrentConfig implements DataPatchInterface { diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/ActionGroup/ConfigureAuthorizenetAcceptjsActionGroup.xml b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/ActionGroup/ConfigureAuthorizenetAcceptjsActionGroup.xml deleted file mode 100644 index 924f2b720dd2f..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/ActionGroup/ConfigureAuthorizenetAcceptjsActionGroup.xml +++ /dev/null @@ -1,57 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="ConfigureAuthorizenetAcceptjs" extends="EnableAuthorizenetAcceptjs"> - <annotations> - <description>Sets up the Authorize.net Accept JS configuration setting with a specified Payment action.</description> - </annotations> - <arguments> - <argument name="paymentAction" type="string"/> - </arguments> - <!-- Fill Auth.net fields and save --> - <waitForElementVisible selector="{{AuthorizenetAcceptjsConfigurationSection.paymentActionCheckbox}}" stepKey="waitForFormVisible"/> - <conditionalClick selector="{{AuthorizenetAcceptjsConfigurationSection.paymentActionCheckbox}}" stepKey="uncheckPaymentActionDefault" dependentSelector="{{AuthorizenetAcceptjsConfigurationSection.paymentActionSelectDisabled}}" visible="true"/> - <selectOption selector="{{AuthorizenetAcceptjsConfigurationSection.paymentActionSelect}}" stepKey="selectPaymentAction" userInput="{{paymentAction}}"/> - <scrollTo selector="{{AuthorizenetAcceptjsConfigurationSection.apiLoginIdField}}" stepKey="scrollToApiLoginId"/> - <fillField selector="{{AuthorizenetAcceptjsConfigurationSection.apiLoginIdField}}" userInput="{{_CREDS.authorizenet_acceptjs_api_login_id}}" stepKey="fillApiLoginId"/> - <fillField selector="{{AuthorizenetAcceptjsConfigurationSection.transactionKeyField}}" userInput="{{_CREDS.authorizenet_acceptjs_transaction_key}}" stepKey="fillTransactionKey"/> - <fillField selector="{{AuthorizenetAcceptjsConfigurationSection.publicClientKeyField}}" userInput="{{_CREDS.authorizenet_acceptjs_public_client_key}}" stepKey="fillPublicClientKey"/> - <fillField selector="{{AuthorizenetAcceptjsConfigurationSection.signatureKeyField}}" userInput="{{_CREDS.authorizenet_acceptjs_signature_key}}" stepKey="fillSignatureKey"/> - </actionGroup> - - <actionGroup name="DisableAuthorizenetAcceptjs"> - <annotations> - <description>Disables the Authorize.net Accept JS configuration setting via the CLI.</description> - </annotations> - - <magentoCLI stepKey="disableAuthorizenetAcceptjs" command="config:set payment/authorizenet_acceptjs/active 0"/> - </actionGroup> - - <actionGroup name="EnableAuthorizenetAcceptjs"> - <scrollTo selector="{{AuthorizenetAcceptjsConfigurationSection.openSectionToggle}}" stepKey="scrollToAuthorizeNetConfigSection"/> - <conditionalClick selector="{{AuthorizenetAcceptjsConfigurationSection.openSectionToggle}}" dependentSelector="{{AuthorizenetAcceptjsConfigurationSection.enabledDefaultSelect}}" visible="false" stepKey="openConfigSection"/> - <waitForElementVisible selector="{{AuthorizenetAcceptjsConfigurationSection.enabledDefaultSelect}}" stepKey="waitForEnableFieldVisible"/> - <uncheckOption selector="{{AuthorizenetAcceptjsConfigurationSection.enabledDefaultCheckbox}}" stepKey="uncheckCheckbox"/> - <selectOption selector="{{AuthorizenetAcceptjsConfigurationSection.enabledDefaultSelect}}" userInput="Yes" stepKey="enablePayment"/> - </actionGroup> - - <actionGroup name="AssertAuthorizenetAcceptjsRequiredFieldsValidationIsPresentOnSave"> - <scrollToTopOfPage stepKey="scrollToTop"/> - <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSave"/> - <scrollTo selector="{{AuthorizenetAcceptjsConfigurationSection.apiLoginIdField}}" stepKey="scrollToApiLoginIdField"/> - <see selector="{{AuthorizenetAcceptjsConfigurationSection.apiLoginIdField}} + {{AdminConfigSection.fieldError}}" userInput="This is a required field." stepKey="seeApiLoginIdRequiredMessage"/> - <scrollTo selector="{{AuthorizenetAcceptjsConfigurationSection.publicClientKeyField}}" stepKey="scrollToPublicClientKeyField"/> - <see selector="{{AuthorizenetAcceptjsConfigurationSection.publicClientKeyField}} + {{AdminConfigSection.fieldError}}" userInput="This is a required field." stepKey="seePublicClientKeyRequiredErrorMessage"/> - <scrollTo selector="{{AuthorizenetAcceptjsConfigurationSection.transactionKeyField}}" stepKey="scrollTransactionKeyField"/> - <see selector="{{AuthorizenetAcceptjsConfigurationSection.transactionKeyField}} + {{AdminConfigSection.fieldError}}" userInput="This is a required field." stepKey="seeTransactionKeyRequiredErrorMessage"/> - <scrollTo selector="{{AuthorizenetAcceptjsConfigurationSection.signatureKeyField}}" stepKey="scrollToSignatureKeyField"/> - <see selector="{{AuthorizenetAcceptjsConfigurationSection.signatureKeyField}} + {{AdminConfigSection.fieldError}}" userInput="This is a required field." stepKey="seeSignatureKeyRequiredErrorMessage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/ActionGroup/FillPaymentInformationActionGroup.xml b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/ActionGroup/FillPaymentInformationActionGroup.xml deleted file mode 100644 index 96c0b122e36d9..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/ActionGroup/FillPaymentInformationActionGroup.xml +++ /dev/null @@ -1,46 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="FillPaymentInformation"> - <annotations> - <description>Fill Payment Method with Authorize.net</description> - </annotations> - - <click stepKey="clickOnAuthorizenetToggle" selector="{{AuthorizenetCheckoutSection.selectAuthorizenet}}"/> - <waitForPageLoad stepKey="waitForCardDataSection"/> - <fillField stepKey="fillCardNumber" selector="{{AuthorizenetCheckoutSection.cardInput}}" userInput="{{PaymentAndShippingInfo.cardNumber}}"/> - <selectOption stepKey="fillExpMonth" selector="{{AuthorizenetCheckoutSection.expMonth}}" userInput="{{PaymentAndShippingInfo.month}}"/> - <selectOption stepKey="fillExpYear" selector="{{AuthorizenetCheckoutSection.expYear}}" userInput="20{{PaymentAndShippingInfo.year}}"/> - <fillField stepKey="fillCvv" selector="{{AuthorizenetCheckoutSection.cvv}}" userInput="123"/> - <click stepKey="checkoutButton" selector="{{AuthorizenetCheckoutSection.checkoutButton}}"/> - <waitForPageLoad stepKey="waitForCheckout"/> - </actionGroup> - - <!-- Guest checkout Authorize.net fill billing address --> - <actionGroup name="GuestCheckoutAuthorizenetFillBillingAddress"> - <annotations> - <description>Fill Billing Address as Guest with Authorize.net</description> - </annotations> - <arguments> - <argument name="customer"/> - <argument name="customerAddress"/> - </arguments> - - <fillField selector="{{GuestAuthorizenetCheckoutSection.firstName}}" userInput="{{customer.firstName}}" stepKey="fillFirstName"/> - <fillField selector="{{GuestAuthorizenetCheckoutSection.lastName}}" userInput="{{customer.lastName}}" stepKey="fillLastName"/> - <fillField selector="{{GuestAuthorizenetCheckoutSection.street}}" userInput="{{customerAddress.street[0]}}" stepKey="fillStreet"/> - <fillField selector="{{GuestAuthorizenetCheckoutSection.city}}" userInput="{{customerAddress.city}}" stepKey="fillCity"/> - <selectOption selector="{{GuestAuthorizenetCheckoutSection.state}}" userInput="{{customerAddress.state}}" stepKey="selectState"/> - <fillField selector="{{GuestAuthorizenetCheckoutSection.postcode}}" userInput="{{customerAddress.postcode}}" stepKey="fillPostCode"/> - <fillField selector="{{GuestAuthorizenetCheckoutSection.telephone}}" userInput="{{customerAddress.telephone}}" stepKey="fillTelephone"/> - <click selector="{{GuestAuthorizenetCheckoutSection.update}}" stepKey="updateAddress"/> - <waitForPageLoad stepKey="waitForUpdate"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/ActionGroup/ViewAndValidateOrderActionGroup.xml b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/ActionGroup/ViewAndValidateOrderActionGroup.xml deleted file mode 100644 index ecbed57ff15b0..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/ActionGroup/ViewAndValidateOrderActionGroup.xml +++ /dev/null @@ -1,80 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="ViewAndValidateOrderActionGroup"> - <annotations> - <description>Validate the Order is Correct. Then Submit the Invoice.</description> - </annotations> - <arguments> - <argument name="amount" type="string"/> - <argument name="status" type="string"/> - <argument name="captureStatus" type="string"/> - <argument name="closedStatus" type="string"/> - </arguments> - - <amOnPage url="{{AdminLoginPage.url}}" stepKey="navigateToAdmin"/> - <click selector="{{AdminMenuSection.sales}}" stepKey="clickSales"/> - <waitForPageLoad stepKey="waitForSalesSubsection"/> - <click selector="{{AdminMenuSection.orders}}" stepKey="clickOrders"/> - <waitForPageLoad stepKey="waitForOrdersGrid" time="30"/> - <click selector="{{OrdersGridSection.viewMostRecentOrder}}" stepKey="viewOrder"/> - <waitForPageLoad stepKey="waitForViewOrder"/> - <click selector="{{ViewOrderSection.openInvoiceForm}}" stepKey="openInvoiceForm"/> - <selectOption selector="{{ViewOrderSection.selectCaptureType}}" stepKey="selectCaptureType" userInput="Capture Online"/> - <click selector="{{ViewOrderSection.submitInvoice}}" stepKey="submitInvoice"/> - <waitForPageLoad stepKey="waitForInvoiceLoad"/> - <click selector="{{ViewOrderSection.commentsHistory}}" stepKey="viewCommentsHistory"/> - <waitForPageLoad stepKey="waitForHistoryLoad"/> - <see userInput="{{amount}}" selector="{{ViewOrderSection.capturedAmountText}}" stepKey="validateCapturedAmount"/> - <see userInput="{{status}}" selector="{{ViewOrderSection.orderStatus}}" stepKey="validateOrderStatus"/> - <click selector="{{ViewOrderSection.invoices}}" stepKey="openInvoices"/> - <waitForPageLoad stepKey="waitForInvoices"/> - <seeElement selector="{{ViewOrderSection.firstInvoice}}" stepKey="seeFirstInvoice"/> - <click selector="{{ViewOrderSection.transactions}}" stepKey="openTransactions"/> - <waitForPageLoad stepKey="waitForTransactions"/> - <see userInput="{{captureStatus}}" selector="{{ViewOrderSection.confirmCapture}}" stepKey="seeCapture"/> - <!-- Enable below line after fix of MC- - <see userInput="{{closedStatus}}" selector="{{ViewOrderSection.confirmClosed}}" stepKey="seeClosed"/> - --> - </actionGroup> - - <actionGroup name="ViewAndValidateOrderActionGroupNoSubmit"> - <annotations> - <description>Validate the Order is Correct. Do Not Submit the Invoice.</description> - </annotations> - <arguments> - <argument name="amount" type="string"/> - <argument name="status" type="string"/> - <argument name="captureStatus" type="string"/> - <argument name="closedStatus" type="string"/> - </arguments> - - <amOnPage url="{{AdminLoginPage.url}}" stepKey="navigateToAdmin"/> - <click selector="{{AdminMenuSection.sales}}" stepKey="clickSales"/> - <waitForPageLoad stepKey="waitForSalesSubsection"/> - <click selector="{{AdminMenuSection.orders}}" stepKey="clickOrders"/> - <waitForPageLoad stepKey="waitForOrdersGrid" time="30"/> - <click selector="{{OrdersGridSection.viewMostRecentOrder}}" stepKey="viewOrder"/> - <waitForPageLoad stepKey="waitForViewOrder"/> - <click selector="{{ViewOrderSection.commentsHistory}}" stepKey="viewCommentsHistory"/> - <waitForPageLoad stepKey="waitForHistoryLoad"/> - <see userInput="{{amount}}" selector="{{ViewOrderSection.capturedAmountTextUnsubmitted}}" stepKey="validateCapturedAmount"/> - <see userInput="{{status}}" selector="{{ViewOrderSection.orderStatus}}" stepKey="validateOrderStatus"/> - <click selector="{{ViewOrderSection.invoices}}" stepKey="openInvoices"/> - <waitForPageLoad stepKey="waitForInvoices"/> - <seeElement selector="{{ViewOrderSection.firstInvoice}}" stepKey="seeFirstInvoice"/> - <click selector="{{ViewOrderSection.transactions}}" stepKey="openTransactions"/> - <waitForPageLoad stepKey="waitForTransactions"/> - <see userInput="{{captureStatus}}" selector="{{ViewOrderSection.confirmCapture}}" stepKey="seeCapture"/> - <!-- Enable below line after fix of MC- - <see userInput="{{closedStatus}}" selector="{{ViewOrderSection.confirmClosed}}" stepKey="seeClosed"/> - --> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Data/AuthorizenetAcceptjsOrderValidationData.xml b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Data/AuthorizenetAcceptjsOrderValidationData.xml deleted file mode 100644 index 59d4be98d450c..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Data/AuthorizenetAcceptjsOrderValidationData.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> - <entity name="AuthorizenetAcceptjsOrderValidationData" type="AuthorizenetAcceptjsCredentials"> - <data key="virtualProductOrderAmount">$24.68</data> - <data key="twoSimpleProductsOrderAmount">$128.00</data> - <data key="processingStatusProcessing">Processing</data> - <data key="captureStatusCapture">Capture</data> - <data key="closedStatusNo">No</data> - </entity> -</entities> diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/AdminMenuSection.xml b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/AdminMenuSection.xml deleted file mode 100644 index defb91339ea8f..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/AdminMenuSection.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="AdminMenuSection"> - <element name="dashboard" type="button" selector="#menu-magento-backend-dashboard"/> - <element name="sales" type="button" selector="#menu-magento-sales-sales"/> - <element name="orders" type="button" selector="//*[@id='menu-magento-sales-sales']//span[text()='Orders']"/> - <element name="catalog" type="button" selector="#menu-magento-catalog-catalog"/> - <element name="customers" type="button" selector="#menu-magento-customer-customer"/> - <element name="marketing" type="button" selector="#menu-magento-backend-marketing"/> - <element name="content" type="button" selector="#menu-magento-backend-content"/> - <element name="reports" type="button" selector="#menu-magento-reports-report"/> - <element name="stores" type="button" selector="#menu-magento-backend-stores"/> - <element name="system" type="button" selector="#menu-magento-backend-system"/> - <element name="findPartners" type="button" selector="#menu-magento-marketplace-partners"/> - <element name="currencySetup" type="button" selector="//a[contains(@class, 'item-nav')]//span[text()='Currency Setup']"/> - </section> -</sections> diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/AuthorizenetAcceptjsConfigurationSection.xml b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/AuthorizenetAcceptjsConfigurationSection.xml deleted file mode 100644 index 31be865ea2678..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/AuthorizenetAcceptjsConfigurationSection.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="AuthorizenetAcceptjsConfigurationSection"> - <element name="openSectionToggle" type="button" selector="#payment_us_authorizenet_acceptjs-head"/> - <element name="alreadyOpenSectionToggle" type="button" selector="#payment_us_authorizenet_acceptjs-head.open"/> - <element name="apiLoginIdField" type="input" selector="#payment_us_authorizenet_acceptjs_required_login"/> - <element name="transactionKeyField" type="input" selector="#payment_us_authorizenet_acceptjs_required_trans_key"/> - <element name="publicClientKeyField" type="input" selector="#payment_us_authorizenet_acceptjs_required_public_client_key"/> - <element name="signatureKeyField" type="input" selector="#payment_us_authorizenet_acceptjs_required_trans_signature_key"/> - <element name="enabledDefaultCheckbox" type="input" selector="#payment_us_authorizenet_acceptjs_active_inherit"/> - <element name="enabledDefaultSelect" type="select" selector="#payment_us_authorizenet_acceptjs_active"/> - <element name="paymentActionCheckbox" type="input" selector="#payment_us_authorizenet_acceptjs_required_payment_action_inherit"/> - <element name="paymentActionSelect" type="select" selector="#payment_us_authorizenet_acceptjs_required_payment_action"/> - <element name="paymentActionSelectDisabled" type="select" selector="#payment_us_authorizenet_acceptjs_required_payment_action[disabled='disabled']"/> - </section> -</sections> diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/AuthorizenetCheckoutSection.xml b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/AuthorizenetCheckoutSection.xml deleted file mode 100644 index 5d97842de374c..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/AuthorizenetCheckoutSection.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="AuthorizenetCheckoutSection"> - <element name="selectAuthorizenet" type="button" selector="#authorizenet_acceptjs"/> - <element name="cardInput" type="input" selector="#authorizenet_acceptjs_cc_number"/> - <element name="expMonth" type="select" selector="#authorizenet_acceptjs_expiration"/> - <element name="expYear" type="select" selector="#authorizenet_acceptjs_expiration_yr"/> - <element name="cvv" type="input" selector="#authorizenet_acceptjs_cc_cid"/> - <element name="checkoutButton" type="button" selector="._active button.action.checkout"/> - </section> -</sections> diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/ConfigurationMainActionsSection.xml b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/ConfigurationMainActionsSection.xml deleted file mode 100644 index 344330c4bc052..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/ConfigurationMainActionsSection.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="ConfigurationMainActionsSection"> - <element name="save" type="button" selector="#save"/> - </section> -</sections> diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/GuestAuthorizenetCheckoutSection.xml b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/GuestAuthorizenetCheckoutSection.xml deleted file mode 100644 index b5f2ecf641162..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/GuestAuthorizenetCheckoutSection.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="GuestAuthorizenetCheckoutSection"> - <element name="firstName" type="input" selector="div[name='billingAddressauthorizenet_acceptjs.firstname'] input"/> - <element name="lastName" type="input" selector="div[name='billingAddressauthorizenet_acceptjs.lastname'] input"/> - <element name="street" type="input" selector="div[name='billingAddressauthorizenet_acceptjs.street.0'] input"/> - <element name="city" type="input" selector="div[name='billingAddressauthorizenet_acceptjs.city'] input"/> - <element name="state" type="select" selector="div[name='billingAddressauthorizenet_acceptjs.region_id'] select"/> - <element name="postcode" type="input" selector="div[name='billingAddressauthorizenet_acceptjs.postcode'] input"/> - <element name="country" type="select" selector="div[name='billingAddressauthorizenet_acceptjs.country_id'] select"/> - <element name="telephone" type="input" selector="div[name='billingAddressauthorizenet_acceptjs.telephone'] input"/> - <element name="update" type="button" selector=".payment-method._active button.action-update" /> - </section> -</sections> diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/OrdersGridSection.xml b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/OrdersGridSection.xml deleted file mode 100644 index 7ae3dd0ffee89..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/OrdersGridSection.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="OrdersGridSection"> - <element name="viewMostRecentOrder" type="button" selector="#container div.admin__data-grid-wrap td:nth-of-type(10) a"/> - </section> -</sections> diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/StoresConfigurationListSection.xml b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/StoresConfigurationListSection.xml deleted file mode 100644 index f9f1bef38d17d..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/StoresConfigurationListSection.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="StoresConfigurationListSection"> - <element name="sales" type="button" selector="#system_config_tabs > div:nth-child(4) > div"/> - <element name="salesPaymentMethods" type="button" selector="//a[contains(@class, 'item-nav')]//span[text()='Payment Methods']"/> - </section> -</sections> diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/StoresSubmenuSection.xml b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/StoresSubmenuSection.xml deleted file mode 100644 index e54f9808fd49e..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/StoresSubmenuSection.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="StoresSubmenuSection"> - <element name="configuration" type="button" selector="#menu-magento-backend-stores li[data-ui-id='menu-magento-config-system-config']"/> - </section> -</sections> diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/ViewOrderSection.xml b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/ViewOrderSection.xml deleted file mode 100644 index 608067d7d31a1..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Section/ViewOrderSection.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="ViewOrderSection"> - <element name="openInvoiceForm" type="button" selector="#order_invoice"/> - <element name="selectCaptureType" type="select" selector="select[name='invoice[capture_case]']"/> - <element name="submitInvoice" type="button" selector="button.submit-button"/> - <element name="commentsHistory" type="button" selector="#sales_order_view_tabs_order_history"/> - <element name="capturedAmountText" type="text" selector="//div[@class='comments-block-item'][2]/div[@class='comments-block-item-comment']"/> - <element name="capturedAmountTextUnsubmitted" type="text" selector="//div[@class='comments-block-item'][1]/div[@class='comments-block-item-comment']"/> - <element name="orderStatus" type="text" selector=".note-list-item .note-list-status"/> - <element name="invoices" type="button" selector="#sales_order_view_tabs_order_invoices"/> - <element name="firstInvoice" type="text" selector=".data-grid tbody tr"/> - <element name="transactions" type="button" selector="#sales_order_view_tabs_order_transactions"/> - <element name="confirmCapture" type="text" selector="//table[@id='order_transactions_table']/tbody/tr/td[6]"/> - <element name="confirmClosed" type="text" selector="//table[@id='order_transactions_table']/tbody/tr/td[7]"/> - </section> -</sections> diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Test/ConfigureAuthorizenetAcceptjsWithoutRequiredOptionsTest.xml b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Test/ConfigureAuthorizenetAcceptjsWithoutRequiredOptionsTest.xml deleted file mode 100644 index cbb702c26f17d..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Test/ConfigureAuthorizenetAcceptjsWithoutRequiredOptionsTest.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="ConfigureAuthorizenetAcceptjsWithoutRequiredOptionsTest"> - <annotations> - <stories value="Authorize.net Accept.js"/> - <title value="Unable to configure Authorize.net Accept.js without required options"/> - <description value="Unable to configure Authorize.net Accept.js without required options"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-17805"/> - <useCaseId value="MC-17753"/> - <group value="AuthorizenetAcceptjs"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <amOnPage url="{{AdminConfigPaymentMethodsPage.url}}" stepKey="navigateToPaymentConfigurationPage"/> - <actionGroup ref="EnableAuthorizenetAcceptjs" stepKey="enableAuthorizenetAcceptjs"/> - <actionGroup ref="AssertAuthorizenetAcceptjsRequiredFieldsValidationIsPresentOnSave" stepKey="assertErrorMessages"/> - </test> -</tests> diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Test/FullCaptureAuthorizenetAcceptjsTest.xml b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Test/FullCaptureAuthorizenetAcceptjsTest.xml deleted file mode 100644 index 7f25482d627e1..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Test/FullCaptureAuthorizenetAcceptjsTest.xml +++ /dev/null @@ -1,77 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="FullCaptureAuthorizenetAcceptjsTest"> - <annotations> - <stories value="Authorize.net Accept.js"/> - <title value="Full Capture using Authorize.net Accept.js"/> - <description value="Capture an order placed using Authorize.net Accept.js"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-12255"/> - <skip> - <issueId value="DEVOPS-4604"/> - </skip> - <group value="AuthorizenetAcceptjs"/> - <group value="ThirdPartyPayments"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <createData stepKey="createCustomer" entity="Simple_US_Customer"/> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - - <!--Configure Auth.net--> - <amOnPage url="{{AdminConfigPaymentMethodsPage.url}}" stepKey="navigateToPaymentConfigurationPage"/> - <actionGroup ref="ConfigureAuthorizenetAcceptjs" stepKey="configureAuthorizenetAcceptjs"> - <argument name="paymentAction" value="Authorize Only"/> - </actionGroup> - <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveConfig"/> - - </before> - <after> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <actionGroup ref="DisableAuthorizenetAcceptjs" stepKey="DisableAuthorizenetAcceptjs"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <!--Storefront Login--> - <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginStorefront"> - <argument name="Customer" value="$$createCustomer$$"/> - </actionGroup> - - <!--Add product to cart--> - <amOnPage url="$$createProduct.name$$.html" stepKey="goToProductPage"/> - <waitForPageLoad stepKey="waitForProductPage"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> - <waitForPageLoad stepKey="waitForCartToFill"/> - <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> - - <!--Checkout steps--> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="GoToCheckoutFromMinicartActionGroup"/> - <waitForPageLoad stepKey="waitForCheckoutLoad"/> - <actionGroup ref="CheckoutSelectFlatRateShippingMethodActionGroup" stepKey="selectFlatRateShipping"/> - <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="submitShippingSelection"/> - <waitForPageLoad stepKey="waitForShippingToFinish"/> - <actionGroup ref="FillPaymentInformation" stepKey="fillPaymentInfo"/> - - <!--View and validate order--> - <actionGroup ref="ViewAndValidateOrderActionGroup" stepKey="viewAndValidateOrder"> - <argument name="amount" value="{{AuthorizenetAcceptjsOrderValidationData.twoSimpleProductsOrderAmount}}"/> - <argument name="status" value="{{AuthorizenetAcceptjsOrderValidationData.processingStatusProcessing}}"/> - <argument name="captureStatus" value="{{AuthorizenetAcceptjsOrderValidationData.captureStatusCapture}}"/> - <argument name="closedStatus" value="{{AuthorizenetAcceptjsOrderValidationData.closedStatusNo}}"/> - </actionGroup> - </test> -</tests> diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Test/GuestCheckoutVirtualProductAuthorizenetAcceptjsTest.xml b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Test/GuestCheckoutVirtualProductAuthorizenetAcceptjsTest.xml deleted file mode 100644 index 919c32d8f70d6..0000000000000 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Test/GuestCheckoutVirtualProductAuthorizenetAcceptjsTest.xml +++ /dev/null @@ -1,86 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="GuestCheckoutVirtualProductAuthorizenetAcceptjsTest"> - <annotations> - <stories value="Authorize.net Accept.js"/> - <title value="Guest Checkout of Virtual Product using Authorize.net Accept.js"/> - <description value="Checkout a virtual product with a guest using Authorize.net Accept.js"/> - <severity value="CRITICAL"/> - <testCaseId value="MC-12712"/> - <skip> - <issueId value="DEVOPS-4604"/> - </skip> - <group value="AuthorizenetAcceptjs"/> - <group value="ThirdPartyPayments"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - - <!-- Create virtual product --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <waitForPageLoad stepKey="waitForProductIndexPage"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> - <argument name="product" value="defaultVirtualProduct"/> - </actionGroup> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductForm"> - <argument name="product" value="defaultVirtualProduct"/> - </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProductForm"/> - - <!--Configure Auth.net--> - <amOnPage url="{{AdminConfigPaymentMethodsPage.url}}" stepKey="navigateToPaymentConfigurationPage"/> - <actionGroup ref="ConfigureAuthorizenetAcceptjs" stepKey="configureAuthorizenetAcceptjs"> - <argument name="paymentAction" value="Authorize and Capture"/> - </actionGroup> - <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveConfig"/> - - </before> - <after> - <actionGroup ref="DisableAuthorizenetAcceptjs" stepKey="DisableAuthorizenetAcceptjs"/> - <!-- Delete virtual product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> - <argument name="product" value="defaultVirtualProduct"/> - </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> - </after> - - <!--Add product to cart twice--> - <amOnPage url="{{defaultVirtualProduct.sku}}.html" stepKey="goToProductPage"/> - <waitForPageLoad stepKey="waitForProductPage"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> - <waitForPageLoad stepKey="waitForCartToFill"/> - <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCartAgain"/> - <waitForPageLoad stepKey="waitForCartToFillAgain"/> - <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage2"/> - - <!--Checkout steps--> - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="GoToCheckoutFromMinicartActionGroup"/> - <waitForPageLoad stepKey="waitForCheckoutLoad"/> - - <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="enterEmail"/> - <click stepKey="clickOnAuthorizenetToggle" selector="{{AuthorizenetCheckoutSection.selectAuthorizenet}}"/> - <waitForPageLoad stepKey="waitForBillingInfoLoad"/> - <actionGroup ref="GuestCheckoutAuthorizenetFillBillingAddress" stepKey="fillAddressForm"> - <argument name="customer" value="Simple_US_Customer"/> - <argument name="customerAddress" value="CustomerAddressSimple"/> - </actionGroup> - <actionGroup ref="FillPaymentInformation" stepKey="fillPaymentInfo"/> - - <!--View and validate order--> - <actionGroup ref="ViewAndValidateOrderActionGroupNoSubmit" stepKey="viewAndValidateOrder"> - <argument name="amount" value="{{AuthorizenetAcceptjsOrderValidationData.virtualProductOrderAmount}}"/> - <argument name="status" value="{{AuthorizenetAcceptjsOrderValidationData.processingStatusProcessing}}"/> - <argument name="captureStatus" value="{{AuthorizenetAcceptjsOrderValidationData.captureStatusCapture}}"/> - <argument name="closedStatus" value="{{AuthorizenetAcceptjsOrderValidationData.closedStatusNo}}"/> - </actionGroup> - </test> -</tests> diff --git a/app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/system.xml b/app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/system.xml index 8623919cf5d6b..7cd00959d9772 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/system.xml +++ b/app/code/Magento/AuthorizenetAcceptjs/etc/adminhtml/system.xml @@ -9,7 +9,7 @@ <system> <section id="payment"> <group id="authorizenet_acceptjs" translate="label" type="text" sortOrder="34" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Authorize.Net</label> + <label>Authorize.Net (Deprecated)</label> <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Enabled</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> diff --git a/app/code/Magento/AuthorizenetAcceptjs/i18n/en_US.csv b/app/code/Magento/AuthorizenetAcceptjs/i18n/en_US.csv index 3c5b677c88cc8..a8b5dbd2df525 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/i18n/en_US.csv +++ b/app/code/Magento/AuthorizenetAcceptjs/i18n/en_US.csv @@ -1,11 +1,11 @@ -Authorize.net,Authorize.net +"Authorize.Net (Deprecated)","Authorize.Net (Deprecated)" "Gateway URL","Gateway URL" "Invalid payload type.","Invalid payload type." "Something went wrong in the payment gateway.","Something went wrong in the payment gateway." "Merchant MD5 (deprecated","Merchant MD5 (deprecated" "Signature Key","Signature Key" "Basic Authorize.Net Settings","Basic Authorize.Net Settings" -"Advanced Authorie.Net Settings","Advanced Authorie.Net Settings" +"Advanced Authorize.Net Settings","Advanced Authorize.Net Settings" "Public Client Key","Public Client Key" "Environment","Environment" "Production","Production" diff --git a/app/code/Magento/AuthorizenetAcceptjs/registration.php b/app/code/Magento/AuthorizenetAcceptjs/registration.php index 5338c9a4ddc80..52a0c497a0993 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/registration.php +++ b/app/code/Magento/AuthorizenetAcceptjs/registration.php @@ -6,6 +6,6 @@ declare(strict_types=1); -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_AuthorizenetAcceptjs', __DIR__); diff --git a/app/code/Magento/AuthorizenetCardinal/Gateway/Request/Authorize3DSecureBuilder.php b/app/code/Magento/AuthorizenetCardinal/Gateway/Request/Authorize3DSecureBuilder.php index 00def8ce2b0cf..bf8e1661a3f61 100644 --- a/app/code/Magento/AuthorizenetCardinal/Gateway/Request/Authorize3DSecureBuilder.php +++ b/app/code/Magento/AuthorizenetCardinal/Gateway/Request/Authorize3DSecureBuilder.php @@ -16,6 +16,9 @@ /** * Adds the cardholder authentication information to the request + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class Authorize3DSecureBuilder implements BuilderInterface { diff --git a/app/code/Magento/AuthorizenetCardinal/Gateway/Validator/CavvResponseValidator.php b/app/code/Magento/AuthorizenetCardinal/Gateway/Validator/CavvResponseValidator.php index 036c1fa332ebf..35287406a12d5 100644 --- a/app/code/Magento/AuthorizenetCardinal/Gateway/Validator/CavvResponseValidator.php +++ b/app/code/Magento/AuthorizenetCardinal/Gateway/Validator/CavvResponseValidator.php @@ -16,6 +16,9 @@ /** * Validates cardholder authentication verification response code. + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class CavvResponseValidator extends AbstractValidator { diff --git a/app/code/Magento/AuthorizenetCardinal/Model/Checkout/ConfigProvider.php b/app/code/Magento/AuthorizenetCardinal/Model/Checkout/ConfigProvider.php index d0cde9c643ebf..8f09395874dce 100644 --- a/app/code/Magento/AuthorizenetCardinal/Model/Checkout/ConfigProvider.php +++ b/app/code/Magento/AuthorizenetCardinal/Model/Checkout/ConfigProvider.php @@ -12,6 +12,9 @@ /** * Configuration provider. + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class ConfigProvider implements ConfigProviderInterface { diff --git a/app/code/Magento/AuthorizenetCardinal/Model/Config.php b/app/code/Magento/AuthorizenetCardinal/Model/Config.php index e70a6a2e39c1f..798fb846c160e 100644 --- a/app/code/Magento/AuthorizenetCardinal/Model/Config.php +++ b/app/code/Magento/AuthorizenetCardinal/Model/Config.php @@ -14,6 +14,9 @@ * AuthorizenetCardinal integration configuration. * * Class is a proxy service for retrieving configuration settings. + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class Config { diff --git a/app/code/Magento/AuthorizenetCardinal/Observer/DataAssignObserver.php b/app/code/Magento/AuthorizenetCardinal/Observer/DataAssignObserver.php index cb2cdf64ae389..aa5fbee327fe5 100644 --- a/app/code/Magento/AuthorizenetCardinal/Observer/DataAssignObserver.php +++ b/app/code/Magento/AuthorizenetCardinal/Observer/DataAssignObserver.php @@ -15,11 +15,14 @@ /** * Adds the payment info to the payment object + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class DataAssignObserver extends AbstractDataAssignObserver { /** - * JWT key + * Cardinal JWT key */ private const JWT_KEY = 'cardinalJWT'; diff --git a/app/code/Magento/AuthorizenetCardinal/registration.php b/app/code/Magento/AuthorizenetCardinal/registration.php index 0153e9eaa4d29..7d663df3c3e3a 100644 --- a/app/code/Magento/AuthorizenetCardinal/registration.php +++ b/app/code/Magento/AuthorizenetCardinal/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_AuthorizenetCardinal', __DIR__); diff --git a/app/code/Magento/AuthorizenetGraphQl/Model/AuthorizenetDataProvider.php b/app/code/Magento/AuthorizenetGraphQl/Model/AuthorizenetDataProvider.php index 704f0af85da06..27d0693884121 100644 --- a/app/code/Magento/AuthorizenetGraphQl/Model/AuthorizenetDataProvider.php +++ b/app/code/Magento/AuthorizenetGraphQl/Model/AuthorizenetDataProvider.php @@ -13,6 +13,9 @@ /** * SetPaymentMethod additional data provider model for Authorizenet payment method + * + * @deprecated Starting from Magento 2.3.4 Authorize.net payment method core integration is deprecated in favor of + * official payment integration available on the marketplace */ class AuthorizenetDataProvider implements AdditionalDataProviderInterface { @@ -46,21 +49,6 @@ public function getData(array $data): array __('Required parameter "authorizenet_acceptjs" for "payment_method" is missing.') ); } - if (!isset($data[self::PATH_ADDITIONAL_DATA]['opaque_data_descriptor'])) { - throw new GraphQlInputException( - __('Required parameter "opaque_data_descriptor" for "authorizenet_acceptjs" is missing.') - ); - } - if (!isset($data[self::PATH_ADDITIONAL_DATA]['opaque_data_value'])) { - throw new GraphQlInputException( - __('Required parameter "opaque_data_value" for "authorizenet_acceptjs" is missing.') - ); - } - if (!isset($data[self::PATH_ADDITIONAL_DATA]['cc_last_4'])) { - throw new GraphQlInputException( - __('Required parameter "cc_last_4" for "authorizenet_acceptjs" is missing.') - ); - } $additionalData = $this->arrayManager->get(static::PATH_ADDITIONAL_DATA, $data); foreach ($additionalData as $key => $value) { diff --git a/app/code/Magento/Backend/Block/Store/Switcher.php b/app/code/Magento/Backend/Block/Store/Switcher.php index 9c35cfb5df81d..2b9f70844df86 100644 --- a/app/code/Magento/Backend/Block/Store/Switcher.php +++ b/app/code/Magento/Backend/Block/Store/Switcher.php @@ -592,7 +592,7 @@ public function getHintHtml() 'What is this?' ) . '"' . ' class="admin__field-tooltip-action action-help"><span>' . __( 'What is this?' - ) . '</span></a></span>' . ' </div>'; + ) . '</span></a>' . ' </div>'; } return $html; } diff --git a/app/code/Magento/Backend/Block/System/Account/Edit/Form.php b/app/code/Magento/Backend/Block/System/Account/Edit/Form.php index 7c5246143b2c6..c075585a6e4eb 100644 --- a/app/code/Magento/Backend/Block/System/Account/Edit/Form.php +++ b/app/code/Magento/Backend/Block/System/Account/Edit/Form.php @@ -68,7 +68,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ protected function _prepareForm() { @@ -114,7 +114,7 @@ protected function _prepareForm() 'name' => 'password', 'label' => __('New Password'), 'title' => __('New Password'), - 'class' => 'validate-admin-password admin__control-text' + 'class' => 'validate-admin-password' ] ); @@ -124,7 +124,7 @@ protected function _prepareForm() [ 'name' => 'password_confirmation', 'label' => __('Password Confirmation'), - 'class' => 'validate-cpassword admin__control-text' + 'class' => 'validate-cpassword' ] ); @@ -152,7 +152,7 @@ protected function _prepareForm() 'label' => __('Your Password'), 'id' => self::IDENTITY_VERIFICATION_PASSWORD_FIELD, 'title' => __('Your Password'), - 'class' => 'validate-current-password required-entry admin__control-text', + 'class' => 'validate-current-password required-entry', 'required' => true ] ); diff --git a/app/code/Magento/Backend/Block/Widget/Grid.php b/app/code/Magento/Backend/Block/Widget/Grid.php index 66298d23389fb..86ad00bfaa7ca 100644 --- a/app/code/Magento/Backend/Block/Widget/Grid.php +++ b/app/code/Magento/Backend/Block/Widget/Grid.php @@ -300,7 +300,10 @@ protected function _addColumnFilterToCollection($column) if ($this->getCollection()) { $field = $column->getFilterIndex() ? $column->getFilterIndex() : $column->getIndex(); if ($column->getFilterConditionCallback()) { - call_user_func($column->getFilterConditionCallback(), $this->getCollection(), $column); + $column->getFilterConditionCallback()[0]->{$column->getFilterConditionCallback()[1]}( + $this->getCollection(), + $column + ); } else { $condition = $column->getFilter()->getCondition(); if ($field && isset($condition)) { @@ -363,7 +366,7 @@ protected function _prepareCollection() $this->_setFilterValues($data); } elseif ($filter && is_array($filter)) { $this->_setFilterValues($filter); - } elseif (0 !== sizeof($this->_defaultFilter)) { + } elseif (0 !== count($this->_defaultFilter)) { $this->_setFilterValues($this->_defaultFilter); } diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Action.php b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Action.php index f4558594332c3..a7d85a4cfef4c 100644 --- a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Action.php +++ b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/Action.php @@ -47,7 +47,7 @@ public function render(\Magento\Framework\DataObject $row) return ' '; } - if (sizeof($actions) == 1 && !$this->getColumn()->getNoLink()) { + if (count($actions) == 1 && !$this->getColumn()->getNoLink()) { foreach ($actions as $action) { if (is_array($action)) { return $this->_toLinkHtml($action, $row); @@ -104,6 +104,7 @@ protected function _toLinkHtml($action, \Magento\Framework\DataObject $row) $this->_transformActionData($action, $actionCaption, $row); if (isset($action['confirm'])) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction $action['onclick'] = 'return window.confirm(\'' . addslashes( $this->escapeHtml($action['confirm']) ) . '\')'; @@ -117,8 +118,8 @@ protected function _toLinkHtml($action, \Magento\Framework\DataObject $row) /** * Prepares action data for html render * - * @param array &$action - * @param string &$actionCaption + * @param &array $action + * @param &string $actionCaption * @param \Magento\Framework\DataObject $row * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) @@ -144,7 +145,7 @@ protected function _transformActionData(&$action, &$actionCaption, \Magento\Fram if (is_array($action['url']) && isset($action['field'])) { $params = [$action['field'] => $this->_getValue($row)]; if (isset($action['url']['params'])) { - $params = array_merge($action['url']['params'], $params); + $params[] = $action['url']['params']; } $action['href'] = $this->getUrl($action['url']['base'], $params); unset($action['field']); diff --git a/app/code/Magento/Backend/Helper/Dashboard/AbstractDashboard.php b/app/code/Magento/Backend/Helper/Dashboard/AbstractDashboard.php index 6eb3ecb3049a9..7cb8690b4ec27 100644 --- a/app/code/Magento/Backend/Helper/Dashboard/AbstractDashboard.php +++ b/app/code/Magento/Backend/Helper/Dashboard/AbstractDashboard.php @@ -8,6 +8,7 @@ /** * Adminhtml abstract dashboard helper. * + * phpcs:disable Magento2.Classes.AbstractApi * @api * @since 100.0.2 */ @@ -28,6 +29,8 @@ abstract class AbstractDashboard extends \Magento\Framework\App\Helper\AbstractH protected $_params = []; /** + * Return collections + * * @return array|\Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection */ public function getCollection() @@ -39,6 +42,8 @@ public function getCollection() } /** + * Init collections + * * @return void */ abstract protected function _initCollection(); @@ -54,14 +59,18 @@ public function getItems() } /** + * Return items count + * * @return int */ public function getCount() { - return sizeof($this->getItems()); + return count($this->getItems()); } /** + * Return column + * * @param string $index * @return array */ @@ -85,6 +94,8 @@ public function getColumn($index) } /** + * Set params with value + * * @param string $name * @param mixed $value * @return void @@ -95,6 +106,8 @@ public function setParam($name, $value) } /** + * Set params + * * @param array $params * @return void */ @@ -104,6 +117,8 @@ public function setParams(array $params) } /** + * Get params with name + * * @param string $name * @return mixed */ @@ -117,6 +132,8 @@ public function getParam($name) } /** + * Get params + * * @return array */ public function getParams() diff --git a/app/code/Magento/Backend/Helper/Dashboard/Data.php b/app/code/Magento/Backend/Helper/Dashboard/Data.php index 29bffbd6a9dc2..c06e7ea3ba38f 100644 --- a/app/code/Magento/Backend/Helper/Dashboard/Data.php +++ b/app/code/Magento/Backend/Helper/Dashboard/Data.php @@ -68,7 +68,7 @@ public function getStores() */ public function countStores() { - return sizeof($this->_stores->getItems()); + return count($this->_stores->getItems()); } /** @@ -88,8 +88,7 @@ public function getDatePeriods() } /** - * Create data hash to ensure that we got valid - * data and it is not changed by some one else. + * Create data hash to ensure that we got valid data and it is not changed by some one else. * * @param string $data * @return string @@ -97,6 +96,7 @@ public function getDatePeriods() public function getChartDataHash($data) { $secret = $this->_installDate; + // phpcs:disable Magento2.Security.InsecureFunction.FoundWithAlternative return md5($data . $secret); } } diff --git a/app/code/Magento/Backend/Model/Auth/Session.php b/app/code/Magento/Backend/Model/Auth/Session.php index 6d2f8f6a21d4a..809b78b7b98bc 100644 --- a/app/code/Magento/Backend/Model/Auth/Session.php +++ b/app/code/Magento/Backend/Model/Auth/Session.php @@ -5,20 +5,17 @@ */ namespace Magento\Backend\Model\Auth; -use Magento\Framework\Acl; -use Magento\Framework\AclFactory; -use Magento\Framework\App\ObjectManager; use Magento\Framework\Stdlib\Cookie\CookieMetadataFactory; use Magento\Framework\Stdlib\CookieManagerInterface; -use Magento\Backend\Spi\SessionUserHydratorInterface; -use Magento\Backend\Spi\SessionAclHydratorInterface; -use Magento\User\Model\User; -use Magento\User\Model\UserFactory; /** * Backend Auth session model * * @api + * @method \Magento\User\Model\User|null getUser() + * @method \Magento\Backend\Model\Auth\Session setUser(\Magento\User\Model\User $value) + * @method \Magento\Framework\Acl|null getAcl() + * @method \Magento\Backend\Model\Auth\Session setAcl(\Magento\Framework\Acl $value) * @method int getUpdatedAt() * @method \Magento\Backend\Model\Auth\Session setUpdatedAt(int $value) * @@ -59,36 +56,6 @@ class Session extends \Magento\Framework\Session\SessionManager implements \Mage */ protected $_config; - /** - * @var SessionUserHydratorInterface - */ - private $userHydrator; - - /** - * @var SessionAclHydratorInterface - */ - private $aclHydrator; - - /** - * @var UserFactory - */ - private $userFactory; - - /** - * @var AclFactory - */ - private $aclFactory; - - /** - * @var User|null - */ - private $user; - - /** - * @var Acl|null - */ - private $acl; - /** * @param \Magento\Framework\App\Request\Http $request * @param \Magento\Framework\Session\SidResolverInterface $sidResolver @@ -103,10 +70,6 @@ class Session extends \Magento\Framework\Session\SessionManager implements \Mage * @param \Magento\Backend\Model\UrlInterface $backendUrl * @param \Magento\Backend\App\ConfigInterface $config * @throws \Magento\Framework\Exception\SessionException - * @param SessionUserHydratorInterface|null $userHydrator - * @param SessionAclHydratorInterface|null $aclHydrator - * @param UserFactory|null $userFactory - * @param AclFactory|null $aclFactory * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -121,19 +84,11 @@ public function __construct( \Magento\Framework\App\State $appState, \Magento\Framework\Acl\Builder $aclBuilder, \Magento\Backend\Model\UrlInterface $backendUrl, - \Magento\Backend\App\ConfigInterface $config, - ?SessionUserHydratorInterface $userHydrator = null, - ?SessionAclHydratorInterface $aclHydrator = null, - ?UserFactory $userFactory = null, - ?AclFactory $aclFactory = null + \Magento\Backend\App\ConfigInterface $config ) { $this->_config = $config; $this->_aclBuilder = $aclBuilder; $this->_backendUrl = $backendUrl; - $this->userHydrator = $userHydrator ?? ObjectManager::getInstance()->get(SessionUserHydratorInterface::class); - $this->aclHydrator = $aclHydrator ?? ObjectManager::getInstance()->get(SessionAclHydratorInterface::class); - $this->userFactory = $userFactory ?? ObjectManager::getInstance()->get(UserFactory::class); - $this->aclFactory = $aclFactory ?? ObjectManager::getInstance()->get(AclFactory::class); parent::__construct( $request, $sidResolver, @@ -277,16 +232,6 @@ public function processLogin() return $this; } - /** - * @inheritDoc - */ - public function destroy(array $options = null) - { - $this->user = null; - $this->acl = null; - parent::destroy($options); - } - /** * Process of configuring of current auth storage when logout was performed * @@ -310,142 +255,4 @@ public function isValidForPath($path) { return true; } - - /** - * Logged-in user. - * - * @return User|null - */ - public function getUser() - { - if (!$this->user) { - $userData = $this->getUserData(); - if ($userData) { - /** @var User $user */ - $user = $this->userFactory->create(); - $this->userHydrator->hydrate($user, $userData); - $this->user = $user; - } elseif ($user = parent::getUser()) { - $this->setUser($user); - } - } - - return $this->user; - } - - /** - * Set logged-in user instance. - * - * @param User|null $user - * @return Session - */ - public function setUser($user) - { - $this->setUserData(null); - if ($user) { - $this->setUserData($this->userHydrator->extract($user)); - } - $this->user = $user; - - return $this; - } - - /** - * Is user logged in? - * - * @return bool - */ - public function hasUser() - { - return (bool)$this->getUser(); - } - - /** - * Remove logged-in user. - * - * @return Session - */ - public function unsUser() - { - $this->user = null; - parent::unsUser(); - return $this->unsUserData(); - } - - /** - * Logged-in user's ACL data. - * - * @return Acl|null - */ - public function getAcl() - { - if (!$this->acl) { - $aclData = $this->getUserAclData(); - if ($aclData) { - /** @var Acl $acl */ - $acl = $this->aclFactory->create(); - $this->aclHydrator->hydrate($acl, $aclData); - $this->acl = $acl; - } elseif ($acl = parent::getAcl()) { - $this->setAcl($acl); - } - } - - return $this->acl; - } - - /** - * Set logged-in user's ACL data instance. - * - * @param Acl|null $acl - * @return Session - */ - public function setAcl($acl) - { - $this->setUserAclData(null); - if ($acl) { - $this->setUserAclData($this->aclHydrator->extract($acl)); - } - $this->acl = $acl; - - return $this; - } - - /** - * Whether ACL data is present. - * - * @return bool - */ - public function hasAcl() - { - return (bool)$this->getAcl(); - } - - /** - * Remove ACL data. - * - * @return Session - */ - public function unsAcl() - { - $this->acl = null; - parent::unsAcl(); - return $this->unsUserAclData(); - } - - /** - * @inheritDoc - */ - public function writeClose() - { - //Updating data in session in case these objects has been changed. - if ($this->user) { - $this->setUser($this->user); - } - if ($this->acl) { - $this->setAcl($this->acl); - } - - parent::writeClose(); - } } diff --git a/app/code/Magento/Backend/Model/Auth/SessionAclHydrator.php b/app/code/Magento/Backend/Model/Auth/SessionAclHydrator.php deleted file mode 100644 index 34e01be696672..0000000000000 --- a/app/code/Magento/Backend/Model/Auth/SessionAclHydrator.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\Backend\Model\Auth; - -use Magento\Backend\Spi\SessionAclHydratorInterface; -use Magento\Framework\Acl; - -/** - * @inheritDoc - */ -class SessionAclHydrator extends Acl implements SessionAclHydratorInterface -{ - /** - * @inheritDoc - */ - public function extract(Acl $acl): array - { - return ['rules' => $acl->_rules, 'resources' => $acl->_resources, 'roles' => $acl->_roleRegistry]; - } - - /** - * @inheritDoc - */ - public function hydrate(Acl $target, array $data): void - { - $target->_rules = $data['rules']; - $target->_resources = $data['resources']; - $target->_roleRegistry = $data['roles']; - } -} diff --git a/app/code/Magento/Backend/Model/Auth/SessionUserHydrator.php b/app/code/Magento/Backend/Model/Auth/SessionUserHydrator.php deleted file mode 100644 index 6dee8b7b302c8..0000000000000 --- a/app/code/Magento/Backend/Model/Auth/SessionUserHydrator.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\Backend\Model\Auth; - -use Magento\Backend\Spi\SessionUserHydratorInterface; -use Magento\User\Model\User; -use Magento\Authorization\Model\Role; -use Magento\Authorization\Model\RoleFactory; - -/** - * @inheritDoc - */ -class SessionUserHydrator implements SessionUserHydratorInterface -{ - /** - * @var RoleFactory - */ - private $roleFactory; - - /** - * @param RoleFactory $roleFactory - */ - public function __construct(RoleFactory $roleFactory) - { - $this->roleFactory = $roleFactory; - } - - /** - * @inheritDoc - */ - public function extract(User $user): array - { - return ['data' => $user->getData(), 'role_data' => $user->getRole()->getData()]; - } - - /** - * @inheritDoc - */ - public function hydrate(User $target, array $data): void - { - $target->setData($data['data']); - /** @var Role $role */ - $role = $this->roleFactory->create(); - $role->setData($data['role_data']); - $target->setData('extracted_role', $role); - $target->getRole(); - } -} diff --git a/app/code/Magento/Backend/Model/Menu/Item/Validator.php b/app/code/Magento/Backend/Model/Menu/Item/Validator.php index 7b72b355f551d..62225c5707c0d 100644 --- a/app/code/Magento/Backend/Model/Menu/Item/Validator.php +++ b/app/code/Magento/Backend/Model/Menu/Item/Validator.php @@ -6,6 +6,9 @@ namespace Magento\Backend\Model\Menu\Item; /** + * Class Validator + * + * @package Magento\Backend\Model\Menu\Item * @api * @since 100.0.2 */ @@ -49,7 +52,7 @@ public function __construct() $attributeValidator = new \Zend_Validate(); $attributeValidator->addValidator(new \Zend_Validate_StringLength(['min' => 3])); - $attributeValidator->addValidator(new \Zend_Validate_Regex('/^[A-Za-z0-9\/_]+$/')); + $attributeValidator->addValidator(new \Zend_Validate_Regex('/^[A-Za-z0-9\/_\-]+$/')); $textValidator = new \Zend_Validate_StringLength(['min' => 3, 'max' => 50]); @@ -101,6 +104,7 @@ private function checkMenuItemIsRemoved($data) /** * Check that menu item contains all required data + * * @param array $data * * @throws \BadMethodCallException diff --git a/app/code/Magento/Backend/Spi/SessionAclHydratorInterface.php b/app/code/Magento/Backend/Spi/SessionAclHydratorInterface.php deleted file mode 100644 index 7227cc92fcc8e..0000000000000 --- a/app/code/Magento/Backend/Spi/SessionAclHydratorInterface.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\Backend\Spi; - -use Magento\Framework\Acl; - -/** - * Extract/hydrate user's ACL data to/from session. - */ -interface SessionAclHydratorInterface -{ - /** - * Extract ACL data to store in session. - * - * @param Acl $acl - * @return array Array of scalars. - */ - public function extract(Acl $acl): array; - - /** - * Fill ACL object with data from session. - * - * @param Acl $target - * @param array $data Data from session. - * @return void - */ - public function hydrate(Acl $target, array $data): void; -} diff --git a/app/code/Magento/Backend/Spi/SessionUserHydratorInterface.php b/app/code/Magento/Backend/Spi/SessionUserHydratorInterface.php deleted file mode 100644 index 211c7b01df3be..0000000000000 --- a/app/code/Magento/Backend/Spi/SessionUserHydratorInterface.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\Backend\Spi; - -use Magento\User\Model\User; - -/** - * Extract/hydrate user data to/from session. - */ -interface SessionUserHydratorInterface -{ - /** - * Extract user data to store in session. - * - * @param User $user - * @return array Array of scalars. - */ - public function extract(User $user): array; - - /** - * Fill User object with data from session. - * - * @param User $target - * @param array $data Data from session. - * @return void - */ - public function hydrate(User $target, array $data): void; -} diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminLogoutActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminLogoutActionGroup.xml new file mode 100644 index 0000000000000..de0def48a1f52 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminLogoutActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminLogoutActionGroup"> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminNavigateMenuActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminNavigateMenuActionGroup.xml index 37fd1cb17ffe3..8d8a3f005d147 100644 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminNavigateMenuActionGroup.xml +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminNavigateMenuActionGroup.xml @@ -17,6 +17,7 @@ <argument name="submenuUiId" type="string"/> </arguments> + <waitForPageLoad stepKey="waitPageLoad"/> <click selector="{{AdminMenuSection.menuItem(menuUiId)}}" stepKey="clickOnMenuItem"/> <click selector="{{AdminMenuSection.menuItem(submenuUiId)}}" stepKey="clickOnSubmenuItem"/> </actionGroup> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.xml new file mode 100644 index 0000000000000..3499f4e0d951c --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.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="AssertAdminUserIsInGridActionGroup"> + <arguments> + <argument name="user" type="entity"/> + </arguments> + <click selector="{{AdminUserGridSection.resetButton}}" stepKey="resetGridFilter"/> + <waitForPageLoad stepKey="waitForFiltersReset" time="15"/> + <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{user.username}}" stepKey="enterUserName"/> + <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearch"/> + <waitForPageLoad stepKey="waitForGridToLoad" time="15"/> + <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{user.username}}" stepKey="seeUser"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.xml new file mode 100644 index 0000000000000..0747eab31588e --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.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="AssertUserRoleRestrictedAccessActionGroup"> + <see selector="{{AdminHeaderSection.pageHeading}}" userInput="Sorry, you need permissions to view this content." stepKey="seeErrorMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Backend/Test/Mftf/Data/CookieConfigData.xml b/app/code/Magento/Backend/Test/Mftf/Data/CookieConfigData.xml index 52a6c27a37ea8..a844e962202f8 100644 --- a/app/code/Magento/Backend/Test/Mftf/Data/CookieConfigData.xml +++ b/app/code/Magento/Backend/Test/Mftf/Data/CookieConfigData.xml @@ -20,4 +20,20 @@ <data key="scope_code">base</data> <data key="value">''</data> </entity> + <entity name="ChangeWebCookieLifetimeConfigData"> + <data key="path">web/cookie/cookie_lifetime</data> + <data key="value">60</data> + </entity> + <entity name="DefaultWebCookieLifetimeConfigData"> + <data key="path">web/cookie/cookie_lifetime</data> + <data key="value">3600</data> + </entity> + <entity name="ChangeAdminSecuritySessionLifetimeConfigData"> + <data key="path">admin/security/session_lifetime</data> + <data key="value">60</data> + </entity> + <entity name="DefaultAdminSecuritySessionLifetimeConfigData"> + <data key="path">admin/security/session_lifetime</data> + <data key="value">7200</data> + </entity> </entities> diff --git a/app/code/Magento/Backend/Test/Mftf/Data/GeneralLocalConfigsData.xml b/app/code/Magento/Backend/Test/Mftf/Data/GeneralLocalConfigsData.xml new file mode 100644 index 0000000000000..22d595c39407f --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Data/GeneralLocalConfigsData.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="GeneralLocalCodeConfigsForChina"> + <data key="path">general/locale/code</data> + <data key="scope">websites</data> + <data key="scope_code">base</data> + <data key="value">zh_Hans_CN</data> + </entity> + <entity name="GeneralLocalCodeConfigsForUS"> + <data key="path">general/locale/code</data> + <data key="scope">websites</data> + <data key="scope_code">base</data> + <data key="value">en_US</data> + </entity> +</entities> diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminLoginFormSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminLoginFormSection.xml index bd65dea89abc2..0ad63c0f0d23a 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminLoginFormSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminLoginFormSection.xml @@ -13,5 +13,6 @@ <element name="password" type="input" selector="#login"/> <element name="signIn" type="button" selector=".actions .action-primary" timeout="30"/> <element name="forgotPasswordLink" type="button" selector=".action-forgotpassword" timeout="10"/> + <element name="loginBlock" type="block" selector=".adminhtml-auth-login"/> </section> </sections> diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml index be3ef92acf0ac..bb1123d01c867 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml @@ -14,5 +14,13 @@ <element name="error" type="text" selector="#messages div.message-error"/> <element name="notice" type="text" selector=".message.message-notice.notice"/> <element name="messageByType" type="text" selector="#messages div.message-{{messageType}}" parameterized="true" /> + <element name="warning" type="text" selector="#messages div.message-warning"/> + <element name="accessDenied" type="text" selector=".access-denied-page"/> + <!-- Deprecated elements, please do not use them. Use elements above--> + <!-- Elements below are too common and catch non messages blocks. Ex: system messages blocks--> + <element name="successMessage" type="text" selector=".message-success"/> + <element name="errorMessage" type="text" selector=".message.message-error.error"/> + <element name="warningMessage" type="text" selector=".message-warning"/> + <element name="noticeMessage" type="text" selector=".message-notice"/> </section> </sections> diff --git a/app/code/Magento/Backend/Test/Mftf/Section/LocaleOptionsSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/LocaleOptionsSection.xml index 3ad8adf9e1b96..5aaefc383f413 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/LocaleOptionsSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/LocaleOptionsSection.xml @@ -15,5 +15,6 @@ <element name="localeEnabled" type="select" selector="#general_locale_code:enabled"/> <element name="localeDisabled" type="select" selector="#general_locale_code[disabled=disabled]"/> <element name="useDefault" type="checkbox" selector="#general_locale_timezone_inherit"/> + <element name="defaultLocale" type="checkbox" selector="#general_locale_code_inherit"/> </section> </sections> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest.xml index 59a6a7e261b87..47b8715b5541c 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest.xml @@ -11,6 +11,7 @@ <test name="AdminCheckLocaleAndDeveloperConfigInDeveloperModeTest"> <annotations> <features value="Backend"/> + <stories value="Menu Navigation"/> <title value="Check locale dropdown and developer configuration page are available in developer mode"/> <description value="Check locale dropdown and developer configuration page are available in developer mode"/> <group value="backend"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml index 2dade727ca411..ae7722b225cdd 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminCheckLocaleAndDeveloperConfigInProductionModeTest.xml @@ -11,6 +11,7 @@ <test name="AdminCheckLocaleAndDeveloperConfigInProductionModeTest"> <annotations> <features value="Backend"/> + <stories value="Menu Navigation"/> <title value="Check locale dropdown and developer configuration page are not available in production mode"/> <description value="Check locale dropdown and developer configuration page are not available in production mode"/> <testCaseId value="MC-14106" /> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardNavigateMenuTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardNavigateMenuTest.xml index 33561d7c3b03e..6434d74b28754 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardNavigateMenuTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardNavigateMenuTest.xml @@ -25,6 +25,7 @@ <after> <actionGroup ref="logout" stepKey="logout"/> </after> + <waitForPageLoad stepKey="waitForPageLoad"/> <click selector="{{AdminMenuSection.menuItem(AdminMenuDashboard.dataUiId)}}" stepKey="clickOnMenuItem"/> <actionGroup ref="AdminAssertPageTitleActionGroup" stepKey="seePageTitle"> <argument name="title" value="{{AdminMenuDashboard.pageTitle}}"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml index f48c7752efc7a..3ba965f746722 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml @@ -54,7 +54,7 @@ <comment userInput="Add product to the shopping cart" stepKey="addProductToCart"/> <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToSimpleProductPage"/> <waitForPageLoad stepKey="waitForProductPageLoad"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> <argument name="productName" value="$$createProduct.name$$"/> </actionGroup> <!--Go to Checkout--> @@ -76,7 +76,7 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> <!-- Search for Order in the order grid --> <comment userInput="Search for Order in the order grid" stepKey="searchOrderInGrid"/> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> <waitForLoadingMaskToDisappear stepKey="waitForSearchingOrder"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireAdminSessionTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireAdminSessionTest.xml new file mode 100644 index 0000000000000..88d26c052b59b --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireAdminSessionTest.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminExpireAdminSessionTest"> + <annotations> + <features value="Backend"/> + <stories value="Admin Session Expire"/> + <title value="Admin Session Expire"/> + <description value="Admin Session Expire"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14111"/> + <group value="Backend"/> + <group value="mtf_migrated"/> + </annotations> + <after> + <!-- 4. Restore default configuration settings. --> + <magentoCLI command="config:set {{DefaultAdminSecuritySessionLifetimeConfigData.path}} {{DefaultAdminSecuritySessionLifetimeConfigData.value}}" stepKey="setDefaultSessionLifetime"/> + </after> + <!-- 1. Apply configuration settings. --> + <magentoCLI command="config:set {{ChangeAdminSecuritySessionLifetimeConfigData.path}} {{ChangeAdminSecuritySessionLifetimeConfigData.value}}" stepKey="changeCookieLifetime"/> + + <!-- 2. Wait for session to expire. --> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <wait time="60" stepKey="waitForSessionLifetime"/> + <reloadPage stepKey="reloadPage"/> + + <!-- 3. Perform asserts. --> + <seeElement selector="{{AdminLoginFormSection.loginBlock}}" stepKey="assertAdminLoginPageIsAvailable"/> + </test> +</tests> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireCustomerSessionTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireCustomerSessionTest.xml new file mode 100644 index 0000000000000..88646401e3a99 --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminExpireCustomerSessionTest.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminExpireCustomerSessionTest"> + <annotations> + <features value="Backend"/> + <stories value="Customer Session Expire"/> + <title value="Customer Session Expireon"/> + <description value="Customer Session Expire"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14110"/> + <group value="Backend"/> + <group value="mtf_migrated"/> + </annotations> + <after> + <!-- 6. Restore default configuration settings. --> + <magentoCLI command="config:set {{DefaultWebCookieLifetimeConfigData.path}} {{DefaultWebCookieLifetimeConfigData.value}}" stepKey="setDefaultCookieLifetime"/> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- 1. Login to Admin. --> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + + <!-- 2. Create customer if needed. --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + + <!-- 3. Apply configuration settings. --> + <magentoCLI command="config:set {{ChangeWebCookieLifetimeConfigData.path}} {{ChangeWebCookieLifetimeConfigData.value}}" stepKey="changeCookieLifetime"/> + + <!-- 4. Wait for session to expire. --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> + <argument name="Customer" value="$$createCustomer$$" /> + </actionGroup> + <wait time="60" stepKey="waitForCookieLifetime"/> + <reloadPage stepKey="reloadPage"/> + + <!-- 5. Perform asserts. --> + <seeElement selector="{{StorefrontPanelHeaderSection.customerLoginLink}}" stepKey="assertAuthorizationLinkIsVisibleOnStoreFront"/> + </test> +</tests> diff --git a/app/code/Magento/Backend/Test/Unit/Helper/Dashboard/DataTest.php b/app/code/Magento/Backend/Test/Unit/Helper/Dashboard/DataTest.php new file mode 100644 index 0000000000000..21c72cb6b4477 --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Helper/Dashboard/DataTest.php @@ -0,0 +1,111 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Backend\Test\Unit\Helper\Dashboard; + +use Magento\Backend\Helper\Dashboard\Data as HelperData; +use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\Config\ConfigOptionsListConstants; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Store\Model\ResourceModel\Store\Collection as StoreCollection; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class DataTest extends TestCase +{ + /** + * Stub path install + */ + private const STUB_PATH_INSTALL = 'Sat, 6 Sep 2014 16:46:11 UTC'; + + /** + * Stub chart data hash + */ + private const STUB_CHART_DATA_HASH = '52870842b23068a78220e01eb9d4404d'; + + /** + * @var \Magento\Backend\Helper\Dashboard\Data + */ + private $helper; + + /** + * @var StoreManagerInterface|MockObject + */ + private $storeManagerMock; + + /** + * @var DeploymentConfig|MockObject + */ + private $deploymentConfigMock; + + /** + * Prepare environment for test + */ + protected function setUp() + { + $this->storeManagerMock = $this->createMock(StoreManagerInterface::class); + $this->deploymentConfigMock = $this->createMock(DeploymentConfig::class); + $this->deploymentConfigMock->expects($this->once())->method('get') + ->with(ConfigOptionsListConstants::CONFIG_PATH_INSTALL_DATE) + ->will($this->returnValue(self::STUB_PATH_INSTALL)); + + $objectManager = new ObjectManager($this); + $this->helper = $objectManager->getObject( + HelperData::class, + [ + 'storeManager' => $this->storeManagerMock, + 'deploymentConfig' => $this->deploymentConfigMock + ] + ); + } + + /** + * Test getStores() when $_stores attribute is null + */ + public function testGetStoresWhenStoreAttributeIsNull() + { + $storeMock = $this->createPartialMock(Store::class, ['getResourceCollection']); + $storeCollectionMock = $this->createMock(StoreCollection::class); + + $storeCollectionMock->expects($this->once())->method('load')->willReturnSelf(); + $storeMock->expects($this->once())->method('getResourceCollection')->willReturn($storeCollectionMock); + $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($storeMock); + + $this->assertEquals($storeCollectionMock, $this->helper->getStores()); + } + + /** + * Test getDatePeriods() method + */ + public function testGetDatePeriods() + { + $this->assertEquals( + [ + '24h' => (string)__('Last 24 Hours'), + '7d' => (string)__('Last 7 Days'), + '1m' => (string)__('Current Month'), + '1y' => (string)__('YTD'), + '2y' => (string)__('2YTD') + ], + $this->helper->getDatePeriods() + ); + } + + /** + * Test getChartDataHash() method + */ + public function testGetChartDataHash() + { + $this->assertEquals( + self::STUB_CHART_DATA_HASH, + $this->helper->getChartDataHash(self::STUB_PATH_INSTALL) + ); + } +} diff --git a/app/code/Magento/Backend/Test/Unit/Helper/JsTest.php b/app/code/Magento/Backend/Test/Unit/Helper/JsTest.php new file mode 100644 index 0000000000000..ff10158a11943 --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Helper/JsTest.php @@ -0,0 +1,83 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backend\Test\Unit\Helper; + +use Magento\Backend\Helper\Js; +use PHPUnit\Framework\TestCase; + +/** + * Class JsTest + * + * Testing decoding serialized grid data + */ +class JsTest extends TestCase +{ + /** + * @var Js + */ + private $helper; + + /** + * Set Up + */ + protected function setUp() + { + $this->helper = new Js(); + } + + /** + * Test decoding the serialized input + * + * @dataProvider getEncodedDataProvider + * + * @param string $encoded + * @param array $expected + */ + public function testDecodeGridSerializedInput(string $encoded, array $expected) + { + $this->assertEquals($expected, $this->helper->decodeGridSerializedInput($encoded)); + } + + /** + * Get serialized grid input + * + * @return array + */ + public function getEncodedDataProvider(): array + { + return [ + 'Decoding empty serialized string' => [ + '', + [] + ], + 'Decoding a simplified serialized string' => [ + '1&2&3&4', + [1, 2, 3, 4] + ], + 'Decoding encoded serialized string' => [ + '2=dGVzdC1zdHJpbmc=', + [ + 2 => [ + 'test-string' => '' + ] + ] + ], + 'Decoding multiple encoded serialized strings' => [ + '2=dGVzdC1zdHJpbmc=&3=bmV3LXN0cmluZw==', + [ + 2 => [ + 'test-string' => '' + ], + 3 => [ + 'new-string' => '' + ] + ] + ] + ]; + } +} diff --git a/app/code/Magento/Backend/Test/Unit/Model/Auth/SessionTest.php b/app/code/Magento/Backend/Test/Unit/Model/Auth/SessionTest.php new file mode 100644 index 0000000000000..dd8e06307cecc --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Model/Auth/SessionTest.php @@ -0,0 +1,276 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Backend\Test\Unit\Model\Auth; + +use Magento\Backend\Model\Auth\Session; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + +/** + * Class SessionTest tests Magento\Backend\Model\Auth\Session + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class SessionTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Backend\App\Config | \PHPUnit_Framework_MockObject_MockObject + */ + private $config; + + /** + * @var \Magento\Framework\Session\Config | \PHPUnit_Framework_MockObject_MockObject + */ + private $sessionConfig; + + /** + * @var \Magento\Framework\Stdlib\CookieManagerInterface | \PHPUnit_Framework_MockObject_MockObject + */ + private $cookieManager; + + /** + * @var \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory | \PHPUnit_Framework_MockObject_MockObject + */ + private $cookieMetadataFactory; + + /** + * @var \Magento\Framework\Session\Storage | \PHPUnit_Framework_MockObject_MockObject + */ + private $storage; + + /** + * @var \Magento\Framework\Acl\Builder | \PHPUnit_Framework_MockObject_MockObject + */ + private $aclBuilder; + + /** + * @var Session + */ + private $session; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->cookieMetadataFactory = $this->createPartialMock( + \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory::class, + ['createPublicCookieMetadata'] + ); + + $this->config = $this->createPartialMock(\Magento\Backend\App\Config::class, ['getValue']); + $this->cookieManager = $this->createPartialMock( + \Magento\Framework\Stdlib\Cookie\PhpCookieManager::class, + ['getCookie', 'setPublicCookie'] + ); + $this->storage = $this->createPartialMock( + \Magento\Framework\Session\Storage::class, + ['getUser', 'getAcl', 'setAcl'] + ); + $this->sessionConfig = $this->createPartialMock( + \Magento\Framework\Session\Config::class, + ['getCookiePath', 'getCookieDomain', 'getCookieSecure', 'getCookieHttpOnly'] + ); + $this->aclBuilder = $this->getMockBuilder(\Magento\Framework\Acl\Builder::class) + ->disableOriginalConstructor() + ->getMock(); + $objectManager = new ObjectManager($this); + $this->session = $objectManager->getObject( + \Magento\Backend\Model\Auth\Session::class, + [ + 'config' => $this->config, + 'sessionConfig' => $this->sessionConfig, + 'cookieManager' => $this->cookieManager, + 'cookieMetadataFactory' => $this->cookieMetadataFactory, + 'storage' => $this->storage, + 'aclBuilder' => $this->aclBuilder + ] + ); + } + + protected function tearDown() + { + $this->config = null; + $this->sessionConfig = null; + $this->session = null; + } + + /** + * @dataProvider refreshAclDataProvider + * @param $isUserPassedViaParams + */ + public function testRefreshAcl($isUserPassedViaParams) + { + $aclMock = $this->getMockBuilder(\Magento\Framework\Acl::class)->disableOriginalConstructor()->getMock(); + $this->aclBuilder->expects($this->any())->method('getAcl')->willReturn($aclMock); + $userMock = $this->getMockBuilder(\Magento\User\Model\User::class) + ->setMethods(['getReloadAclFlag', 'setReloadAclFlag', 'unsetData', 'save']) + ->disableOriginalConstructor() + ->getMock(); + $userMock->expects($this->any())->method('getReloadAclFlag')->willReturn(true); + $userMock->expects($this->once())->method('setReloadAclFlag')->with('0')->willReturnSelf(); + $userMock->expects($this->once())->method('save'); + $this->storage->expects($this->once())->method('setAcl')->with($aclMock); + $this->storage->expects($this->any())->method('getAcl')->willReturn($aclMock); + if ($isUserPassedViaParams) { + $this->session->refreshAcl($userMock); + } else { + $this->storage->expects($this->once())->method('getUser')->willReturn($userMock); + $this->session->refreshAcl(); + } + $this->assertSame($aclMock, $this->session->getAcl()); + } + + /** + * @return array + */ + public function refreshAclDataProvider() + { + return [ + 'User set via params' => [true], + 'User set to session object' => [false] + ]; + } + + public function testIsLoggedInPositive() + { + $user = $this->createPartialMock(\Magento\User\Model\User::class, ['getId', '__wakeup']); + $user->expects($this->once()) + ->method('getId') + ->will($this->returnValue(1)); + + $this->storage->expects($this->any()) + ->method('getUser') + ->will($this->returnValue($user)); + + $this->assertTrue($this->session->isLoggedIn()); + } + + public function testProlong() + { + $name = session_name(); + $cookie = 'cookie'; + $lifetime = 900; + $path = '/'; + $domain = 'magento2'; + $secure = true; + $httpOnly = true; + + $this->config->expects($this->once()) + ->method('getValue') + ->with(\Magento\Backend\Model\Auth\Session::XML_PATH_SESSION_LIFETIME) + ->willReturn($lifetime); + $cookieMetadata = $this->createMock(\Magento\Framework\Stdlib\Cookie\PublicCookieMetadata::class); + $cookieMetadata->expects($this->once()) + ->method('setDuration') + ->with($lifetime) + ->will($this->returnSelf()); + $cookieMetadata->expects($this->once()) + ->method('setPath') + ->with($path) + ->will($this->returnSelf()); + $cookieMetadata->expects($this->once()) + ->method('setDomain') + ->with($domain) + ->will($this->returnSelf()); + $cookieMetadata->expects($this->once()) + ->method('setSecure') + ->with($secure) + ->will($this->returnSelf()); + $cookieMetadata->expects($this->once()) + ->method('setHttpOnly') + ->with($httpOnly) + ->will($this->returnSelf()); + + $this->cookieMetadataFactory->expects($this->once()) + ->method('createPublicCookieMetadata') + ->will($this->returnValue($cookieMetadata)); + + $this->cookieManager->expects($this->once()) + ->method('getCookie') + ->with($name) + ->will($this->returnValue($cookie)); + $this->cookieManager->expects($this->once()) + ->method('setPublicCookie') + ->with($name, $cookie, $cookieMetadata); + + $this->sessionConfig->expects($this->once()) + ->method('getCookiePath') + ->will($this->returnValue($path)); + $this->sessionConfig->expects($this->once()) + ->method('getCookieDomain') + ->will($this->returnValue($domain)); + $this->sessionConfig->expects($this->once()) + ->method('getCookieSecure') + ->will($this->returnValue($secure)); + $this->sessionConfig->expects($this->once()) + ->method('getCookieHttpOnly') + ->will($this->returnValue($httpOnly)); + + $this->session->prolong(); + + $this->assertLessThanOrEqual(time(), $this->session->getUpdatedAt()); + } + + /** + * @dataProvider isAllowedDataProvider + * @param bool $isUserDefined + * @param bool $isAclDefined + * @param bool $isAllowed + * @param true $expectedResult + */ + public function testIsAllowed($isUserDefined, $isAclDefined, $isAllowed, $expectedResult) + { + $userAclRole = 'userAclRole'; + if ($isAclDefined) { + $aclMock = $this->getMockBuilder(\Magento\Framework\Acl::class)->disableOriginalConstructor()->getMock(); + $this->storage->expects($this->any())->method('getAcl')->willReturn($aclMock); + } + if ($isUserDefined) { + $userMock = $this->getMockBuilder(\Magento\User\Model\User::class)->disableOriginalConstructor()->getMock(); + $this->storage->expects($this->once())->method('getUser')->willReturn($userMock); + } + if ($isAclDefined && $isUserDefined) { + $userMock->expects($this->any())->method('getAclRole')->willReturn($userAclRole); + $aclMock->expects($this->once())->method('isAllowed')->with($userAclRole)->willReturn($isAllowed); + } + + $this->assertEquals($expectedResult, $this->session->isAllowed('resource')); + } + + /** + * @return array + */ + public function isAllowedDataProvider() + { + return [ + "Negative: User not defined" => [false, true, true, false], + "Negative: Acl not defined" => [true, false, true, false], + "Negative: Permission denied" => [true, true, false, false], + "Positive: Permission granted" => [true, true, false, false], + ]; + } + + /** + * @dataProvider firstPageAfterLoginDataProvider + * @param bool $isFirstPageAfterLogin + */ + public function testFirstPageAfterLogin($isFirstPageAfterLogin) + { + $this->session->setIsFirstPageAfterLogin($isFirstPageAfterLogin); + $this->assertEquals($isFirstPageAfterLogin, $this->session->isFirstPageAfterLogin()); + } + + /** + * @return array + */ + public function firstPageAfterLoginDataProvider() + { + return [ + 'First page after login' => [true], + 'Not first page after login' => [false], + ]; + } +} diff --git a/app/code/Magento/Backend/Test/Unit/Model/Authorization/RoleLocatorTest.php b/app/code/Magento/Backend/Test/Unit/Model/Authorization/RoleLocatorTest.php new file mode 100644 index 0000000000000..8264c0868eb90 --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Model/Authorization/RoleLocatorTest.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Backend\Test\Unit\Model\Authorization; + +class RoleLocatorTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Backend\Model\Authorization\RoleLocator + */ + private $_model; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $_sessionMock = []; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->_sessionMock = $this->createPartialMock( + \Magento\Backend\Model\Auth\Session::class, + ['getUser', 'getAclRole', 'hasUser'] + ); + $this->_model = new \Magento\Backend\Model\Authorization\RoleLocator($this->_sessionMock); + } + + public function testGetAclRoleIdReturnsCurrentUserAclRoleId() + { + $this->_sessionMock->expects($this->once())->method('hasUser')->will($this->returnValue(true)); + $this->_sessionMock->expects($this->once())->method('getUser')->will($this->returnSelf()); + $this->_sessionMock->expects($this->once())->method('getAclRole')->will($this->returnValue('some_role')); + $this->assertEquals('some_role', $this->_model->getAclRoleId()); + } +} diff --git a/app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php b/app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php new file mode 100644 index 0000000000000..f3d62b34c46e9 --- /dev/null +++ b/app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php @@ -0,0 +1,130 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Backend\Test\Unit\Model\Locale; + +use Magento\Framework\Locale\Resolver; + +class ManagerTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Backend\Model\Locale\Manager + */ + private $_model; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\TranslateInterface + */ + private $_translator; + + /** + * @var \Magento\Backend\Model\Session + */ + private $_session; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Backend\Model\Auth\Session + */ + private $_authSession; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Backend\App\ConfigInterface + */ + private $_backendConfig; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->_session = $this->createMock(\Magento\Backend\Model\Session::class); + + $this->_authSession = $this->createPartialMock(\Magento\Backend\Model\Auth\Session::class, ['getUser']); + + $this->_backendConfig = $this->getMockForAbstractClass( + \Magento\Backend\App\ConfigInterface::class, + [], + '', + false + ); + + $userMock = new \Magento\Framework\DataObject(); + + $this->_authSession->expects($this->any())->method('getUser')->will($this->returnValue($userMock)); + + $this->_translator = $this->getMockBuilder(\Magento\Framework\TranslateInterface::class) + ->setMethods(['init', 'setLocale']) + ->getMockForAbstractClass(); + + $this->_translator->expects($this->any())->method('setLocale')->will($this->returnValue($this->_translator)); + + $this->_translator->expects($this->any())->method('init')->will($this->returnValue(false)); + + $this->_model = new \Magento\Backend\Model\Locale\Manager( + $this->_session, + $this->_authSession, + $this->_translator, + $this->_backendConfig + ); + } + + /** + * @return array + */ + public function switchBackendInterfaceLocaleDataProvider() + { + return ['case1' => ['locale' => 'de_DE'], 'case2' => ['locale' => 'en_US']]; + } + + /** + * @param string $locale + * @dataProvider switchBackendInterfaceLocaleDataProvider + * @covers \Magento\Backend\Model\Locale\Manager::switchBackendInterfaceLocale + */ + public function testSwitchBackendInterfaceLocale($locale) + { + $this->_model->switchBackendInterfaceLocale($locale); + + $userInterfaceLocale = $this->_authSession->getUser()->getInterfaceLocale(); + $this->assertEquals($userInterfaceLocale, $locale); + + $sessionLocale = $this->_session->getSessionLocale(); + $this->assertEquals($sessionLocale, null); + } + + /** + * @covers \Magento\Backend\Model\Locale\Manager::getUserInterfaceLocale + */ + public function testGetUserInterfaceLocaleDefault() + { + $locale = $this->_model->getUserInterfaceLocale(); + + $this->assertEquals($locale, Resolver::DEFAULT_LOCALE); + } + + /** + * @covers \Magento\Backend\Model\Locale\Manager::getUserInterfaceLocale + */ + public function testGetUserInterfaceLocale() + { + $this->_model->switchBackendInterfaceLocale('de_DE'); + $locale = $this->_model->getUserInterfaceLocale(); + + $this->assertEquals($locale, 'de_DE'); + } + + /** + * @covers \Magento\Backend\Model\Locale\Manager::getUserInterfaceLocale + */ + public function testGetUserInterfaceGeneralLocale() + { + $this->_backendConfig->expects($this->any()) + ->method('getValue') + ->with('general/locale/code') + ->willReturn('test_locale'); + $locale = $this->_model->getUserInterfaceLocale(); + $this->assertEquals($locale, 'test_locale'); + } +} diff --git a/app/code/Magento/Backend/Test/Unit/Model/Menu/Config/_files/invalidMenuXmlArray.php b/app/code/Magento/Backend/Test/Unit/Model/Menu/Config/_files/invalidMenuXmlArray.php index cd3128754444b..5a4c8e978b78b 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Menu/Config/_files/invalidMenuXmlArray.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Menu/Config/_files/invalidMenuXmlArray.php @@ -10,7 +10,7 @@ ' resource="Test_Value::value"/></menu></config>', [ "Element 'add', attribute 'action': [facet 'pattern'] The value '' is not accepted by the " . - "pattern '[a-zA-Z0-9/_]{3,}'.\nLine: 1\n", + "pattern '[a-zA-Z0-9/_\-]{3,}'.\nLine: 1\n", "Element 'add', attribute 'action': '' is not a valid value of the atomic type 'typeAction'.\nLine: 1\n" ], ], @@ -20,7 +20,7 @@ 'resource="Test_Value::value"/></menu></config>', [ "Element 'add', attribute 'action': [facet 'pattern'] The value 'ad' is not accepted by the " . - "pattern '[a-zA-Z0-9/_]{3,}'.\nLine: 1\n", + "pattern '[a-zA-Z0-9/_\-]{3,}'.\nLine: 1\n", "Element 'add', attribute 'action': 'ad' is not a valid value of the atomic type 'typeAction'.\nLine: 1\n" ], ], @@ -31,7 +31,7 @@ '</menu></config>', [ "Element 'add', attribute 'action': [facet 'pattern'] The value 'adm$#@inhtml/notification' is not " . - "accepted by the pattern '[a-zA-Z0-9/_]{3,}'.\nLine: 1\n", + "accepted by the pattern '[a-zA-Z0-9/_\-]{3,}'.\nLine: 1\n", "Element 'add', attribute 'action': 'adm$#@inhtml/notification' is not a valid value of the atomic " . "type 'typeAction'.\nLine: 1\n" ], @@ -452,7 +452,7 @@ '<?xml version="1.0"?><config><menu><update action="" ' . 'id="Test_Value::some_value"/></menu></config>', [ "Element 'update', attribute 'action': [facet 'pattern'] The value '' is not accepted by the " . - "pattern '[a-zA-Z0-9/_]{3,}'.\nLine: 1\n", + "pattern '[a-zA-Z0-9/_\-]{3,}'.\nLine: 1\n", "Element 'update', attribute 'action': '' is not a valid value of the atomic type 'typeAction'.\nLine: 1\n" ], ], @@ -462,7 +462,7 @@ 'resource="Test_Value::value"/></menu></config>', [ "Element 'update', attribute 'action': [facet 'pattern'] The value 'v' is not accepted by the " . - "pattern '[a-zA-Z0-9/_]{3,}'.\nLine: 1\n", + "pattern '[a-zA-Z0-9/_\-]{3,}'.\nLine: 1\n", "Element 'update', attribute 'action': 'v' is not a valid value of the atomic type 'typeAction'.\nLine: 1\n" ], ], @@ -471,7 +471,7 @@ 'id="Test_Value::some_value"/></menu></config>', [ "Element 'update', attribute 'action': [facet 'pattern'] The value '/@##gt;' is not " . - "accepted by the pattern '[a-zA-Z0-9/_]{3,}'.\nLine: 1\n", + "accepted by the pattern '[a-zA-Z0-9/_\-]{3,}'.\nLine: 1\n", "Element 'update', attribute 'action': '/@##gt;' is not a valid value of the atomic" . " type 'typeAction'.\nLine: 1\n" ], diff --git a/app/code/Magento/Backend/composer.json b/app/code/Magento/Backend/composer.json index 5a7884a9607fe..4862e701404f7 100644 --- a/app/code/Magento/Backend/composer.json +++ b/app/code/Magento/Backend/composer.json @@ -22,7 +22,6 @@ "magento/module-store": "*", "magento/module-translation": "*", "magento/module-ui": "*", - "magento/module-authorization": "*", "magento/module-user": "*" }, "suggest": { diff --git a/app/code/Magento/Backend/etc/di.xml b/app/code/Magento/Backend/etc/di.xml index 41db85b9323a8..c526703da9975 100644 --- a/app/code/Magento/Backend/etc/di.xml +++ b/app/code/Magento/Backend/etc/di.xml @@ -198,8 +198,4 @@ <argument name="anchorRenderer" xsi:type="object">Magento\Backend\Block\AnchorRenderer</argument> </arguments> </type> - <preference for="Magento\Backend\Spi\SessionUserHydratorInterface" - type="Magento\Backend\Model\Auth\SessionUserHydrator" /> - <preference for="Magento\Backend\Spi\SessionAclHydratorInterface" - type="Magento\Backend\Model\Auth\SessionAclHydrator" /> </config> diff --git a/app/code/Magento/Backend/etc/menu.xsd b/app/code/Magento/Backend/etc/menu.xsd index 2619b3f5fedac..4b408e8e86a17 100644 --- a/app/code/Magento/Backend/etc/menu.xsd +++ b/app/code/Magento/Backend/etc/menu.xsd @@ -100,7 +100,7 @@ </xs:documentation> </xs:annotation> <xs:restriction base="xs:string"> - <xs:pattern value="[a-zA-Z0-9/_]{3,}" /> + <xs:pattern value="[a-zA-Z0-9/_\-]{3,}" /> </xs:restriction> </xs:simpleType> diff --git a/app/code/Magento/Backend/registration.php b/app/code/Magento/Backend/registration.php index a7a5a58ca0d9c..8f0c19af6e89a 100644 --- a/app/code/Magento/Backend/registration.php +++ b/app/code/Magento/Backend/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Backend', __DIR__); diff --git a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_block.xml b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_block.xml index 6d2ecd8d36a99..0fbf777bdf607 100644 --- a/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_block.xml +++ b/app/code/Magento/Backend/view/adminhtml/layout/adminhtml_cache_block.xml @@ -80,6 +80,7 @@ <argument name="type" xsi:type="string">options</argument> <argument name="width" xsi:type="string">120</argument> <argument name="align" xsi:type="string">left</argument> + <argument name="sortable" xsi:type="string">0</argument> <argument name="options" xsi:type="array"> <item name="disabled" xsi:type="array"> <item name="value" xsi:type="string">0</item> diff --git a/app/code/Magento/Backup/Test/Mftf/ActionGroup/CreateBackupActionGroup.xml b/app/code/Magento/Backup/Test/Mftf/ActionGroup/CreateBackupActionGroup.xml deleted file mode 100644 index cc7691752c62e..0000000000000 --- a/app/code/Magento/Backup/Test/Mftf/ActionGroup/CreateBackupActionGroup.xml +++ /dev/null @@ -1,64 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="createSystemBackup"> - <annotations> - <description>Creates a System Backup using provided Backup Entity.</description> - </annotations> - <arguments> - <argument name="backup" defaultValue="SystemBackup"/> - </arguments> - - <click selector="{{AdminMainActionsSection.systemBackup}}" stepKey="clickCreateBackupButton"/> - <waitForElementVisible selector="{{AdminCreateBackupFormSection.backupNameField}}" stepKey="waitForForm"/> - <fillField selector="{{AdminCreateBackupFormSection.backupNameField}}" userInput="{{backup.name}}" stepKey="fillBackupName"/> - <click selector="{{AdminCreateBackupFormSection.ok}}" stepKey="clickOk"/> - <waitForElementNotVisible selector=".loading-mask" time="300" stepKey="waitForBackupProcess"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You created the system backup." stepKey="seeSuccessMessage"/> - <see selector="{{AdminGridTableSection.backupNameColumn}}" userInput="{{backup.name}}" stepKey="seeBackupInGrid"/> - <see selector="{{AdminGridTableSection.backupTypeByName(backup.name)}}" userInput="{{backup.type}}" stepKey="seeBackupType"/> - </actionGroup> - - <actionGroup name="createMediaBackup"> - <annotations> - <description>Creates a Media Backup using provided Backup Entity.</description> - </annotations> - <arguments> - <argument name="backup" defaultValue="MediaBackup"/> - </arguments> - - <click selector="{{AdminMainActionsSection.mediaBackup}}" stepKey="clickCreateBackupButton"/> - <waitForElementVisible selector="{{AdminCreateBackupFormSection.backupNameField}}" stepKey="waitForForm"/> - <fillField selector="{{AdminCreateBackupFormSection.backupNameField}}" userInput="{{backup.name}}" stepKey="fillBackupName"/> - <click selector="{{AdminCreateBackupFormSection.ok}}" stepKey="clickOk"/> - <waitForPageLoad time="120" stepKey="waitForBackupProcess"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You created the database and media backup." stepKey="seeSuccessMessage"/> - <see selector="{{AdminGridTableSection.backupNameColumn}}" userInput="{{backup.name}}" stepKey="seeBackupInGrid"/> - <see selector="{{AdminGridTableSection.backupTypeByName(backup.name)}}" userInput="{{backup.type}}" stepKey="seeBackupType"/> - </actionGroup> - - <actionGroup name="createDatabaseBackup"> - <annotations> - <description>Creates a Database Backup using provided Backup Entity.</description> - </annotations> - <arguments> - <argument name="backup" defaultValue="DatabaseBackup"/> - </arguments> - - <click selector="{{AdminMainActionsSection.databaseBackup}}" stepKey="clickCreateBackupButton"/> - <waitForElementVisible selector="{{AdminCreateBackupFormSection.backupNameField}}" stepKey="waitForForm"/> - <fillField selector="{{AdminCreateBackupFormSection.backupNameField}}" userInput="{{backup.name}}" stepKey="fillBackupName"/> - <click selector="{{AdminCreateBackupFormSection.ok}}" stepKey="clickOk"/> - <waitForPageLoad time="120" stepKey="waitForBackupProcess"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You created the database backup." stepKey="seeSuccessMessage"/> - <see selector="{{AdminGridTableSection.backupNameColumn}}" userInput="{{backup.name}}" stepKey="seeBackupInGrid"/> - <see selector="{{AdminGridTableSection.backupTypeByName(backup.name)}}" userInput="{{backup.type}}" stepKey="seeBackupType"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Backup/Test/Mftf/ActionGroup/CreateDatabaseBackupActionGroup.xml b/app/code/Magento/Backup/Test/Mftf/ActionGroup/CreateDatabaseBackupActionGroup.xml new file mode 100644 index 0000000000000..171bdb88dc428 --- /dev/null +++ b/app/code/Magento/Backup/Test/Mftf/ActionGroup/CreateDatabaseBackupActionGroup.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="CreateDatabaseBackupActionGroup"> + <annotations> + <description>Creates a Database Backup using provided Backup Entity.</description> + </annotations> + <arguments> + <argument name="backup" defaultValue="DatabaseBackup"/> + </arguments> + + <click selector="{{AdminMainActionsSection.databaseBackup}}" stepKey="clickCreateBackupButton"/> + <waitForElementVisible selector="{{AdminCreateBackupFormSection.backupNameField}}" stepKey="waitForForm"/> + <fillField selector="{{AdminCreateBackupFormSection.backupNameField}}" userInput="{{backup.name}}" stepKey="fillBackupName"/> + <click selector="{{AdminCreateBackupFormSection.ok}}" stepKey="clickOk"/> + <waitForPageLoad time="120" stepKey="waitForBackupProcess"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You created the database backup." stepKey="seeSuccessMessage"/> + <see selector="{{AdminGridTableSection.backupNameColumn}}" userInput="{{backup.name}}" stepKey="seeBackupInGrid"/> + <see selector="{{AdminGridTableSection.backupTypeByName(backup.name)}}" userInput="{{backup.type}}" stepKey="seeBackupType"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Backup/Test/Mftf/ActionGroup/CreateMediaBackupActionGroup.xml b/app/code/Magento/Backup/Test/Mftf/ActionGroup/CreateMediaBackupActionGroup.xml new file mode 100644 index 0000000000000..ffa66ae230c04 --- /dev/null +++ b/app/code/Magento/Backup/Test/Mftf/ActionGroup/CreateMediaBackupActionGroup.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="CreateMediaBackupActionGroup"> + <annotations> + <description>Creates a Media Backup using provided Backup Entity.</description> + </annotations> + <arguments> + <argument name="backup" defaultValue="MediaBackup"/> + </arguments> + + <click selector="{{AdminMainActionsSection.mediaBackup}}" stepKey="clickCreateBackupButton"/> + <waitForElementVisible selector="{{AdminCreateBackupFormSection.backupNameField}}" stepKey="waitForForm"/> + <fillField selector="{{AdminCreateBackupFormSection.backupNameField}}" userInput="{{backup.name}}" stepKey="fillBackupName"/> + <click selector="{{AdminCreateBackupFormSection.ok}}" stepKey="clickOk"/> + <waitForPageLoad time="120" stepKey="waitForBackupProcess"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You created the database and media backup." stepKey="seeSuccessMessage"/> + <see selector="{{AdminGridTableSection.backupNameColumn}}" userInput="{{backup.name}}" stepKey="seeBackupInGrid"/> + <see selector="{{AdminGridTableSection.backupTypeByName(backup.name)}}" userInput="{{backup.type}}" stepKey="seeBackupType"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Backup/Test/Mftf/ActionGroup/CreateSystemBackupActionGroup.xml b/app/code/Magento/Backup/Test/Mftf/ActionGroup/CreateSystemBackupActionGroup.xml new file mode 100644 index 0000000000000..ca4f7ad1544df --- /dev/null +++ b/app/code/Magento/Backup/Test/Mftf/ActionGroup/CreateSystemBackupActionGroup.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="CreateSystemBackupActionGroup"> + <annotations> + <description>Creates a System Backup using provided Backup Entity.</description> + </annotations> + <arguments> + <argument name="backup" defaultValue="SystemBackup"/> + </arguments> + + <click selector="{{AdminMainActionsSection.systemBackup}}" stepKey="clickCreateBackupButton"/> + <waitForElementVisible selector="{{AdminCreateBackupFormSection.backupNameField}}" stepKey="waitForForm"/> + <fillField selector="{{AdminCreateBackupFormSection.backupNameField}}" userInput="{{backup.name}}" stepKey="fillBackupName"/> + <click selector="{{AdminCreateBackupFormSection.ok}}" stepKey="clickOk"/> + <waitForElementNotVisible selector=".loading-mask" time="300" stepKey="waitForBackupProcess"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You created the system backup." stepKey="seeSuccessMessage"/> + <see selector="{{AdminGridTableSection.backupNameColumn}}" userInput="{{backup.name}}" stepKey="seeBackupInGrid"/> + <see selector="{{AdminGridTableSection.backupTypeByName(backup.name)}}" userInput="{{backup.type}}" stepKey="seeBackupType"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Backup/Test/Mftf/Test/AdminCreateAndDeleteBackupsTest.xml b/app/code/Magento/Backup/Test/Mftf/Test/AdminCreateAndDeleteBackupsTest.xml index 26f8817c0a1bb..778c6d5112b6a 100644 --- a/app/code/Magento/Backup/Test/Mftf/Test/AdminCreateAndDeleteBackupsTest.xml +++ b/app/code/Magento/Backup/Test/Mftf/Test/AdminCreateAndDeleteBackupsTest.xml @@ -30,13 +30,13 @@ <waitForPageLoad stepKey="waitForBackupPage"/> <!--Create system backup--> - <actionGroup ref="createSystemBackup" stepKey="createSystemBackup"/> + <actionGroup ref="CreateSystemBackupActionGroup" stepKey="createSystemBackup"/> <!--Create database/media backup--> - <actionGroup ref="createMediaBackup" stepKey="createMediaBackup"/> + <actionGroup ref="CreateMediaBackupActionGroup" stepKey="createMediaBackup"/> <!--Create database backup--> - <actionGroup ref="createDatabaseBackup" stepKey="createDatabaseBackup"/> + <actionGroup ref="CreateDatabaseBackupActionGroup" stepKey="createDatabaseBackup"/> <!--Delete system backup--> <actionGroup ref="deleteBackup" stepKey="deleteSystemBackup"> diff --git a/app/code/Magento/Backup/etc/adminhtml/system.xml b/app/code/Magento/Backup/etc/adminhtml/system.xml index aa6635b4dde4a..78e0aae1dd4c2 100644 --- a/app/code/Magento/Backup/etc/adminhtml/system.xml +++ b/app/code/Magento/Backup/etc/adminhtml/system.xml @@ -12,7 +12,7 @@ <label>Backup Settings</label> <field id="functionality_enabled" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Enable Backup</label> - <comment>Disabled by default for security reasons</comment> + <comment>Disabled by default for security reasons.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="enabled" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> diff --git a/app/code/Magento/Backup/registration.php b/app/code/Magento/Backup/registration.php index 811e3c5657484..59864b84d8217 100644 --- a/app/code/Magento/Backup/registration.php +++ b/app/code/Magento/Backup/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Backup', __DIR__); diff --git a/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php b/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php index ab23037b4e98e..1ba696839a95d 100644 --- a/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php +++ b/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php @@ -67,11 +67,12 @@ public function __construct( public function getConfig() { $storeId = $this->session->getStoreId(); + $isActive = $this->config->isActive($storeId); return [ 'payment' => [ self::CODE => [ - 'isActive' => $this->config->isActive($storeId), - 'clientToken' => $this->getClientToken(), + 'isActive' => $isActive, + 'clientToken' => $isActive ? $this->getClientToken() : null, 'ccTypesMapper' => $this->config->getCcTypesMapper(), 'sdkUrl' => $this->config->getSdkUrl(), 'hostedFieldsSdkUrl' => $this->config->getHostedFieldsSdkUrl(), diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminCreateNewRoleActionGroup.xml b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminCreateNewRoleActionGroup.xml new file mode 100644 index 0000000000000..29000563ee87f --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminCreateNewRoleActionGroup.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="AdminCreateNewRoleActionGroup"> + <annotations> + <description>Creates a User Role using the provided Data.</description> + </annotations> + <arguments> + <argument name="role" type="string" defaultValue=""/> + <argument name="resource" type="string" defaultValue="All"/> + <argument name="scope" type="string" defaultValue="Custom"/> + <argument name="websites" type="string" defaultValue="Main Website"/> + </arguments> + + <click selector="{{AdminCreateRoleSection.create}}" stepKey="clickToAddNewRole"/> + <fillField selector="{{AdminCreateRoleSection.name}}" userInput="{{role.name}}" stepKey="setRoleName"/> + <fillField stepKey="setPassword" selector="{{AdminCreateRoleSection.password}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> + <click selector="{{AdminCreateRoleSection.roleResources}}" stepKey="clickToOpenRoleResources"/> + <waitForPageLoad stepKey="waitForRoleResourcePage" time="5"/> + <click stepKey="checkSales" selector="//a[text()='Sales']"/> + <click selector="{{AdminCreateRoleSection.save}}" stepKey="clickToSaveRole"/> + <waitForPageLoad stepKey="waitForPageLoad" time="10"/> + <see userInput="You saved the role." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminDeleteRoleActionGroup.xml b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminDeleteRoleActionGroup.xml new file mode 100644 index 0000000000000..9ee3c79d1288e --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminDeleteRoleActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminDeleteRoleActionGroup"> + <annotations> + <description>Deletes a User Role that contains the text 'Role'. PLEASE NOTE: The Action Group values are Hardcoded.</description> + </annotations> + <arguments> + <argument name="role" defaultValue=""/> + </arguments> + + <click stepKey="clickOnRole" selector="{{AdminDeleteRoleSection.theRole}}"/> + <fillField stepKey="TypeCurrentPassword" selector="{{AdminDeleteRoleSection.current_pass}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> + <click stepKey="clickToDeleteRole" selector="{{AdminDeleteRoleSection.delete}}"/> + <waitForAjaxLoad stepKey="waitForDeleteConfirmationPopup" time="5"/> + <click stepKey="clickToConfirm" selector="{{AdminDeleteRoleSection.confirm}}"/> + <waitForPageLoad stepKey="waitForPageLoad" time="10"/> + <see stepKey="seeSuccessMessage" userInput="You deleted the role."/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminRoleActionGroup.xml b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminRoleActionGroup.xml deleted file mode 100644 index 384ccbf379bc3..0000000000000 --- a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminRoleActionGroup.xml +++ /dev/null @@ -1,62 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="GoToUserRoles"> - <annotations> - <description>Navigate to the User Roles page via Backend Admin Side Menu. PLEASE NOTE: Use the amOnPage action instead.</description> - </annotations> - - <click selector="#menu-magento-backend-system" stepKey="clickOnSystemIcon"/> - <waitForPageLoad stepKey="waitForSystemsPageToOpen"/> - <click selector="//span[contains(text(), 'User Roles')]" stepKey="clickToSelectUserRoles"/> - <waitForPageLoad stepKey="waitForUserRolesPageToOpen"/> - </actionGroup> - - <!--Create new role--> - <actionGroup name="AdminCreateNewRole"> - <annotations> - <description>Creates a User Role using the provided Data.</description> - </annotations> - <arguments> - <argument name="role" type="string" defaultValue=""/> - <argument name="resource" type="string" defaultValue="All"/> - <argument name="scope" type="string" defaultValue="Custom"/> - <argument name="websites" type="string" defaultValue="Main Website"/> - </arguments> - - <click selector="{{AdminCreateRoleSection.create}}" stepKey="clickToAddNewRole"/> - <fillField selector="{{AdminCreateRoleSection.name}}" userInput="{{role.name}}" stepKey="setRoleName"/> - <fillField stepKey="setPassword" selector="{{AdminCreateRoleSection.password}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> - <click selector="{{AdminCreateRoleSection.roleResources}}" stepKey="clickToOpenRoleResources"/> - <waitForPageLoad stepKey="waitForRoleResourcePage" time="5"/> - <click stepKey="checkSales" selector="//a[text()='Sales']"/> - <click selector="{{AdminCreateRoleSection.save}}" stepKey="clickToSaveRole"/> - <waitForPageLoad stepKey="waitForPageLoad" time="10"/> - <see userInput="You saved the role." stepKey="seeSuccessMessage"/> - </actionGroup> - - <!--Delete role--> - <actionGroup name="AdminDeleteRoleActionGroup"> - <annotations> - <description>Deletes a User Role that contains the text 'Role'. PLEASE NOTE: The Action Group values are Hardcoded.</description> - </annotations> - <arguments> - <argument name="role" defaultValue=""/> - </arguments> - - <click stepKey="clickOnRole" selector="{{AdminDeleteRoleSection.theRole}}"/> - <fillField stepKey="TypeCurrentPassword" selector="{{AdminDeleteRoleSection.current_pass}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> - <click stepKey="clickToDeleteRole" selector="{{AdminDeleteRoleSection.delete}}"/> - <waitForAjaxLoad stepKey="waitForDeleteConfirmationPopup" time="5"/> - <click stepKey="clickToConfirm" selector="{{AdminDeleteRoleSection.confirm}}"/> - <waitForPageLoad stepKey="waitForPageLoad" time="10"/> - <see stepKey="seeSuccessMessage" userInput="You deleted the role."/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminUserActionGroup.xml b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminUserActionGroup.xml deleted file mode 100644 index 78e0db6bc44ee..0000000000000 --- a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/AdminUserActionGroup.xml +++ /dev/null @@ -1,49 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Go to all users--> - <actionGroup name="GoToAllUsers"> - <annotations> - <description>Navigate to the Users page via Backend Admin Side Menu. PLEASE NOTE: Use the amOnPage action instead.</description> - </annotations> - - <click selector="{{AdminCreateUserSection.system}}" stepKey="clickOnSystemIcon"/> - <waitForPageLoad stepKey="waitForSystemsPageToOpen"/> - <click selector="{{AdminCreateUserSection.allUsers}}" stepKey="clickToSelectUserRoles"/> - <waitForPageLoad stepKey="waitForUserRolesPageToOpen"/> - </actionGroup> - - <!--Create new user with specified role--> - <actionGroup name="AdminCreateUserAction"> - <annotations> - <description>Creates a User using the NewAdmin User Entity and User Role Entity. PLEASE NOTE: The Action Group values are Hardcoded.</description> - </annotations> - - <click selector="{{AdminCreateUserSection.create}}" stepKey="clickToCreateNewUser"/> - <waitForPageLoad stepKey="waitForNewUserPageLoad" time="10"/> - <fillField selector="{{AdminCreateUserSection.usernameTextField}}" userInput="{{NewAdmin.username}}" stepKey="enterUserName"/> - <fillField selector="{{AdminCreateUserSection.firstNameTextField}}" userInput="{{NewAdmin.firstName}}" stepKey="enterFirstName"/> - <fillField selector="{{AdminCreateUserSection.lastNameTextField}}" userInput="{{NewAdmin.lastName}}" stepKey="enterLastName"/> - <fillField selector="{{AdminCreateUserSection.emailTextField}}" userInput="{{NewAdmin.email}}" stepKey="enterEmail"/> - <fillField selector="{{AdminCreateUserSection.passwordTextField}}" userInput="{{NewAdmin.password}}" stepKey="enterPassword"/> - <fillField selector="{{AdminCreateUserSection.pwConfirmationTextField}}" userInput="{{NewAdmin.password}}" stepKey="confirmPassword"/> - <fillField selector="{{AdminCreateUserSection.currentPasswordField}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterCurrentPassword"/> - <scrollToTopOfPage stepKey="scrollToTopOfPage"/> - <click selector="{{AdminCreateUserSection.userRoleTab}}" stepKey="clickUserRole"/> - <waitForAjaxLoad stepKey="waitForRoles" time="5"/> - <fillField selector="{{AdminCreateRoleSection.roleNameFilterTextField}}" userInput="{{role.name}}" stepKey="filterRole"/> - <click selector="{{AdminCreateRoleSection.searchButton}}" stepKey="clickSearch"/> - <waitForPageLoad stepKey="waitForSearch" time="10"/> - <click selector="{{AdminCreateRoleSection.searchResultFirstRow}}" stepKey="selectRole"/> - <click selector="{{AdminCreateUserSection.saveButton}}" stepKey="clickSaveUser"/> - <waitForPageLoad stepKey="waitForSaveUser" time="10"/> - <see userInput="You saved the user." stepKey="seeSuccessMessage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/ConfigureBraintreeActionGroup.xml b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/ConfigureBraintreeActionGroup.xml index 6f379c2e66a48..5e5f6d634caad 100644 --- a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/ConfigureBraintreeActionGroup.xml +++ b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/ConfigureBraintreeActionGroup.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="ConfigureBraintree"> + <actionGroup name="ConfigureBraintreeActionGroup"> <annotations> <description>Sets up the Braintree configuration setting using the BraintreeConfigurationSection Data Entity. PLEASE NOTE: The Action Group values are Hardcoded.</description> </annotations> @@ -49,13 +49,4 @@ <click stepKey="save" selector="{{BraintreeConfiguraionSection.save}}"/> <waitForElementVisible selector="{{BraintreeConfiguraionSection.successfulMessage}}" stepKey="waitForSuccessfullyConfigured" time="10"/> </actionGroup> - - <actionGroup name="DisableBrainTree"> - <annotations> - <description>Disables the Braintree and BraintreePaypal configuration settings via the CLI.</description> - </annotations> - - <magentoCLI stepKey="disableBrainTree" command="config:set payment/braintree/active 0"/> - <magentoCLI stepKey="disableBrainTreePaypal" command="config:set payment/braintree_paypal/active 0"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/DisableBraintreeActionGroup.xml b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/DisableBraintreeActionGroup.xml new file mode 100644 index 0000000000000..9f8b7735fc067 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/DisableBraintreeActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DisableBraintreeActionGroup"> + <annotations> + <description>Disables the Braintree and BraintreePaypal configuration settings via the CLI.</description> + </annotations> + + <magentoCLI stepKey="disableBrainTree" command="config:set payment/braintree/active 0"/> + <magentoCLI stepKey="disableBrainTreePaypal" command="config:set payment/braintree_paypal/active 0"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/GoToAllUsersActionGroup.xml b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/GoToAllUsersActionGroup.xml new file mode 100644 index 0000000000000..f5cffbe81b509 --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/GoToAllUsersActionGroup.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="GoToAllUsersActionGroup"> + <annotations> + <description>Navigate to the Users page via Backend Admin Side Menu. PLEASE NOTE: Use the amOnPage action instead.</description> + </annotations> + + <click selector="{{AdminCreateUserSection.system}}" stepKey="clickOnSystemIcon"/> + <waitForPageLoad stepKey="waitForSystemsPageToOpen"/> + <click selector="{{AdminCreateUserSection.allUsers}}" stepKey="clickToSelectUserRoles"/> + <waitForPageLoad stepKey="waitForUserRolesPageToOpen"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/GoToUserRolesActionGroup.xml b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/GoToUserRolesActionGroup.xml new file mode 100644 index 0000000000000..43bdb3cb02c0d --- /dev/null +++ b/app/code/Magento/Braintree/Test/Mftf/ActionGroup/GoToUserRolesActionGroup.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="GoToUserRolesActionGroup"> + <annotations> + <description>Navigate to the User Roles page via Backend Admin Side Menu. PLEASE NOTE: Use the amOnPage action instead.</description> + </annotations> + + <click selector="#menu-magento-backend-system" stepKey="clickOnSystemIcon"/> + <waitForPageLoad stepKey="waitForSystemsPageToOpen"/> + <click selector="//span[contains(text(), 'User Roles')]" stepKey="clickToSelectUserRoles"/> + <waitForPageLoad stepKey="waitForUserRolesPageToOpen"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml b/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml index 2594d245f9ff0..16c8c93e94cbe 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Test/CreateAnAdminOrderUsingBraintreePaymentTest1.xml @@ -21,8 +21,6 @@ <issueId value="MQE-1576"/> </skip> </annotations> - - <before> <!--Login As Admin--> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> @@ -39,17 +37,20 @@ <!--Configure Braintree--> - <actionGroup ref="ConfigureBraintree" stepKey="configureBraintree"/> + <actionGroup ref="ConfigureBraintreeActionGroup" stepKey="configureBraintree"/> <!--Create New Role--> - <actionGroup ref="GoToUserRoles" stepKey="GoToUserRoles"/> + <actionGroup ref="GoToUserRolesActionGroup" stepKey="GoToUserRoles"/> <waitForPageLoad stepKey="waitForAllRoles" time="15"/> - <actionGroup ref="AdminCreateNewRole" stepKey="AdminCreateNewRole"/> + <actionGroup ref="AdminCreateNewRoleActionGroup" stepKey="AdminCreateNewRole"/> <!--Create new admin user--> - <actionGroup ref="GoToAllUsers" stepKey="GoToAllUsers"/> + <actionGroup ref="GoToAllUsersActionGroup" stepKey="GoToAllUsers"/> <waitForPageLoad stepKey="waitForUsers" time="15"/> - <actionGroup ref="AdminCreateUserAction" stepKey="AdminCreateNewUser"/> + <actionGroup ref="AdminCreateUserActionGroup" stepKey="AdminCreateNewUser"> + <argument name="role" value="role"/> + <argument name="User" value="NewAdmin"/> + </actionGroup> <!--SignOut--> <actionGroup ref="logout" stepKey="signOutFromAdmin"/> @@ -59,23 +60,23 @@ <waitForPageLoad stepKey="waitForLogin" time="3"/> <!--Create New Order--> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrder"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrder"> <argument name="customer" value="Simple_US_Customer"/> </actionGroup> <!--Add Product to Order--> - <actionGroup ref="addSimpleProductToOrder" stepKey="addProduct"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addProduct"> <argument name="product" value="_defaultProduct"/> </actionGroup> <!--Fill Order Customer Information--> - <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerAddress"> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerAddress"> <argument name="customer" value="Simple_US_Customer"/> <argument name="address" value="US_Address_TX"/> </actionGroup> <!--Select Shipping--> - <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRateShipping"/> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRateShipping"/> <waitForPageLoad stepKey="waitForShippingToFinish"/> <!--Pay with Braintree --> @@ -88,7 +89,7 @@ <after> <!-- Disable BrainTree --> - <actionGroup ref="DisableBrainTree" stepKey="disableBrainTree"/> + <actionGroup ref="DisableBraintreeActionGroup" stepKey="disableBrainTree"/> <!--SignOut--> <actionGroup ref="SignOut" stepKey="signOutFromNewUser"/> @@ -101,11 +102,11 @@ <deleteData stepKey="deleteCustomer" createDataKey="createCustomer"/> <!--Delete User --> - <actionGroup ref="GoToAllUsers" stepKey="GoBackToAllUsers"/> + <actionGroup ref="GoToAllUsersActionGroup" stepKey="GoBackToAllUsers"/> <actionGroup ref="AdminDeleteNewUserActionGroup" stepKey="AdminDeleteUserActionGroup"/> <!--Delete Role--> - <actionGroup ref="GoToUserRoles" stepKey="GoBackToUserRoles"/> + <actionGroup ref="GoToUserRolesActionGroup" stepKey="GoBackToUserRoles"/> <actionGroup ref="AdminDeleteRoleActionGroup" stepKey="AdminDeleteRoleActionGroup"/> </after> </test> diff --git a/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscount.xml b/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscount.xml index 8f0ce4918b978..371b59418e4a9 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscount.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Test/CretateAdminOrderWithOnlinePaymentIncludingTaxAndDiscount.xml @@ -81,7 +81,7 @@ <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> - <actionGroup ref="selectRetailerCustomerGroup" stepKey="selectRetailerCustomerGroup"/> + <actionGroup ref="SelectRetailerCustomerGroupActionGroup" stepKey="selectRetailerCustomerGroup"/> <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Percent of product price discount" stepKey="selectActionType"/> <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="10" stepKey="fillDiscountAmount"/> @@ -90,25 +90,25 @@ <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> <!--Set Taxable Goods for Shipping Tax Class--> - <actionGroup ref="changeShippingTaxClass" stepKey="changeShippingTaxClass"/> + <actionGroup ref="ChangeShippingTaxClassActionGroup" stepKey="changeShippingTaxClass"/> <!--Adding Special price to product--> <amOnPage url="{{AdminProductEditPage.url($$simpleProduct.id$$)}}" stepKey="openAdminProductEditPage"/> <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="addSpecialPrice"/> - <actionGroup ref="saveProductForm" stepKey="saveProductForm"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> <!--Create New Order--> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> <argument name="customer" value="$$simpleCustomer$$"/> </actionGroup> <!--Add a product to order--> - <actionGroup ref="addSimpleProductToOrder" stepKey="addProductToOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addProductToOrder"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> <!--Select FlatRate shipping method--> - <actionGroup ref="orderSelectFlatRateShipping" stepKey="orderSelectFlatRateShippingMethod"/> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="orderSelectFlatRateShippingMethod"/> <!--Select Braintree online Payment method --> <actionGroup ref="AdminOrderBraintreeFillActionGroup" stepKey="selectCreditCardPayment"/> @@ -119,7 +119,7 @@ <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeOrderSuccessMessage" after="waitForSubmitOrder"/> <!-- Create New invoice--> - <actionGroup ref="adminFastCreateInvoice" stepKey="createInvoice"/> + <actionGroup ref="AdminFastCreateInvoiceActionGroup" stepKey="createInvoice"/> <!--Get access to Credit Memo page from Invoice page--> <click selector="{{AdminInvoiceMainActionsSection.openNewCreditMemoFromInvoice}}" stepKey="clickCreateNewCreditMemo"/> diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Ui/ConfigProviderTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Ui/ConfigProviderTest.php index 55bc2cb195d6e..fb34113be15ae 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/Ui/ConfigProviderTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/Ui/ConfigProviderTest.php @@ -76,7 +76,7 @@ protected function setUp() } /** - * Run test getConfig method + * Ensure that get config returns correct data if payment is active or not * * @param array $config * @param array $expected @@ -84,22 +84,37 @@ protected function setUp() */ public function testGetConfig($config, $expected) { - $this->braintreeAdapter->expects(static::once()) - ->method('generate') - ->willReturn(self::CLIENT_TOKEN); + if ($config['isActive']) { + $this->braintreeAdapter->expects($this->once()) + ->method('generate') + ->willReturn(self::CLIENT_TOKEN); + } else { + $config = array_replace_recursive( + $this->getConfigDataProvider()[0]['config'], + $config + ); + $expected = array_replace_recursive( + $this->getConfigDataProvider()[0]['expected'], + $expected + ); + $this->braintreeAdapter->expects($this->never()) + ->method('generate'); + } foreach ($config as $method => $value) { - $this->config->expects(static::once()) + $this->config->expects($this->once()) ->method($method) ->willReturn($value); } - static::assertEquals($expected, $this->configProvider->getConfig()); + $this->assertEquals($expected, $this->configProvider->getConfig()); } /** - * @covers \Magento\Braintree\Model\Ui\ConfigProvider::getClientToken + * @covers \Magento\Braintree\Model\Ui\ConfigProvider::getClientToken * @dataProvider getClientTokenDataProvider + * @param $merchantAccountId + * @param $params */ public function testGetClientToken($merchantAccountId, $params) { @@ -124,7 +139,7 @@ public function getConfigDataProvider() [ 'config' => [ 'isActive' => true, - 'getCcTypesMapper' => ['visa' => 'VI', 'american-express'=> 'AE'], + 'getCcTypesMapper' => ['visa' => 'VI', 'american-express' => 'AE'], 'getSdkUrl' => self::SDK_URL, 'getHostedFieldsSdkUrl' => 'https://sdk.com/test.js', 'getCountrySpecificCardTypeConfig' => [ @@ -148,7 +163,7 @@ public function getConfigDataProvider() 'ccTypesMapper' => ['visa' => 'VI', 'american-express' => 'AE'], 'sdkUrl' => self::SDK_URL, 'hostedFieldsSdkUrl' => 'https://sdk.com/test.js', - 'countrySpecificCardTypes' =>[ + 'countrySpecificCardTypes' => [ 'GB' => ['VI', 'AE'], 'US' => ['DI', 'JCB'] ], @@ -166,6 +181,19 @@ public function getConfigDataProvider() ] ] ] + ], + [ + 'config' => [ + 'isActive' => false, + ], + 'expected' => [ + 'payment' => [ + ConfigProvider::CODE => [ + 'isActive' => false, + 'clientToken' => null, + ] + ] + ] ] ]; } diff --git a/app/code/Magento/Braintree/registration.php b/app/code/Magento/Braintree/registration.php index 9a266e9706c7d..1a0d00ec6557d 100644 --- a/app/code/Magento/Braintree/registration.php +++ b/app/code/Magento/Braintree/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Braintree', __DIR__); diff --git a/app/code/Magento/BraintreeGraphQl/Model/BraintreeDataProvider.php b/app/code/Magento/BraintreeGraphQl/Model/BraintreeDataProvider.php index 23ca1d88e3625..cb5c4a31837b4 100644 --- a/app/code/Magento/BraintreeGraphQl/Model/BraintreeDataProvider.php +++ b/app/code/Magento/BraintreeGraphQl/Model/BraintreeDataProvider.php @@ -31,19 +31,6 @@ public function getData(array $args): array __('Required parameter "braintree" for "payment_method" is missing.') ); } - - if (!isset($args[self::PATH_ADDITIONAL_DATA]['payment_method_nonce'])) { - throw new GraphQlInputException( - __('Required parameter "payment_method_nonce" for "braintree" is missing.') - ); - } - - if (!isset($args[self::PATH_ADDITIONAL_DATA]['is_active_payment_token_enabler'])) { - throw new GraphQlInputException( - __('Required parameter "is_active_payment_token_enabler" for "braintree" is missing.') - ); - } - return $args[self::PATH_ADDITIONAL_DATA]; } } diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AddBundleOptionWithOneProductActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AddBundleOptionWithOneProductActionGroup.xml new file mode 100644 index 0000000000000..7c60cde4e20d3 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AddBundleOptionWithOneProductActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddBundleOptionWithOneProductActionGroup" extends="AddBundleOptionWithTwoProductsActionGroup"> + <annotations> + <description>Requires Navigation to the Product Creation page. Adds Bundle Option with One Product as specified in arguments. 'x' refers to Bundle option number. 'n' refers to the first number after x.</description> + </annotations> + + <remove keyForRemoval="openProductFilters2"/> + <remove keyForRemoval="fillProductSkuFilter2"/> + <remove keyForRemoval="clickApplyFilters2"/> + <remove keyForRemoval="waitForFilteredGridLoad2"/> + <remove keyForRemoval="selectProduct2"/> + <remove keyForRemoval="selectProduct2"/> + <remove keyForRemoval="fillQuantity1"/> + <remove keyForRemoval="fillQuantity2"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '0')}}" userInput="1" stepKey="fillQuantity" after="clickAddButton1"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AddBundleOptionWithSixProductsActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AddBundleOptionWithSixProductsActionGroup.xml new file mode 100644 index 0000000000000..9fd89bbbfe6d5 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AddBundleOptionWithSixProductsActionGroup.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddBundleOptionWithSixProductsActionGroup" extends="AddBundleOptionWithTwoProductsActionGroup"> + <annotations> + <description>Requires Navigation to Product Creation page. Adds Bundle Option with Six Products as specified in arguments. 'x' refers to Bundle option number. 'n' refers to the first number after x.</description> + </annotations> + <arguments> + <argument name="prodTreeSku" type="string"/> + <argument name="prodFourSku" type="string"/> + <argument name="prodFiveSku" type="string"/> + <argument name="prodSixSku" type="string"/> + </arguments> + + <remove keyForRemoval="fillQuantity1"/> + <remove keyForRemoval="fillQuantity2"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters3" after="selectProduct2"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters3" after="clickClearFilters3"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{prodTreeSku}}" stepKey="fillProductSkuFilter3" after="openProductFilters3"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters3" after="fillProductSkuFilter3"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad3" time="30" after="clickApplyFilters3"/> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectProduct3" after="waitForFilteredGridLoad3"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters4" after="selectProduct3"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters4" after="clickClearFilters4"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{prodFourSku}}" stepKey="fillProductSkuFilter4" after="openProductFilters4"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters4" after="fillProductSkuFilter4"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad4" time="30" after="clickApplyFilters4"/> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectProduct4" after="clickApplyFilters4"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters5" after="selectProduct4"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters5" after="clickClearFilters5"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{prodFiveSku}}" stepKey="fillProductSkuFilter5" after="openProductFilters5"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters5" after="fillProductSkuFilter5"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad5" time="30" after="clickApplyFilters5"/> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectProduct5" after="waitForFilteredGridLoad5"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters6" after="selectProduct5"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters6" after="clickClearFilters6"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{prodSixSku}}" stepKey="fillProductSkuFilter6" after="openProductFilters6"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters6" after="fillProductSkuFilter6"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad6" time="30" after="clickApplyFilters6"/> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectProduct6" after="waitForFilteredGridLoad6"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '0')}}" userInput="2" stepKey="fillQuantity1" after="clickAddButton1"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '1')}}" userInput="2" stepKey="fillQuantity2" after="fillQuantity1"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '2')}}" userInput="2" stepKey="fillQuantity3" after="fillQuantity2"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '3')}}" userInput="2" stepKey="fillQuantity4" after="fillQuantity3"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '4')}}" userInput="2" stepKey="fillQuantity5" after="fillQuantity4"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '5')}}" userInput="2" stepKey="fillQuantity6" after="fillQuantity5"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AddBundleOptionWithTreeProductsActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AddBundleOptionWithTreeProductsActionGroup.xml new file mode 100644 index 0000000000000..085ee47b89e0e --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AddBundleOptionWithTreeProductsActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddBundleOptionWithThreeProductsActionGroup" extends="AddBundleOptionWithTwoProductsActionGroup"> + <annotations> + <description>Requires Navigation to the Product Creation page. Adds Bundle Option with Three Products using the provided arguments. 'x' refers to Bundle option number. 'n' refers to the first number after x.</description> + </annotations> + <arguments> + <argument name="prodTreeSku" type="string"/> + </arguments> + + <remove keyForRemoval="fillQuantity1"/> + <remove keyForRemoval="fillQuantity2"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters3" after="selectProduct2"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters3" after="clickClearFilters3"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{prodTreeSku}}" stepKey="fillProductSkuFilter3" after="openProductFilters3"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters3" after="fillProductSkuFilter3"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad3" time="30" after="clickApplyFilters3"/> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectProduct3" after="waitForFilteredGridLoad3"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '0')}}" userInput="1" stepKey="fillQuantity1" after="clickAddButton1"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '1')}}" userInput="1" stepKey="fillQuantity2" after="fillQuantity1"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '2')}}" userInput="1" stepKey="fillQuantity3" after="fillQuantity2"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AddBundleOptionWithTwoProductsActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AddBundleOptionWithTwoProductsActionGroup.xml new file mode 100644 index 0000000000000..f54704de9dbd9 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AddBundleOptionWithTwoProductsActionGroup.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddBundleOptionWithTwoProductsActionGroup"> + <annotations> + <description>Requires Navigation to the Product Creation page. Adds Bundle Option with Two Products using the provided arguments. 'x' refers to Bundle option number. 'n' refers to the first number after x.</description> + </annotations> + <arguments> + <argument name="x" type="string"/> + <argument name="n" type="string"/> + <argument name="prodOneSku" type="string"/> + <argument name="prodTwoSku" type="string"/> + <argument name="optionTitle" type="string"/> + <argument name="inputType" type="string"/> + </arguments> + + <conditionalClick selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" dependentSelector="{{AdminProductFormBundleSection.bundleItemsToggle}}" visible="false" stepKey="conditionallyOpenSectionBundleItems"/> + <scrollTo selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" stepKey="scrollUpABit"/> + <click selector="{{AdminProductFormBundleSection.addOption}}" stepKey="clickAddOption"/> + <waitForElementVisible selector="{{AdminProductFormBundleSection.bundleOptionXTitle(x)}}" stepKey="waitForOptions"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXTitle(x)}}" userInput="{{optionTitle}}" stepKey="fillTitle"/> + <selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType(x)}}" userInput="{{inputType}}" stepKey="selectType"/> + <waitForElementVisible selector="{{AdminProductFormBundleSection.nthAddProductsToOption(n)}}" stepKey="waitForAddBtn"/> + <click selector="{{AdminProductFormBundleSection.nthAddProductsToOption(n)}}" stepKey="clickAdd"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters1"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters1"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{prodOneSku}}" stepKey="fillProductSkuFilter1"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters1"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad1" time="30"/> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectProduct1"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters2"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters2"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{prodTwoSku}}" stepKey="fillProductSkuFilter2"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters2"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad2" time="30"/> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectProduct2"/> + <click selector="{{AdminAddProductsToOptionPanel.addSelectedProducts}}" stepKey="clickAddButton1"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '0')}}" userInput="50" stepKey="fillQuantity1"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '1')}}" userInput="50" stepKey="fillQuantity2"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminBundleProductActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminBundleProductActionGroup.xml deleted file mode 100644 index e49126f4cf275..0000000000000 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminBundleProductActionGroup.xml +++ /dev/null @@ -1,60 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Fill main fields in create product form--> - <actionGroup name="fillMainBundleProductForm"> - <annotations> - <description>Fills the Name, SKU and Stock Status fields.</description> - </annotations> - <arguments> - <argument name="product" defaultValue="BundleProduct"/> - </arguments> - - <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{product.name}}" stepKey="fillProductSku"/> - <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{product.sku}}" stepKey="fillProductName"/> - <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{product.status}}" stepKey="selectStockStatus"/> - </actionGroup> - - <!--Check that required fields are actually required--> - <actionGroup name="checkRequiredFieldsInBundleProductForm"> - <annotations> - <description>Clears the Name and SKU fields when adding a Product and then verifies that they are required after attempting to Save.</description> - </annotations> - - <clearField selector="{{AdminProductFormSection.productName}}" stepKey="clearProductSku"/> - <clearField selector="{{AdminProductFormSection.productSku}}" stepKey="clearProductName"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Product" stepKey="seeStillOnEditPage"/> - <see selector="{{AdminProductFormSection.fieldError('name')}}" userInput="This is a required field." stepKey="seeNameRequired"/> - <see selector="{{AdminProductFormSection.fieldError('sku')}}" userInput="This is a required field." stepKey="seeSkuRequired"/> - </actionGroup> - - <!--Filter product grid and see expected product--> - <actionGroup name="viewBundleProductInAdminGrid"> - <annotations> - <description>Clears the Grid Filters on the Catalog Grid page and applies Filter by Name and Sku. Then checks to see if the Product exists in the 1st row. Then clears the Grid Filters again for future Tests.</description> - </annotations> - <arguments> - <argument name="product" defaultValue="BundleProduct"/> - <argument name="thumbnail" defaultValue="ProductPlaceholderImage"/> - </arguments> - - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> - <waitForPageLoad stepKey="waitForPageLoadInitial"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> - <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{product.name}}" stepKey="fillProductNameFilter"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillProductSkuFilter"/> - <selectOption selector="{{AdminProductGridFilterSection.typeFilter}}" userInput="{{product.type_id}}" stepKey="selectionProductType"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <see selector="{{AdminProductGridSection.firstProductRow}}" userInput="{{product.name}}" stepKey="seeProductNameInGrid"/> - <click selector="{{AdminProductGridFilterSection.clearFilters}}" stepKey="clickClearFiltersAfter"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiBundleProductActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiBundleProductActionGroup.xml deleted file mode 100644 index 8993a936475b4..0000000000000 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiBundleProductActionGroup.xml +++ /dev/null @@ -1,184 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminCreateApiDynamicBundleProductActionGroup"> - <annotations> - <description>Creates 4 products with varying prices. Creates the bundle product with specified name. Adds the multiple select and checkbox options and 4 links to the created products. Uses the 'ApiBundleProduct' entity.</description> - </annotations> - <arguments> - <argument name="productName" defaultValue="Api Dynamic Bundle Product" type="string"/> - </arguments> - - <!--Create 4 simple products--> - <createData entity="SimpleProduct2" stepKey="simpleProduct1"> - <field key="price">4.99</field> - </createData> - <createData entity="SimpleProduct2" stepKey="simpleProduct2"> - <field key="price">2.89</field> - </createData> - <createData entity="SimpleProduct2" stepKey="simpleProduct3"> - <field key="price">7.33</field> - </createData> - <createData entity="SimpleProduct2" stepKey="simpleProduct4"> - <field key="price">18.25</field> - </createData> - - <!-- Create the bundle product based --> - <createData entity="ApiBundleProduct" stepKey="createBundleProduct"> - <field key="name">{{productName}}</field> - </createData> - <createData entity="MultipleSelectOption" stepKey="createBundleOption1_1"> - <requiredEntity createDataKey="createBundleProduct"/> - <field key="required">false</field> - </createData> - <createData entity="CheckboxOption" stepKey="createBundleOption1_2"> - <requiredEntity createDataKey="createBundleProduct"/> - </createData> - <createData entity="ApiBundleLink" stepKey="linkOptionToProduct"> - <requiredEntity createDataKey="createBundleProduct"/> - <requiredEntity createDataKey="createBundleOption1_1"/> - <requiredEntity createDataKey="simpleProduct1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="linkOptionToProduct2"> - <requiredEntity createDataKey="createBundleProduct"/> - <requiredEntity createDataKey="createBundleOption1_1"/> - <requiredEntity createDataKey="simpleProduct2"/> - </createData> - <createData entity="ApiBundleLink" stepKey="linkOptionToProduct3"> - <requiredEntity createDataKey="createBundleProduct"/> - <requiredEntity createDataKey="createBundleOption1_2"/> - <requiredEntity createDataKey="simpleProduct3"/> - </createData> - <createData entity="ApiBundleLink" stepKey="linkOptionToProduct4"> - <requiredEntity createDataKey="createBundleProduct"/> - <requiredEntity createDataKey="createBundleOption1_2"/> - <requiredEntity createDataKey="simpleProduct4"/> - </createData> - </actionGroup> - - <actionGroup name="AdminCreateApiFixedBundleProductActionGroup"> - <annotations> - <description>Creates 4 products with varying prices. Creates the bundle product with specified name. Adds the multiple select and checkbox options and 4 links to the created products. Uses the 'ApiFixedBundleProduct' entity.</description> - </annotations> - <arguments> - <argument name="productName" defaultValue="Api Fixed Bundle Product" type="string"/> - </arguments> - - <!--Create 4 simple products--> - <createData entity="SimpleProduct2" stepKey="simpleProduct1"> - <field key="price">4.99</field> - </createData> - <createData entity="SimpleProduct2" stepKey="simpleProduct2"> - <field key="price">2.89</field> - </createData> - <createData entity="SimpleProduct2" stepKey="simpleProduct3"> - <field key="price">7.33</field> - </createData> - <createData entity="SimpleProduct2" stepKey="simpleProduct4"> - <field key="price">18.25</field> - </createData> - - <!-- Create the bundle product based --> - <createData entity="ApiFixedBundleProduct" stepKey="createBundleProduct"> - <field key="name">{{productName}}</field> - </createData> - <createData entity="MultipleSelectOption" stepKey="createBundleOption1_1"> - <requiredEntity createDataKey="createBundleProduct"/> - <field key="required">false</field> - </createData> - <createData entity="CheckboxOption" stepKey="createBundleOption1_2"> - <requiredEntity createDataKey="createBundleProduct"/> - </createData> - <createData entity="ApiBundleLink" stepKey="linkOptionToProduct"> - <requiredEntity createDataKey="createBundleProduct"/> - <requiredEntity createDataKey="createBundleOption1_1"/> - <requiredEntity createDataKey="simpleProduct1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="linkOptionToProduct2"> - <requiredEntity createDataKey="createBundleProduct"/> - <requiredEntity createDataKey="createBundleOption1_1"/> - <requiredEntity createDataKey="simpleProduct2"/> - </createData> - <createData entity="ApiBundleLink" stepKey="linkOptionToProduct3"> - <requiredEntity createDataKey="createBundleProduct"/> - <requiredEntity createDataKey="createBundleOption1_2"/> - <requiredEntity createDataKey="simpleProduct3"/> - </createData> - <createData entity="ApiBundleLink" stepKey="linkOptionToProduct4"> - <requiredEntity createDataKey="createBundleProduct"/> - <requiredEntity createDataKey="createBundleOption1_2"/> - <requiredEntity createDataKey="simpleProduct4"/> - </createData> - </actionGroup> - - <actionGroup name="AdminCreateApiDynamicBundleProductAllOptionTypesActionGroup"> - <annotations> - <description>Creates 3 products with varying prices. Creates the dynamic bundle product with specified name. Adds the multiple select, checkbox options and links to the created products. Uses the 'ApiBundleProduct' entity.</description> - </annotations> - <arguments> - <argument name="productName" defaultValue="Api Dynamic Bundle Product" type="string"/> - </arguments> - - <!-- Create simple products --> - <createData entity="SimpleProduct2" stepKey="simpleProduct1"> - <field key="price">10</field> - </createData> - <createData entity="SimpleProduct2" stepKey="simpleProduct2"> - <field key="price">20</field> - </createData> - - <!-- Create Bundle product --> - <createData entity="ApiBundleProduct" stepKey="createBundleProduct"> - <field key="name">{{productName}}</field> - </createData> - <createData entity="DropDownBundleOption" stepKey="createDropDownBundleOption"> - <requiredEntity createDataKey="createBundleProduct"/> - <field key="title">Drop-down Option</field> - </createData> - <createData entity="RadioButtonsOption" stepKey="createBundleRadioButtonsOption"> - <requiredEntity createDataKey="createBundleProduct"/> - <field key="title">Radio Buttons Option</field> - </createData> - <createData entity="CheckboxOption" stepKey="createBundleCheckboxOption"> - <requiredEntity createDataKey="createBundleProduct"/> - <field key="title">Checkbox Option</field> - </createData> - <createData entity="ApiBundleLink" stepKey="linkCheckboxOptionToProduct1"> - <requiredEntity createDataKey="createBundleProduct"/> - <requiredEntity createDataKey="createBundleCheckboxOption"/> - <requiredEntity createDataKey="simpleProduct1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="linkCheckboxOptionToProduct2"> - <requiredEntity createDataKey="createBundleProduct"/> - <requiredEntity createDataKey="createBundleCheckboxOption"/> - <requiredEntity createDataKey="simpleProduct2"/> - </createData> - <createData entity="ApiBundleLink" stepKey="linkDropDownOptionToProduct1"> - <requiredEntity createDataKey="createBundleProduct"/> - <requiredEntity createDataKey="createDropDownBundleOption"/> - <requiredEntity createDataKey="simpleProduct1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="linkDropDownOptionToProduct2"> - <requiredEntity createDataKey="createBundleProduct"/> - <requiredEntity createDataKey="createDropDownBundleOption"/> - <requiredEntity createDataKey="simpleProduct2"/> - </createData> - <createData entity="ApiBundleLink" stepKey="linkRadioButtonsOptionToProduct1"> - <requiredEntity createDataKey="createBundleProduct"/> - <requiredEntity createDataKey="createBundleRadioButtonsOption"/> - <requiredEntity createDataKey="simpleProduct1"/> - </createData> - <createData entity="ApiBundleLink" stepKey="linkRadioButtonsOptionToProduct2"> - <requiredEntity createDataKey="createBundleProduct"/> - <requiredEntity createDataKey="createBundleRadioButtonsOption"/> - <requiredEntity createDataKey="simpleProduct2"/> - </createData> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductActionGroup.xml new file mode 100644 index 0000000000000..7e17822710a18 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductActionGroup.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateApiDynamicBundleProductActionGroup"> + <annotations> + <description>Creates 4 products with varying prices. Creates the bundle product with specified name. Adds the multiple select and checkbox options and 4 links to the created products. Uses the 'ApiBundleProduct' entity.</description> + </annotations> + <arguments> + <argument name="productName" defaultValue="Api Dynamic Bundle Product" type="string"/> + </arguments> + + <!--Create 4 simple products--> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"> + <field key="price">4.99</field> + </createData> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"> + <field key="price">2.89</field> + </createData> + <createData entity="SimpleProduct2" stepKey="simpleProduct3"> + <field key="price">7.33</field> + </createData> + <createData entity="SimpleProduct2" stepKey="simpleProduct4"> + <field key="price">18.25</field> + </createData> + + <!-- Create the bundle product based --> + <createData entity="ApiBundleProduct" stepKey="createBundleProduct"> + <field key="name">{{productName}}</field> + </createData> + <createData entity="MultipleSelectOption" stepKey="createBundleOption1_1"> + <requiredEntity createDataKey="createBundleProduct"/> + <field key="required">false</field> + </createData> + <createData entity="CheckboxOption" stepKey="createBundleOption1_2"> + <requiredEntity createDataKey="createBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct2"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct3"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_2"/> + <requiredEntity createDataKey="simpleProduct3"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct4"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_2"/> + <requiredEntity createDataKey="simpleProduct4"/> + </createData> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductAllOptionTypesActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductAllOptionTypesActionGroup.xml new file mode 100644 index 0000000000000..c9f5c52c05736 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiDynamicBundleProductAllOptionTypesActionGroup.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateApiDynamicBundleProductAllOptionTypesActionGroup"> + <annotations> + <description>Creates 3 products with varying prices. Creates the dynamic bundle product with specified name. Adds the multiple select, checkbox options and links to the created products. Uses the 'ApiBundleProduct' entity.</description> + </annotations> + <arguments> + <argument name="productName" defaultValue="Api Dynamic Bundle Product" type="string"/> + </arguments> + + <!-- Create simple products --> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"> + <field key="price">10</field> + </createData> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"> + <field key="price">20</field> + </createData> + + <!-- Create Bundle product --> + <createData entity="ApiBundleProduct" stepKey="createBundleProduct"> + <field key="name">{{productName}}</field> + </createData> + <createData entity="DropDownBundleOption" stepKey="createDropDownBundleOption"> + <requiredEntity createDataKey="createBundleProduct"/> + <field key="title">Drop-down Option</field> + </createData> + <createData entity="RadioButtonsOption" stepKey="createBundleRadioButtonsOption"> + <requiredEntity createDataKey="createBundleProduct"/> + <field key="title">Radio Buttons Option</field> + </createData> + <createData entity="CheckboxOption" stepKey="createBundleCheckboxOption"> + <requiredEntity createDataKey="createBundleProduct"/> + <field key="title">Checkbox Option</field> + </createData> + <createData entity="ApiBundleLink" stepKey="linkCheckboxOptionToProduct1"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleCheckboxOption"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkCheckboxOptionToProduct2"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleCheckboxOption"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkDropDownOptionToProduct1"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createDropDownBundleOption"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkDropDownOptionToProduct2"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createDropDownBundleOption"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkRadioButtonsOptionToProduct1"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleRadioButtonsOption"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkRadioButtonsOptionToProduct2"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleRadioButtonsOption"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiFixedBundleProductActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiFixedBundleProductActionGroup.xml new file mode 100644 index 0000000000000..13c31cf2e7127 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminCreateApiFixedBundleProductActionGroup.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateApiFixedBundleProductActionGroup"> + <annotations> + <description>Creates 4 products with varying prices. Creates the bundle product with specified name. Adds the multiple select and checkbox options and 4 links to the created products. Uses the 'ApiFixedBundleProduct' entity.</description> + </annotations> + <arguments> + <argument name="productName" defaultValue="Api Fixed Bundle Product" type="string"/> + </arguments> + + <!--Create 4 simple products--> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"> + <field key="price">4.99</field> + </createData> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"> + <field key="price">2.89</field> + </createData> + <createData entity="SimpleProduct2" stepKey="simpleProduct3"> + <field key="price">7.33</field> + </createData> + <createData entity="SimpleProduct2" stepKey="simpleProduct4"> + <field key="price">18.25</field> + </createData> + + <!-- Create the bundle product based --> + <createData entity="ApiFixedBundleProduct" stepKey="createBundleProduct"> + <field key="name">{{productName}}</field> + </createData> + <createData entity="MultipleSelectOption" stepKey="createBundleOption1_1"> + <requiredEntity createDataKey="createBundleProduct"/> + <field key="required">false</field> + </createData> + <createData entity="CheckboxOption" stepKey="createBundleOption1_2"> + <requiredEntity createDataKey="createBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct2"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct3"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_2"/> + <requiredEntity createDataKey="simpleProduct3"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct4"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_2"/> + <requiredEntity createDataKey="simpleProduct4"/> + </createData> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AncillaryPrepBundleProductActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AncillaryPrepBundleProductActionGroup.xml new file mode 100644 index 0000000000000..99551742deca1 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AncillaryPrepBundleProductActionGroup.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="AncillaryPrepBundleProductActionGroup"> + <annotations> + <description>Requires Navigation to the Product Creation page. Fills out Name, Sku, and SEO information using the BundleProduct Data Entity. PLEASE NOTE: The Action Group values are Hardcoded.</description> + </annotations> + + <!--PreReq: go to bundle product creation page--> + <fillField selector="{{AdminProductFormBundleSection.productName}}" userInput="{{BundleProduct.name}}" stepKey="fillProductName"/> + <fillField selector="{{AdminProductFormBundleSection.productSku}}" userInput="{{BundleProduct.sku}}" stepKey="fillProductSku"/> + + <!--Trigger SEO drop down--> + <scrollTo selector="{{AdminProductFormBundleSection.seoDropdown}}" stepKey="moveToSEOSection"/> + <conditionalClick selector="{{AdminProductFormBundleSection.seoDropdown}}" dependentSelector="{{AdminProductFormBundleSection.urlKey}}" visible="false" stepKey="openDropDownIfClosed"/> + <waitForPageLoad stepKey="WaitForDropDownSEO"/> + + <!--Fill URL input--> + <fillField userInput="{{BundleProduct.urlKey}}" selector="{{AdminProductFormBundleSection.urlKey}}" stepKey="FillsinSEOlinkExtension"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/CheckRequiredFieldsInBundleProductFormActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/CheckRequiredFieldsInBundleProductFormActionGroup.xml new file mode 100644 index 0000000000000..9575349ba3110 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/CheckRequiredFieldsInBundleProductFormActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CheckRequiredFieldsInBundleProductFormActionGroup"> + <annotations> + <description>Clears the Name and SKU fields when adding a Product and then verifies that they are required after attempting to Save.</description> + </annotations> + + <clearField selector="{{AdminProductFormSection.productName}}" stepKey="clearProductSku"/> + <clearField selector="{{AdminProductFormSection.productSku}}" stepKey="clearProductName"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Product" stepKey="seeStillOnEditPage"/> + <see selector="{{AdminProductFormSection.fieldError('name')}}" userInput="This is a required field." stepKey="seeNameRequired"/> + <see selector="{{AdminProductFormSection.fieldError('sku')}}" userInput="This is a required field." stepKey="seeSkuRequired"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/CreateBasicBundleProductActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/CreateBasicBundleProductActionGroup.xml new file mode 100644 index 0000000000000..fd81b392db708 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/CreateBasicBundleProductActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateBasicBundleProductActionGroup"> + <annotations> + <description>Requires Navigation to the Product Creation page. Fills out Name, Sku, and SEO information using the BundleProduct Data Entity. PLEASE NOTE: The Action Group values are Hardcoded.</description> + </annotations> + + <!--PreReq: Go to bundle product creation page--> + <!--Product name and SKU--> + <fillField selector="{{AdminProductFormBundleSection.productName}}" userInput="{{BundleProduct.name}}" stepKey="fillProductName"/> + <fillField selector="{{AdminProductFormBundleSection.productSku}}" userInput="{{BundleProduct.sku}}" stepKey="fillProductSku"/> + + <!--Trigger SEO drop down--> + <scrollTo selector="{{AdminProductFormBundleSection.seoDropdown}}" stepKey="scrollToSeoDropDown"/> + <conditionalClick selector="{{AdminProductFormBundleSection.seoDropdown}}" dependentSelector="{{AdminProductFormBundleSection.urlKey}}" visible="false" stepKey="openDropDownIfClosed"/> + <waitForPageLoad stepKey="waitForDropDownSEO"/> + + <!--Fill URL input--> + <fillField userInput="{{BundleProduct.urlKey}}" selector="{{AdminProductFormBundleSection.urlKey}}" stepKey="fillsInSeoLinkExtension"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/CreateBundleProductActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/CreateBundleProductActionGroup.xml deleted file mode 100644 index 2a50c5141ad4e..0000000000000 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/CreateBundleProductActionGroup.xml +++ /dev/null @@ -1,149 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="CreateBasicBundleProduct"> - <annotations> - <description>Requires Navigation to the Product Creation page. Fills out Name, Sku, and SEO information using the BundleProduct Data Entity. PLEASE NOTE: The Action Group values are Hardcoded.</description> - </annotations> - - <!--PreReq: Go to bundle product creation page--> - <!--Product name and SKU--> - <fillField selector="{{AdminProductFormBundleSection.productName}}" userInput="{{BundleProduct.name}}" stepKey="fillProductName"/> - <fillField selector="{{AdminProductFormBundleSection.productSku}}" userInput="{{BundleProduct.sku}}" stepKey="fillProductSku"/> - - <!--Trigger SEO drop down--> - <scrollTo selector="{{AdminProductFormBundleSection.seoDropdown}}" stepKey="scrollToSeoDropDown"/> - <conditionalClick selector="{{AdminProductFormBundleSection.seoDropdown}}" dependentSelector="{{AdminProductFormBundleSection.urlKey}}" visible="false" stepKey="openDropDownIfClosed"/> - <waitForPageLoad stepKey="waitForDropDownSEO"/> - - <!--Fill URL input--> - <fillField userInput="{{BundleProduct.urlKey}}" selector="{{AdminProductFormBundleSection.urlKey}}" stepKey="fillsInSeoLinkExtension"/> - </actionGroup> - - <actionGroup name="addBundleOptionWithTwoProducts"> - <annotations> - <description>Requires Navigation to the Product Creation page. Adds Bundle Option with Two Products using the provided arguments. 'x' refers to Bundle option number. 'n' refers to the first number after x.</description> - </annotations> - <arguments> - <argument name="x" type="string"/> - <argument name="n" type="string"/> - <argument name="prodOneSku" type="string"/> - <argument name="prodTwoSku" type="string"/> - <argument name="optionTitle" type="string"/> - <argument name="inputType" type="string"/> - </arguments> - - <conditionalClick selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" dependentSelector="{{AdminProductFormBundleSection.bundleItemsToggle}}" visible="false" stepKey="conditionallyOpenSectionBundleItems"/> - <scrollTo selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" stepKey="scrollUpABit"/> - <click selector="{{AdminProductFormBundleSection.addOption}}" stepKey="clickAddOption"/> - <waitForElementVisible selector="{{AdminProductFormBundleSection.bundleOptionXTitle(x)}}" stepKey="waitForOptions"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXTitle(x)}}" userInput="{{optionTitle}}" stepKey="fillTitle"/> - <selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType(x)}}" userInput="{{inputType}}" stepKey="selectType"/> - <waitForElementVisible selector="{{AdminProductFormBundleSection.nthAddProductsToOption(n)}}" stepKey="waitForAddBtn"/> - <click selector="{{AdminProductFormBundleSection.nthAddProductsToOption(n)}}" stepKey="clickAdd"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters1"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters1"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{prodOneSku}}" stepKey="fillProductSkuFilter1"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters1"/> - <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad1" time="30"/> - <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectProduct1"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters2"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters2"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{prodTwoSku}}" stepKey="fillProductSkuFilter2"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters2"/> - <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad2" time="30"/> - <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectProduct2"/> - <click selector="{{AdminAddProductsToOptionPanel.addSelectedProducts}}" stepKey="clickAddButton1"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '0')}}" userInput="50" stepKey="fillQuantity1"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '1')}}" userInput="50" stepKey="fillQuantity2"/> - </actionGroup> - - <actionGroup name="addBundleOptionWithOneProduct" extends="addBundleOptionWithTwoProducts"> - <annotations> - <description>Requires Navigation to the Product Creation page. Adds Bundle Option with One Product as specified in arguments. 'x' refers to Bundle option number. 'n' refers to the first number after x.</description> - </annotations> - - <remove keyForRemoval="openProductFilters2"/> - <remove keyForRemoval="fillProductSkuFilter2"/> - <remove keyForRemoval="clickApplyFilters2"/> - <remove keyForRemoval="waitForFilteredGridLoad2"/> - <remove keyForRemoval="selectProduct2"/> - <remove keyForRemoval="selectProduct2"/> - <remove keyForRemoval="fillQuantity1"/> - <remove keyForRemoval="fillQuantity2"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '0')}}" userInput="1" stepKey="fillQuantity" after="clickAddButton1"/> - </actionGroup> - - <actionGroup name="addBundleOptionWithTreeProducts" extends="addBundleOptionWithTwoProducts"> - <annotations> - <description>Requires Navigation to the Product Creation page. Adds Bundle Option with Three Products using the provided arguments. 'x' refers to Bundle option number. 'n' refers to the first number after x.</description> - </annotations> - <arguments> - <argument name="prodTreeSku" type="string"/> - </arguments> - - <remove keyForRemoval="fillQuantity1"/> - <remove keyForRemoval="fillQuantity2"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters3" after="selectProduct2"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters3" after="clickClearFilters3"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{prodTreeSku}}" stepKey="fillProductSkuFilter3" after="openProductFilters3"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters3" after="fillProductSkuFilter3"/> - <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad3" time="30" after="clickApplyFilters3"/> - <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectProduct3" after="waitForFilteredGridLoad3"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '0')}}" userInput="1" stepKey="fillQuantity1" after="clickAddButton1"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '1')}}" userInput="1" stepKey="fillQuantity2" after="fillQuantity1"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '2')}}" userInput="1" stepKey="fillQuantity3" after="fillQuantity2"/> - </actionGroup> - - <actionGroup name="addBundleOptionWithSixProducts" extends="addBundleOptionWithTwoProducts"> - <annotations> - <description>Requires Navigation to Product Creation page. Adds Bundle Option with Six Products as specified in arguments. 'x' refers to Bundle option number. 'n' refers to the first number after x.</description> - </annotations> - <arguments> - <argument name="prodTreeSku" type="string"/> - <argument name="prodFourSku" type="string"/> - <argument name="prodFiveSku" type="string"/> - <argument name="prodSixSku" type="string"/> - </arguments> - - <remove keyForRemoval="fillQuantity1"/> - <remove keyForRemoval="fillQuantity2"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters3" after="selectProduct2"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters3" after="clickClearFilters3"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{prodTreeSku}}" stepKey="fillProductSkuFilter3" after="openProductFilters3"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters3" after="fillProductSkuFilter3"/> - <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad3" time="30" after="clickApplyFilters3"/> - <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectProduct3" after="waitForFilteredGridLoad3"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters4" after="selectProduct3"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters4" after="clickClearFilters4"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{prodFourSku}}" stepKey="fillProductSkuFilter4" after="openProductFilters4"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters4" after="fillProductSkuFilter4"/> - <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad4" time="30" after="clickApplyFilters4"/> - <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectProduct4" after="clickApplyFilters4"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters5" after="selectProduct4"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters5" after="clickClearFilters5"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{prodFiveSku}}" stepKey="fillProductSkuFilter5" after="openProductFilters5"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters5" after="fillProductSkuFilter5"/> - <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad5" time="30" after="clickApplyFilters5"/> - <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectProduct5" after="waitForFilteredGridLoad5"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters6" after="selectProduct5"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters6" after="clickClearFilters6"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{prodSixSku}}" stepKey="fillProductSkuFilter6" after="openProductFilters6"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters6" after="fillProductSkuFilter6"/> - <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad6" time="30" after="clickApplyFilters6"/> - <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectProduct6" after="waitForFilteredGridLoad6"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '0')}}" userInput="2" stepKey="fillQuantity1" after="clickAddButton1"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '1')}}" userInput="2" stepKey="fillQuantity2" after="fillQuantity1"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '2')}}" userInput="2" stepKey="fillQuantity3" after="fillQuantity2"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '3')}}" userInput="2" stepKey="fillQuantity4" after="fillQuantity3"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '4')}}" userInput="2" stepKey="fillQuantity5" after="fillQuantity4"/> - <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '5')}}" userInput="2" stepKey="fillQuantity6" after="fillQuantity5"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/DeleteBundleOptionByIndexActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/DeleteBundleOptionByIndexActionGroup.xml new file mode 100644 index 0000000000000..52a4897f8e354 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/DeleteBundleOptionByIndexActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteBundleOptionByIndexActionGroup"> + <annotations> + <description>Requires Navigation to Product Creation page. Removes any Bundle Option by index specified in arguments. 'deleteIndex' refers to Bundle option number.</description> + </annotations> + <arguments> + <argument name="deleteIndex" type="string"/> + </arguments> + + <conditionalClick selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" dependentSelector="{{AdminProductFormBundleSection.bundleItemsToggle}}" visible="false" stepKey="conditionallyOpenSectionBundleItems"/> + <scrollTo selector="{{AdminProductFormBundleSection.bundleItemsToggle}}" stepKey="scrollUpABit"/> + <click selector="{{AdminProductFormBundleSection.deleteOption(deleteIndex)}}" stepKey="clickDeleteOption"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/EnableDisableProductActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/EnableDisableProductActionGroup.xml deleted file mode 100644 index 92d885485949c..0000000000000 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/EnableDisableProductActionGroup.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AncillaryPrepBundleProduct"> - <annotations> - <description>Requires Navigation to the Product Creation page. Fills out Name, Sku, and SEO information using the BundleProduct Data Entity. PLEASE NOTE: The Action Group values are Hardcoded.</description> - </annotations> - - <!--PreReq: go to bundle product creation page--> - <fillField selector="{{AdminProductFormBundleSection.productName}}" userInput="{{BundleProduct.name}}" stepKey="fillProductName"/> - <fillField selector="{{AdminProductFormBundleSection.productSku}}" userInput="{{BundleProduct.sku}}" stepKey="fillProductSku"/> - - <!--Trigger SEO drop down--> - <scrollTo selector="{{AdminProductFormBundleSection.seoDropdown}}" stepKey="moveToSEOSection"/> - <conditionalClick selector="{{AdminProductFormBundleSection.seoDropdown}}" dependentSelector="{{AdminProductFormBundleSection.urlKey}}" visible="false" stepKey="openDropDownIfClosed"/> - <waitForPageLoad stepKey="WaitForDropDownSEO"/> - - <!--Fill URL input--> - <fillField userInput="{{BundleProduct.urlKey}}" selector="{{AdminProductFormBundleSection.urlKey}}" stepKey="FillsinSEOlinkExtension"/> - </actionGroup> - - <!--Edit existing product by searching in product catalog--> - <actionGroup name="FindProductToEdit"> - <annotations> - <description>Clears the Backend Admin Grid Filters on the Backend Admin Product Grid page. Searches for the BundleProduct Data Entity. Then clicks on the first item in the Admin Grid. PLEASE NOTE: The Action Group values are Hardcoded.</description> - </annotations> - - <amOnPage url="{{ProductCatalogPage.url}}" stepKey="GoToProductCatalog"/> - <waitForPageLoad stepKey="WaitForCatalogProductPageToLoad"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <fillField userInput="{{BundleProduct.name}}" selector="#fulltext" stepKey="EnterProductNameInSearch"/> - <click stepKey="ClickSearch" selector="{{AdminProductFormBundleSection.searchButton}}"/> - <click stepKey="ClickOnProduct" selector="{{AdminProductFormBundleSection.firstCatalogProduct}}"/> - <waitForPageLoad stepKey="WaitForProductEditPageToLoad"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/FillMainBundleProductFormActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/FillMainBundleProductFormActionGroup.xml new file mode 100644 index 0000000000000..71baf0c450759 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/FillMainBundleProductFormActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FillMainBundleProductFormActionGroup"> + <annotations> + <description>Fills the Name, SKU and Stock Status fields.</description> + </annotations> + <arguments> + <argument name="product" defaultValue="BundleProduct"/> + </arguments> + + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{product.name}}" stepKey="fillProductSku"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{product.sku}}" stepKey="fillProductName"/> + <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{product.status}}" stepKey="selectStockStatus"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/FindProductToEditActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/FindProductToEditActionGroup.xml new file mode 100644 index 0000000000000..bd6dbb79489b3 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/FindProductToEditActionGroup.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="FindProductToEditActionGroup"> + <annotations> + <description>Clears the Backend Admin Grid Filters on the Backend Admin Product Grid page. Searches for the BundleProduct Data Entity. Then clicks on the first item in the Admin Grid. PLEASE NOTE: The Action Group values are Hardcoded.</description> + </annotations> + + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="GoToProductCatalog"/> + <waitForPageLoad stepKey="WaitForCatalogProductPageToLoad"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <fillField userInput="{{BundleProduct.name}}" selector="#fulltext" stepKey="EnterProductNameInSearch"/> + <click stepKey="ClickSearch" selector="{{AdminProductFormBundleSection.searchButton}}"/> + <click stepKey="ClickOnProduct" selector="{{AdminProductFormBundleSection.firstCatalogProduct}}"/> + <waitForPageLoad stepKey="WaitForProductEditPageToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontAddBundleProductFromCategoryToCartActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontAddBundleProductFromCategoryToCartActionGroup.xml new file mode 100644 index 0000000000000..ccfa356881594 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontAddBundleProductFromCategoryToCartActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAddBundleProductFromCategoryToCartActionGroup"> + <annotations> + <description>Adds a Bundled Product to the Cart from the Product page. PLEASE NOTE: The Quantity selection is not available in the Action Group.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + </arguments> + + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductTitleByName(productName)}}" stepKey="moveMouseOverProduct"/> + <click selector="{{StorefrontCategoryProductSection.ProductTitleByName(productName)}}" stepKey="openProductPage"/> + <waitForPageLoad time="30" stepKey="waitForBundleProductPageLoad"/> + <click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomizeAndAddToCart"/> + <click selector="{{StorefrontBundledSection.addToCartConfigured}}" stepKey="clickAddBundleProductToCart"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.productCount}}" stepKey="waitProductCount"/> + <see userInput="You added {{productName}} to your shopping cart." selector="{{StorefrontMessagesSection.success}}" stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontAddBundleProductFromProductToCartActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontAddBundleProductFromProductToCartActionGroup.xml new file mode 100644 index 0000000000000..cf4cfa4659264 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontAddBundleProductFromProductToCartActionGroup.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="StorefrontAddBundleProductFromProductToCartActionGroup"> + <annotations> + <description>Adds a Bundled Product to the Cart from the Product page. PLEASE NOTE: The Quantity selection is not available in the Action Group.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + </arguments> + + <click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomizeAndAddToCart"/> + <click selector="{{StorefrontBundledSection.addToCartConfigured}}" stepKey="clickAddBundleProductToCart"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.productCount}}" stepKey="waitProductCount"/> + <see userInput="You added {{productName}} to your shopping cart." selector="{{StorefrontMessagesSection.success}}" stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontAddBundleProductFromProductToCartWithMultiOptionActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontAddBundleProductFromProductToCartWithMultiOptionActionGroup.xml new file mode 100644 index 0000000000000..49adf670b4c98 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontAddBundleProductFromProductToCartWithMultiOptionActionGroup.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="StorefrontAddBundleProductFromProductToCartWithMultiOptionActionGroup" extends="StorefrontAddBundleProductFromProductToCartActionGroup"> + <annotations> + <description>Selects a Bundled Product option on the Bundled Product page. PLEASE NOTE: The Quantity selection is not available in the Action Group.</description> + </annotations> + <arguments> + <argument name="optionName" type="string"/> + <argument name="value" type="string"/> + </arguments> + + <selectOption selector="{{StorefrontBundledSection.multiselectOptionFourProducts(optionName)}}" userInput="{{value}}" stepKey="selectValue" before="clickAddBundleProductToCart"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontAddCategoryBundleProductToCartActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontAddCategoryBundleProductToCartActionGroup.xml new file mode 100644 index 0000000000000..bf0e7068fbbde --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontAddCategoryBundleProductToCartActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAddCategoryBundleProductToCartActionGroup"> + <annotations> + <description>Adds a Bundled Product to the Cart from the Category page.</description> + </annotations> + <arguments> + <argument name="product"/> + <argument name="quantity" defaultValue="1" type="string"/> + </arguments> + + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(product.name)}}" stepKey="moveMouseOverProduct"/> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(product.name)}}" stepKey="clickAddToCart"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> + <click selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="clickCustomizeAndAddToCart"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad2"/> + <fillField selector="{{StorefrontBundleProductActionSection.quantityField}}" userInput="{{quantity}}" stepKey="fillBundleProductQuantity"/> + <click selector="{{StorefrontBundleProductActionSection.addToCartButton}}" stepKey="clickAddBundleProductToCart"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad3"/> + <waitForText userInput="{{quantity}}" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml deleted file mode 100644 index b260068dedf7c..0000000000000 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml +++ /dev/null @@ -1,77 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!-- Add Bundle Product to Cart from the category page with specified quantity to cart --> - <actionGroup name="StorefrontAddCategoryBundleProductToCartActionGroup"> - <annotations> - <description>Adds a Bundled Product to the Cart from the Category page.</description> - </annotations> - <arguments> - <argument name="product"/> - <argument name="quantity" defaultValue="1" type="string"/> - </arguments> - - <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(product.name)}}" stepKey="moveMouseOverProduct"/> - <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(product.name)}}" stepKey="clickAddToCart"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> - <click selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="clickCustomizeAndAddToCart"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad2"/> - <fillField selector="{{StorefrontBundleProductActionSection.quantityField}}" userInput="{{quantity}}" stepKey="fillBundleProductQuantity"/> - <click selector="{{StorefrontBundleProductActionSection.addToCartButton}}" stepKey="clickAddBundleProductToCart"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad3"/> - <waitForText userInput="{{quantity}}" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> - </actionGroup> - - <!-- Add Bundle Product to Cart from the category page --> - <actionGroup name="StorefrontAddBundleProductFromCategoryToCartActionGroup"> - <annotations> - <description>Adds a Bundled Product to the Cart from the Product page. PLEASE NOTE: The Quantity selection is not available in the Action Group.</description> - </annotations> - <arguments> - <argument name="productName" type="string"/> - </arguments> - - <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductTitleByName(productName)}}" stepKey="moveMouseOverProduct"/> - <click selector="{{StorefrontCategoryProductSection.ProductTitleByName(productName)}}" stepKey="openProductPage"/> - <waitForPageLoad time="30" stepKey="waitForBundleProductPageLoad"/> - <click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomizeAndAddToCart"/> - <click selector="{{StorefrontBundledSection.addToCartConfigured}}" stepKey="clickAddBundleProductToCart"/> - <waitForElementVisible selector="{{StorefrontMinicartSection.productCount}}" stepKey="waitProductCount"/> - <see userInput="You added {{productName}} to your shopping cart." selector="{{StorefrontMessagesSection.success}}" stepKey="seeSuccessMessage"/> - </actionGroup> - - <!-- Add Bundle Product to Cart from product Page--> - <actionGroup name="StorefrontAddBundleProductFromProductToCartActionGroup"> - <annotations> - <description>Adds a Bundled Product to the Cart from the Product page. PLEASE NOTE: The Quantity selection is not available in the Action Group.</description> - </annotations> - <arguments> - <argument name="productName" type="string"/> - </arguments> - - <click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomizeAndAddToCart"/> - <click selector="{{StorefrontBundledSection.addToCartConfigured}}" stepKey="clickAddBundleProductToCart"/> - <waitForElementVisible selector="{{StorefrontMinicartSection.productCount}}" stepKey="waitProductCount"/> - <see userInput="You added {{productName}} to your shopping cart." selector="{{StorefrontMessagesSection.success}}" stepKey="seeSuccessMessage"/> - </actionGroup> - - <!-- Add Bundled Product to Cart with selected multiselect option--> - <actionGroup name="StorefrontAddBundleProductFromProductToCartWithMultiOption" extends="StorefrontAddBundleProductFromProductToCartActionGroup"> - <annotations> - <description>Selects a Bundled Product option on the Bundled Product page. PLEASE NOTE: The Quantity selection is not available in the Action Group.</description> - </annotations> - <arguments> - <argument name="optionName" type="string"/> - <argument name="value" type="string"/> - </arguments> - - <selectOption selector="{{StorefrontBundledSection.multiselectOptionFourProducts(optionName)}}" userInput="{{value}}" stepKey="selectValue" before="clickAddBundleProductToCart"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/ViewBundleProductInAdminGridActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/ViewBundleProductInAdminGridActionGroup.xml new file mode 100644 index 0000000000000..bd0d08e4c0924 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/ViewBundleProductInAdminGridActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ViewBundleProductInAdminGridActionGroup"> + <annotations> + <description>Clears the Grid Filters on the Catalog Grid page and applies Filter by Name and Sku. Then checks to see if the Product exists in the 1st row. Then clears the Grid Filters again for future Tests.</description> + </annotations> + <arguments> + <argument name="product" defaultValue="BundleProduct"/> + <argument name="thumbnail" defaultValue="ProductPlaceholderImage"/> + </arguments> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> + <waitForPageLoad stepKey="waitForPageLoadInitial"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{product.name}}" stepKey="fillProductNameFilter"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillProductSkuFilter"/> + <selectOption selector="{{AdminProductGridFilterSection.typeFilter}}" userInput="{{product.type_id}}" stepKey="selectionProductType"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <see selector="{{AdminProductGridSection.firstProductRow}}" userInput="{{product.name}}" stepKey="seeProductNameInGrid"/> + <click selector="{{AdminProductGridFilterSection.clearFilters}}" stepKey="clickClearFiltersAfter"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml index bd13f4daa0dbd..967cf5ac49ed5 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Section/AdminProductFormBundleSection.xml @@ -46,6 +46,10 @@ <element name="currentBundleOption" type="text" selector="//div[@data-index='bundle-items']//div[contains(@class, 'admin__collapsible-title')]/span"/> <!--AddingAnOption--> <element name="addOptions" type="button" selector="//tr[@data-repeat-index='0']//td[4]" timeout="30"/> + <!--DragAnOption --> + <element name="dragOption" type="block" selector="//tr[{{dragIndex}}]//div[contains(@class, 'draggable-handle')]" timeout="30" parameterized="true"/> + <!--DeleteAnOption --> + <element name="deleteOption" type="button" selector="//tr[{{deleteIndex}}]//button[@data-index='delete_button']" timeout="30" parameterized="true"/> <!--SEODropdownTab--> <element name="seoDropdown" type="button" selector="//div[@data-index='search-engine-optimization']"/> <element name="seoDependent" type="button" selector="//div[@data-index='search-engine-optimization']//div[contains(@class, '_show')]"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml index 401d360a34c64..bf428440a24eb 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleItemsTest.xml @@ -51,11 +51,11 @@ <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> <argument name="product" value="$$simpleProduct0$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> @@ -64,7 +64,7 @@ <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty2"/> <!--Fill out ancillary data on bundle product--> - <actionGroup ref="AncillaryPrepBundleProduct" stepKey="createBundledProductForTwoSimpleProducts"> + <actionGroup ref="AncillaryPrepBundleProductActionGroup" stepKey="createBundledProductForTwoSimpleProducts"> <argument name="bundleProduct" value="BundleProduct"/> </actionGroup> @@ -91,7 +91,7 @@ <waitForPageLoad stepKey="WaitForPageToLoad"/> <conditionalClick selector="{{AdminProductFiltersSection.filtersClear}}" dependentSelector="{{AdminProductFiltersSection.filtersClear}}" visible="true" stepKey="ClickOnButtonToRemoveFiltersIfPresent"/> <waitForPageLoad stepKey="WaitForClear"/> - <actionGroup ref="filterProductGridByName" stepKey="filterBundleProductOptionsDownToName"> + <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="filterBundleProductOptionsDownToName"> <argument name="product" value="BundleProduct"/> </actionGroup> <click selector="{{AdminProductFormBundleSection.addOptions}}" stepKey="clickOnBundleProductToEdit"/> @@ -103,11 +103,11 @@ <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToNewBundle"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToNewOption"/> <waitForPageLoad stepKey="waitForPageLoadAfterNewBundleProducts"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterNewBundleProductOptions"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterNewBundleProductOptions"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <checkOption selector="//div[@class='admin__data-grid-outer-wrap']//tr[@data-repeat-index='0']//input[@type='checkbox']" stepKey="selectNewFirstGridRow"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterNewBundleProductOptions2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterNewBundleProductOptions2"> <argument name="product" value="$$simpleProduct3$$"/> </actionGroup> <checkOption selector="{{AdminProductFormBundleSection.firstProductOption}}" stepKey="selectNewFirstGridRow2"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleProductToCartFromWishListPageTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleProductToCartFromWishListPageTest.xml index 2a4b119a5cabc..b2d3c376d9b5a 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleProductToCartFromWishListPageTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddBundleProductToCartFromWishListPageTest.xml @@ -51,7 +51,7 @@ <requiredEntity createDataKey="createSimpleProduct2"/> </createData> <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="goToProductEditPage"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> </before> <after> <!-- Delete created data --> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml index 21e6be98b3169..3770e47079c98 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultImageBundleProductTest.xml @@ -32,11 +32,11 @@ <!-- Create a bundle product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPageBundle"/> <waitForPageLoad stepKey="waitForProductPageLoadBundle"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateBundleProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateBundleProduct"> <argument name="product" value="BundleProduct"/> </actionGroup> - <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillBundleProductNameAndSku"> + <actionGroup ref="FillProductNameAndSkuInProductFormActionGroup" stepKey="fillBundleProductNameAndSku"> <argument name="product" value="BundleProduct"/> </actionGroup> @@ -51,11 +51,11 @@ <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> @@ -64,23 +64,23 @@ <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty2"/> <!-- Add image to product --> - <actionGroup ref="addProductImage" stepKey="addImageForProduct"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImageForProduct"> <argument name="image" value="MagentoLogo"/> </actionGroup> <!--Save product--> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Assert product image in admin product form --> - <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertProductImageAdminProductPage"/> + <actionGroup ref="AssertProductImageAdminProductPageActionGroup" stepKey="assertProductImageAdminProductPage"/> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="BundleProduct"/> </actionGroup> <!-- Assert product image in storefront product page --> - <actionGroup ref="assertProductImageStorefrontProductPage" stepKey="assertProductImageStorefrontProductPage"> + <actionGroup ref="AssertProductImageStorefrontProductPageActionGroup" stepKey="assertProductImageStorefrontProductPage"> <argument name="product" value="BundleProduct"/> <argument name="image" value="MagentoLogo"/> </actionGroup> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml index c49202f31aefb..66443e130ed08 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAddDefaultVideoBundleProductTest.xml @@ -29,10 +29,10 @@ <!-- Create a bundle product --> <!-- Replacing steps in base AdminAddDefaultVideoSimpleProductTest --> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProductPage"> <argument name="product" value="BundleProduct"/> </actionGroup> - <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillMainProductForm"> + <actionGroup ref="FillProductNameAndSkuInProductFormActionGroup" stepKey="fillMainProductForm"> <argument name="product" value="BundleProduct"/> </actionGroup> @@ -46,11 +46,11 @@ <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProducts" after="selectOptionBundleTitle"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProducts" after="waitForAddProducts"/> <waitForPageLoad stepKey="waitForPageLoad" after="clickAddProducts"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku1" after="waitForPageLoad"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku1" after="waitForPageLoad"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="checkOption1" after="filterProductGridBySku1"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku2" after="checkOption1"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku2" after="checkOption1"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="checkOption2" after="filterProductGridBySku2"/> @@ -59,7 +59,7 @@ <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillQty2" before="saveProductForm"/> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="BundleProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAssociateBundleProductToWebsitesTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAssociateBundleProductToWebsitesTest.xml index 505a319c5c44f..b58637cf2e81d 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAssociateBundleProductToWebsitesTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAssociateBundleProductToWebsitesTest.xml @@ -79,14 +79,14 @@ <argument name="websiteName" value="{{secondCustomWebsite.name}}"/> </actionGroup> - <actionGroup ref="NavigateToAndResetProductGridToDefaultView" stepKey="resetProductGridFilter"/> + <actionGroup ref="NavigateToAndResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridFilter"/> <!-- Admin logout --> <actionGroup ref="logout" stepKey="adminLogout"/> </after> <!-- Open product page and assign grouped project to second website --> - <actionGroup ref="filterAndSelectProduct" stepKey="openAdminProductPage"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="openAdminProductPage"> <argument name="productSku" value="$$createBundleProduct.sku$$"/> </actionGroup> <actionGroup ref="AdminAssignProductInWebsiteActionGroup" stepKey="assignProductToSecondWebsite"> @@ -95,15 +95,15 @@ <actionGroup ref="AdminUnassignProductInWebsiteActionGroup" stepKey="unassignProductFromDefaultWebsite"> <argument name="website" value="{{_defaultWebsite.name}}"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveGroupedProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveGroupedProduct"/> <!-- Assert product is assigned to Second website --> - <actionGroup ref="AssertProductIsAssignedToWebsite" stepKey="seeCustomWebsiteIsChecked"> + <actionGroup ref="AssertProductIsAssignedToWebsiteActionGroup" stepKey="seeCustomWebsiteIsChecked"> <argument name="website" value="{{secondCustomWebsite.name}}"/> </actionGroup> <!-- Assert product is not assigned to Main website --> - <actionGroup ref="AssertProductIsNotAssignedToWebsite" stepKey="seeMainWebsiteIsNotChecked"> + <actionGroup ref="AssertProductIsNotAssignedToWebsiteActionGroup" stepKey="seeMainWebsiteIsNotChecked"> <argument name="website" value="{{_defaultWebsite.name}}"/> </actionGroup> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAttributeSetSelectionTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAttributeSetSelectionTest.xml index 1d2f21b7d15f9..c8977cbae1957 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminAttributeSetSelectionTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminAttributeSetSelectionTest.xml @@ -56,7 +56,7 @@ <!--Set filter to product name--> <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="GoToCatalogProductPage"/> <waitForPageLoad stepKey="WaitForPageToLoad"/> - <actionGroup ref="filterProductGridByName" stepKey="filterBundleProductOptionsDownToName"> + <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="filterBundleProductOptionsDownToName"> <argument name="product" value="BundleProduct"/> </actionGroup> <seeElement selector="{{AdminProductFiltersSection.attributeSetOfFirstRow(ProductAttributeFrontendLabel.label)}}" stepKey="seeAttributeSet"/> @@ -77,7 +77,7 @@ <!--Set filter to product name--> <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="GoToCatalogProductPage2"/> <waitForPageLoad stepKey="WaitForPageToLoad2"/> - <actionGroup ref="filterProductGridByName" stepKey="filterBundleProductOptionsDownToName2"> + <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="filterBundleProductOptionsDownToName2"> <argument name="product" value="BundleProduct"/> </actionGroup> <seeElement selector="{{AdminProductFiltersSection.attributeSetOfFirstRow(BundleProduct.defaultAttribute)}}" stepKey="seeAttributeSet2"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml index c6a07f7ed95c3..fcfd80ac8533c 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBasicBundleProductAttributesTest.xml @@ -24,7 +24,7 @@ <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> </after> <!--Create attribute set--> - <actionGroup ref="CreateDefaultAttributeSet" stepKey="createDefaultAttributeSet"> + <actionGroup ref="CreateDefaultAttributeSetActionGroup" stepKey="createDefaultAttributeSet"> <argument name="label" value="{{ProductAttributeFrontendLabel.label}}"/> </actionGroup> @@ -91,14 +91,14 @@ <seeOptionIsSelected selector="{{AdminProductFormBundleSection.countryOfManufactureDropDown}}" userInput="Italy" stepKey="seeCountryOfManufacture"/> <!--Create second attribute set for edit--> - <actionGroup ref="CreateDefaultAttributeSet" stepKey="createSecondAttributeSet"> + <actionGroup ref="CreateDefaultAttributeSetActionGroup" stepKey="createSecondAttributeSet"> <argument name="label" value="{{ProductAttributeFrontendLabelTwo.label}}"/> </actionGroup> <!--Filter catalog--> <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPage"/> <waitForPageLoad stepKey="WaitForPageToLoad"/> - <actionGroup ref="filterProductGridByName" stepKey="filterBundleProductOptionsDownToName"> + <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="filterBundleProductOptionsDownToName"> <argument name="product" value="BundleProduct"/> </actionGroup> <click selector="{{AdminProductFiltersSection.attributeSetOfFirstRow(ProductAttributeFrontendLabel.label)}}" stepKey="clickAttributeSet2"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBundleProductSetEditContentTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBundleProductSetEditContentTest.xml index 65733a5bcc037..788dc4a848fd3 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminBundleProductSetEditContentTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminBundleProductSetEditContentTest.xml @@ -21,16 +21,16 @@ </annotations> <after> <!-- Delete bundle product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="BundleProduct"/> </actionGroup> </after> <!-- Create product --> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="BundleProduct"/> </actionGroup> - <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillProductNameAndSkuInProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="BundleProduct"/> </actionGroup> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml index f7a64f943f307..a80d5f040f825 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminCreateAndEditBundleProductSettingsTest.xml @@ -32,20 +32,25 @@ <!-- Delete the simple product --> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <!-- Delete a Website --> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> + <argument name="websiteName" value="Second Website"/> + </actionGroup> + <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> </after> <!-- Create new bundle product --> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="createBundleProduct"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createBundleProduct"> <argument name="productType" value="bundle"/> </actionGroup> <!-- Fill all main fields --> - <actionGroup ref="fillMainBundleProductForm" stepKey="fillMainProductFields"/> + <actionGroup ref="FillMainBundleProductFormActionGroup" stepKey="fillMainProductFields"/> <!-- Add the bundle option to the product --> - <actionGroup ref="addBundleOptionWithOneProduct" stepKey="addBundleOption"> + <actionGroup ref="AddBundleOptionWithOneProductActionGroup" stepKey="addBundleOption"> <argument name="x" value="0"/> <argument name="n" value="1"/> <argument name="prodOneSku" value="$$createSimpleProduct.sku$$"/> @@ -70,7 +75,7 @@ </actionGroup> <!-- Save product form --> - <actionGroup ref="saveProductForm" stepKey="clickSaveButton"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveButton"/> <!-- Open product page --> <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openStorefrontProductPage"> @@ -96,7 +101,7 @@ </actionGroup> <!-- Assert product in assigned to Website --> - <actionGroup ref="AssertProductIsAssignedToWebsite" stepKey="seeCustomWebsiteIsChecked"> + <actionGroup ref="AssertProductIsAssignedToWebsiteActionGroup" stepKey="seeCustomWebsiteIsChecked"> <argument name="website" value="$createWebsite.website[name]$"/> </actionGroup> @@ -117,7 +122,7 @@ <actionGroup ref="AdminSwitchProductGiftMessageStatusActionGroup" stepKey="disableGiftMessageSettings"/> <!-- Save product form --> - <actionGroup ref="saveProductForm" stepKey="clickSaveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveProduct"/> <!-- Verify Url Key after changing --> <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openProductPage"> @@ -132,13 +137,119 @@ <dontSeeElement selector="{{StorefrontProductCartGiftOptionSection.giftOptions}}" stepKey="dontSeeGiftOptionBtn"/> <!-- Delete created bundle product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="BundleProduct"/> </actionGroup> + </test> + <test name="AdminCreateAndEditBundleProductOptionsNegativeTest"> + <annotations> + <features value="Bundle"/> + <stories value="Modify bundle product in Admin"/> + <title value="Admin should be able to remove any bundle option a bundle product"/> + <description value="Admin should be able to set/edit other product information when creating/editing a bundle product"/> + <severity value="MAJOR"/> + <testCaseId value="MC-224"/> + <skip> + <issueId value="https://github.com/magento/magento2/issues/25468"/> + </skip> + <group value="Catalog"/> + </annotations> + <before> + <!-- Create a Website --> + <createData entity="customWebsite" stepKey="createWebsite"/> + + <!-- Create first simple product for a bundle option --> + <createData entity="SimpleProduct2" stepKey="createFirstSimpleProduct"/> + + <!-- Create second simple product for a bundle option --> + <createData entity="SimpleProduct2" stepKey="createSecondSimpleProduct"/> - <!-- Delete created Website --> - <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> - <argument name="websiteName" value="$createWebsite.website[name]$"/> + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!-- Delete the simple product --> + <deleteData createDataKey="createFirstSimpleProduct" stepKey="deleteFirstSimpleProduct"/> + + <!-- Delete the simple product --> + <deleteData createDataKey="createSecondSimpleProduct" stepKey="deleteSecondSimpleProduct"/> + + <!-- Delete a Website --> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> + <argument name="websiteName" value="Second Website"/> + </actionGroup> + + <!-- Log out --> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Create new bundle product --> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createBundleProduct"> + <argument name="productType" value="bundle"/> + </actionGroup> + + <!-- Fill all main fields --> + <actionGroup ref="FillMainBundleProductFormActionGroup" stepKey="fillMainProductFields"/> + + <!-- Add first bundle option to the product --> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addFirstBundleOption"> + <argument name="x" value="0"/> + <argument name="n" value="1"/> + <argument name="prodOneSku" value="$$createFirstSimpleProduct.sku$$"/> + <argument name="prodTwoSku" value="$$createSecondSimpleProduct.sku$$"/> + <argument name="optionTitle" value="{{RadioButtonsOption.title}}"/> + <argument name="inputType" value="{{RadioButtonsOption.type}}"/> + </actionGroup> + + <!-- Add second bundle option to the product --> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addSecondBundleOption"> + <argument name="x" value="1"/> + <argument name="n" value="2"/> + <argument name="prodOneSku" value="$$createFirstSimpleProduct.sku$$"/> + <argument name="prodTwoSku" value="$$createSecondSimpleProduct.sku$$"/> + <argument name="optionTitle" value="{{CheckboxOption.title}}"/> + <argument name="inputType" value="{{CheckboxOption.type}}"/> + </actionGroup> + + <!-- Add third bundle option to the product --> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addThirdBundleOption"> + <argument name="x" value="2"/> + <argument name="n" value="3"/> + <argument name="prodOneSku" value="$$createFirstSimpleProduct.sku$$"/> + <argument name="prodTwoSku" value="$$createSecondSimpleProduct.sku$$"/> + <argument name="optionTitle" value="{{RadioButtonsOption.title}}"/> + <argument name="inputType" value="{{RadioButtonsOption.type}}"/> + </actionGroup> + + <!-- Set product in created Website --> + <actionGroup ref="AdminAssignProductInWebsiteActionGroup" stepKey="selectProductInWebsites"> + <argument name="website" value="$createWebsite.website[name]$"/> + </actionGroup> + + <!-- Save product form --> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveWithThreeOptions"/> + + <!-- Open created product --> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + + <!-- Remove second option --> + <actionGroup ref="DeleteBundleOptionByIndexActionGroup" stepKey="deleteSecondOption"> + <argument name="deleteIndex" value="1"/> + </actionGroup> + + <!-- Save product form --> + <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveProduct"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveWithTwoOptions"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="messageYouSavedTheProductIsShown"/> + + <!-- Delete created bundle product --> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> + <argument name="product" value="BundleProduct"/> </actionGroup> </test> </tests> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProduct.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProduct.xml index 86db6f372b5f8..f272f3f98a8c9 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProduct.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProduct.xml @@ -40,11 +40,11 @@ <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> @@ -53,7 +53,7 @@ <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty2"/> <!--Fill out ancillary data on bundle product--> - <actionGroup ref="AncillaryPrepBundleProduct" stepKey="createBundledProductForTwoSimpleProducts"/> + <actionGroup ref="AncillaryPrepBundleProductActionGroup" stepKey="createBundledProductForTwoSimpleProducts"/> <!--Save the product--> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> @@ -64,7 +64,7 @@ <waitForPageLoad stepKey="Loading"/> <!--Apply Name Filter--> - <actionGroup ref="filterProductGridByName" stepKey="filterBundleProductOptionsDownToName"> + <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="filterBundleProductOptionsDownToName"> <argument name="product" value="BundleProduct"/> </actionGroup> <click selector="{{AdminProductFiltersSection.allCheckbox}}" stepKey="SelectAllOnly1"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml index a4e26256e9773..51821b136ba26 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml @@ -31,7 +31,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteBundleProductFilteredBySkuAndName"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteBundleProductFilteredBySkuAndName"> <argument name="product" value="$$createDynamicBundleProduct$$"/> </actionGroup> <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="A total of 1 record(s) have been deleted." stepKey="deleteMessage"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleFixedProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleFixedProductTest.xml index 2527dae7eadf8..dcd53fff6f6dd 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleFixedProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleFixedProductTest.xml @@ -28,7 +28,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteBundleProductFilteredBySkuAndName"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteBundleProductFilteredBySkuAndName"> <argument name="product" value="$$createFixedBundleProduct$$"/> </actionGroup> <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="A total of 1 record(s) have been deleted." stepKey="deleteMessage"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml index 08faa9d2444df..632ba194cf8de 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminEditRelatedBundleProductTest.xml @@ -26,7 +26,7 @@ </before> <after> <!-- Delete the bundled product --> - <actionGroup stepKey="deleteBundle" ref="deleteProductUsingProductGrid"> + <actionGroup stepKey="deleteBundle" ref="DeleteProductUsingProductGridActionGroup"> <argument name="product" value="BundleProduct"/> </actionGroup> <!--Logging out--> @@ -38,15 +38,15 @@ <!-- Create a bundle product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPageBundle"/> <waitForPageLoad stepKey="waitForProductPageLoadBundle"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateBundleProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateBundleProduct"> <argument name="product" value="BundleProduct"/> </actionGroup> - <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillBundleProductNameAndSku"> + <actionGroup ref="FillProductNameAndSkuInProductFormActionGroup" stepKey="fillBundleProductNameAndSku"> <argument name="product" value="BundleProduct"/> </actionGroup> - <actionGroup ref="addRelatedProductBySku" stepKey="addRelatedProduct0"> + <actionGroup ref="AddRelatedProductBySkuActionGroup" stepKey="addRelatedProduct0"> <argument name="sku" value="$$simpleProduct0.sku$$"/> </actionGroup> @@ -54,7 +54,7 @@ <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="messageYouSavedTheProductIsShown"/> - <actionGroup ref="addRelatedProductBySku" stepKey="addRelatedProduct1"> + <actionGroup ref="AddRelatedProductBySkuActionGroup" stepKey="addRelatedProduct1"> <argument name="sku" value="$$simpleProduct1.sku$$"/> </actionGroup> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProduct.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProduct.xml index 40a6e1b75c60a..5aa72fb651985 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProduct.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminFilterProductListByBundleProduct.xml @@ -40,11 +40,11 @@ <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> @@ -53,7 +53,7 @@ <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty2"/> <!--Fill out ancillary data on bundle product--> - <actionGroup ref="AncillaryPrepBundleProduct" stepKey="createBundledProductForTwoSimpleProducts"/> + <actionGroup ref="AncillaryPrepBundleProductActionGroup" stepKey="createBundledProductForTwoSimpleProducts"/> <!--Save the product--> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml index 2f891fcc8f169..28abd06253393 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml @@ -48,11 +48,11 @@ <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> @@ -61,7 +61,7 @@ <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty2"/> <!--Fill out ancillary data on bundle product--> - <actionGroup ref="AncillaryPrepBundleProduct" stepKey="createBundledProductForTwoSimpleProducts"> + <actionGroup ref="AncillaryPrepBundleProductActionGroup" stepKey="createBundledProductForTwoSimpleProducts"> <argument name="bundleProduct" value="BundleProduct"/> </actionGroup> @@ -83,11 +83,11 @@ <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle2"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption2"/> <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts2"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptionsx2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptionsx2"> <argument name="product" value="$$simpleProduct3$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRowx2"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions22"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions22"> <argument name="product" value="$$simpleProduct4$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow22"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml index 1f46e1fc9f0b1..78bc85da6f69b 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminProductBundleCreationTest.xml @@ -32,7 +32,7 @@ <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToProductIndexPage"/> - <actionGroup ref="deleteProductsIfTheyExist" stepKey="deleteAllProducts"/> + <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteAllProducts"/> <actionGroup ref="logout" stepKey="logout"/> </after> <!-- go to bundle product creation page--> @@ -48,11 +48,11 @@ <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> @@ -61,7 +61,7 @@ <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty2"/> <!--Create a bundle product with ancillary data--> - <actionGroup ref="CreateBasicBundleProduct" stepKey="createBundledProduct"> + <actionGroup ref="CreateBasicBundleProductActionGroup" stepKey="createBundledProduct"> <argument name="bundleProduct" value="BundleProduct"/> </actionGroup> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml index 730df90b31be6..77be5b879b1c6 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultImageBundleProductTest.xml @@ -25,7 +25,7 @@ </before> <after> <!-- Delete the bundled product we created in the test body --> - <actionGroup ref="deleteProductBySku" stepKey="deleteBundleProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteBundleProduct"> <argument name="sku" value="{{BundleProduct.sku}}"/> </actionGroup> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> @@ -36,11 +36,11 @@ <!-- Create a bundle product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPageBundle"/> <waitForPageLoad stepKey="waitForProductPageLoadBundle"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateBundleProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateBundleProduct"> <argument name="product" value="BundleProduct"/> </actionGroup> - <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillBundleProductNameAndSku"> + <actionGroup ref="FillProductNameAndSkuInProductFormActionGroup" stepKey="fillBundleProductNameAndSku"> <argument name="product" value="BundleProduct"/> </actionGroup> @@ -55,11 +55,11 @@ <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> @@ -68,27 +68,27 @@ <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty2"/> <!-- Add image to product --> - <actionGroup ref="addProductImage" stepKey="addImageForProduct"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImageForProduct"> <argument name="image" value="MagentoLogo"/> </actionGroup> <!--Save product--> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Remove image from product --> - <actionGroup ref="removeProductImage" stepKey="removeProductImage"/> + <actionGroup ref="RemoveProductImageActionGroup" stepKey="removeProductImage"/> - <actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductFormAfterRemove"/> <!-- Assert product image not in admin product form --> - <actionGroup ref="assertProductImageNotInAdminProductPage" stepKey="assertProductImageNotInAdminProductPage"/> + <actionGroup ref="AssertProductImageNotInAdminProductPageActionGroup" stepKey="assertProductImageNotInAdminProductPage"/> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPageAfterRemove"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPageAfterRemove"> <argument name="product" value="BundleProduct"/> </actionGroup> <!-- Assert product image not in storefront product page --> - <actionGroup ref="assertProductImageNotInStorefrontProductPage" stepKey="assertProductImageNotInStorefrontProductPage"> + <actionGroup ref="AssertProductImageNotInStorefrontProductPageActionGroup" stepKey="assertProductImageNotInStorefrontProductPage"> <argument name="product" value="BundleProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml index d050c5443d1fe..0de9f4ee75a4d 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminRemoveDefaultVideoBundleProductTest.xml @@ -29,10 +29,10 @@ <!-- Create a bundle product --> <!-- Replacing steps in base AdminRemoveDefaultVideoSimpleProductTest --> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProductPage"> <argument name="product" value="BundleProduct"/> </actionGroup> - <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillMainProductForm"> + <actionGroup ref="FillProductNameAndSkuInProductFormActionGroup" stepKey="fillMainProductForm"> <argument name="product" value="BundleProduct"/> </actionGroup> @@ -46,11 +46,11 @@ <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProducts" after="selectOptionBundleTitle"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProducts" after="waitForAddProducts"/> <waitForPageLoad stepKey="waitForPageLoad" after="clickAddProducts"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku1" after="waitForPageLoad"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku1" after="waitForPageLoad"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="checkOption1" after="filterProductGridBySku1"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku2" after="checkOption1"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku2" after="checkOption1"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="checkOption2" after="filterProductGridBySku2"/> @@ -59,7 +59,7 @@ <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillQty2" before="saveProductForm"/> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="BundleProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminShouldBeAbleToMassUpdateAttributesForBundleProductsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminShouldBeAbleToMassUpdateAttributesForBundleProductsTest.xml index fe55fda4d0e05..994a10ae02692 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminShouldBeAbleToMassUpdateAttributesForBundleProductsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminShouldBeAbleToMassUpdateAttributesForBundleProductsTest.xml @@ -59,17 +59,17 @@ <magentoCLI command="cron:run" stepKey="cronRun"/> <magentoCLI command="cron:run" stepKey="cronRunTwice"/> <!-- Search for a product with a new name and Open Product --> - <actionGroup ref="filterProductGridByName" stepKey="searchWithNewProductName"> + <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="searchWithNewProductName"> <argument name="product" value="UpdateAttributeNameAndDescription"/> </actionGroup> <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openProductPage"> <argument name="product" value="$$createFixedBundleProduct$$"/> </actionGroup> <!-- Assert product name and description --> - <actionGroup ref="AssertProductNameInProductEditForm" stepKey="assertProductName"> + <actionGroup ref="AssertProductNameInProductEditFormActionGroup" stepKey="assertProductName"> <argument name="productName" value="{{UpdateAttributeNameAndDescription.name}}"/> </actionGroup> - <actionGroup ref="AssertProductDescriptionInProductEditForm" stepKey="assertProductDescription"> + <actionGroup ref="AssertProductDescriptionInProductEditFormActionGroup" stepKey="assertProductDescription"> <argument name="productDescription" value="{{UpdateAttributeNameAndDescription.description}}"/> </actionGroup> </test> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml index c922b981aecd9..2b36458caa182 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductFixedPricingTest.xml @@ -32,7 +32,7 @@ <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> <!-- Delete the bundled product we created in the test body --> - <actionGroup ref="deleteProductBySku" stepKey="deleteBundleProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteBundleProduct"> <argument name="sku" value="{{BundleProduct.sku}}"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> @@ -50,11 +50,11 @@ <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> @@ -63,7 +63,7 @@ <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty2"/> <!--Fill out ancillary data on bundle product--> - <actionGroup ref="AncillaryPrepBundleProduct" stepKey="createBundledProductForTwoSimpleProducts"> + <actionGroup ref="AncillaryPrepBundleProductActionGroup" stepKey="createBundledProductForTwoSimpleProducts"> <argument name="bundleProduct" value="BundleProduct"/> </actionGroup> @@ -82,7 +82,7 @@ <waitForPageLoad stepKey="WaitForPageToLoad"/> <conditionalClick selector="{{AdminProductFiltersSection.filtersClear}}" dependentSelector="{{AdminProductFiltersSection.filtersClear}}" visible="true" stepKey="ClickOnButtonToRemoveFiltersIfPresent"/> <waitForPageLoad stepKey="WaitForClear"/> - <actionGroup ref="filterProductGridByName" stepKey="filterBundleProductOptionsDownToName"> + <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="filterBundleProductOptionsDownToName"> <argument name="product" value="BundleProduct"/> </actionGroup> <seeElement selector="{{AdminProductFiltersSection.priceOfFirstRow(BundleProduct.fixedPrice)}}" stepKey="seePrice"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml index 46c6114637af6..91a2d15287033 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/BundleProductWithTierPriceInCartTest.xml @@ -26,7 +26,7 @@ <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> <actionGroup ref="StorefrontSignOutActionGroup" stepKey="StorefrontSignOutActionGroup"/> - <actionGroup stepKey="deleteBundle" ref="deleteProductUsingProductGrid"> + <actionGroup stepKey="deleteBundle" ref="DeleteProductUsingProductGridActionGroup"> <argument name="product" value="BundleProduct"/> </actionGroup> <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearFiltersAfter"/> @@ -37,8 +37,8 @@ </after> <amOnPage url="{{AdminProductCreatePage.url(BundleProduct.set, BundleProduct.type)}}" stepKey="goToBundleProductCreationPage"/> <waitForPageLoad stepKey="waitForBundleProductCreatePageToLoad"/> - <actionGroup ref="fillMainBundleProductForm" stepKey="fillMainFieldsForBundle"/> - <actionGroup ref="addBundleOptionWithOneProduct" stepKey="addBundleOption1"> + <actionGroup ref="FillMainBundleProductFormActionGroup" stepKey="fillMainFieldsForBundle"/> + <actionGroup ref="AddBundleOptionWithOneProductActionGroup" stepKey="addBundleOption1"> <argument name="x" value="0"/> <argument name="n" value="1"/> <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> @@ -46,7 +46,7 @@ <argument name="optionTitle" value="Option1"/> <argument name="inputType" value="checkbox"/> </actionGroup> - <actionGroup ref="addBundleOptionWithOneProduct" stepKey="addBundleOption2"> + <actionGroup ref="AddBundleOptionWithOneProductActionGroup" stepKey="addBundleOption2"> <argument name="x" value="1"/> <argument name="n" value="2"/> <argument name="prodOneSku" value="$$simpleProduct2.sku$$"/> @@ -55,7 +55,7 @@ <argument name="inputType" value="checkbox"/> </actionGroup> <scrollToTopOfPage stepKey="scrollTopPageProduct"/> - <actionGroup ref="ProductSetAdvancedPricing" stepKey="addTierPriceProduct"> + <actionGroup ref="ProductSetAdvancedPricingActionGroup" stepKey="addTierPriceProduct"> <argument name="group" value="ALL GROUPS"/> <argument name="quantity" value="1"/> <argument name="price" value="Discount"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml index ded8bb3c83337..4efacbb267a0b 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/CurrencyChangingBundleProductInCartTest.xml @@ -24,7 +24,7 @@ </before> <after> <!-- Delete the bundled product --> - <actionGroup stepKey="deleteBundle" ref="deleteProductUsingProductGrid"> + <actionGroup stepKey="deleteBundle" ref="DeleteProductUsingProductGridActionGroup"> <argument name="product" value="BundleProduct"/> </actionGroup> <actionGroup ref="AdminClearFiltersActionGroup" stepKey="ClearFiltersAfter"/> @@ -46,9 +46,9 @@ <!--Go to bundle product creation page--> <amOnPage url="{{AdminProductCreatePage.url(BundleProduct.set, BundleProduct.type)}}" stepKey="goToBundleProductCreationPage"/> <waitForPageLoad stepKey="waitForBundleProductCreatePageToLoad"/> - <actionGroup ref="fillMainBundleProductForm" stepKey="fillMainFieldsForBundle"/> + <actionGroup ref="FillMainBundleProductFormActionGroup" stepKey="fillMainFieldsForBundle"/> <!-- Add Option, a "Radio Buttons" type option --> - <actionGroup ref="addBundleOptionWithTwoProducts" stepKey="addBundleOptionWithTwoProducts2"> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts2"> <argument name="x" value="0"/> <argument name="n" value="1"/> <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> @@ -58,7 +58,7 @@ </actionGroup> <checkOption selector="{{AdminProductFormBundleSection.userDefinedQuantity('0', '0')}}" stepKey="userDefinedQuantitiyOptionProduct0"/> <checkOption selector="{{AdminProductFormBundleSection.userDefinedQuantity('0', '1')}}" stepKey="userDefinedQuantitiyOptionProduct1"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <amOnPage url="{{ConfigCurrencySetupPage.url}}" stepKey="navigateToConfigCurrencySetupPage"/> <waitForPageLoad stepKey="waitForConfigCurrencySetupPage"/> <conditionalClick selector="{{CurrencySetupSection.currencyOptions}}" dependentSelector="{{CurrencySetupSection.allowCurrencies}}" visible="false" stepKey="openOptions"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml index 0cfd1f99a8ce0..f73818a86a025 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/EnableDisableBundleProductStatusTest.xml @@ -46,11 +46,11 @@ <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> @@ -59,7 +59,7 @@ <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty2"/> <!--Fill out ancillary data on bundle product--> - <actionGroup ref="AncillaryPrepBundleProduct" stepKey="createBundledProductForTwoSimpleProducts"> + <actionGroup ref="AncillaryPrepBundleProductActionGroup" stepKey="createBundledProductForTwoSimpleProducts"> <argument name="bundleProduct" value="BundleProduct"/> </actionGroup> @@ -77,7 +77,7 @@ <!--Testing disabled view--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="GoToProductCatalog"/> <waitForPageLoad stepKey="WaitForCatalogProductPageToLoad"/> - <actionGroup ref="filterProductGridBySku" stepKey="FindProductEditPage"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="FindProductEditPage"> <argument name="product" value="BundleProduct"/> </actionGroup> <click selector="{{AdminDataGridTableSection.rowViewAction('1')}}" stepKey="ClickProductInGrid"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/EndToEndB2CAdminTest.xml index 9040d675be34f..7e5db7643c2dd 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -11,7 +11,7 @@ <!--Create Bundle Product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPageBundle" after="seeSimpleProductInGrid"/> <waitForPageLoad stepKey="waitForProductPageLoadBundle" after="visitAdminProductPageBundle"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateBundleProduct" after="waitForProductPageLoadBundle"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateBundleProduct" after="waitForProductPageLoadBundle"> <argument name="product" value="BundleProduct"/> </actionGroup> @@ -24,20 +24,20 @@ <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle" after="selectInputType"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption" after="waitForAddProductsToBundle"/> <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts" after="clickAddProductsToOption"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions" after="waitForPageLoadAfterBundleProducts"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions" after="waitForPageLoadAfterBundleProducts"> <argument name="product" value="SimpleProduct"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow" after="filterBundleProductOptions"/> <click selector="{{AdminAddProductsToOptionPanel.addSelectedProducts}}" stepKey="clickAddSelectedBundleProducts" after="selectFirstGridRow"/> <fillField selector="{{AdminProductFormBundleSection.firstProductQuantity}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty" after="clickAddSelectedBundleProducts"/> - <actionGroup ref="saveProductForm" stepKey="saveBundleProduct" after="fillProductDefaultQty"/> - <actionGroup ref="viewBundleProductInAdminGrid" stepKey="viewBundleProductInGrid" after="saveBundleProduct"> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveBundleProduct" after="fillProductDefaultQty"/> + <actionGroup ref="ViewBundleProductInAdminGridActionGroup" stepKey="viewBundleProductInGrid" after="saveBundleProduct"> <argument name="product" value="BundleProduct"/> </actionGroup> <!--@TODO Move cleanup to "after" when MQE-830 is resolved--> <comment userInput="Clean up bundle product" stepKey="cleanUpBundleProduct" after="deleteSimpleProduct"/> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteBundleProduct" after="cleanUpBundleProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteBundleProduct" after="cleanUpBundleProduct"> <argument name="product" value="BundleProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml index ff192538637ef..97e509db39fa7 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/MassEnableDisableBundleProductsTest.xml @@ -48,11 +48,11 @@ <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> @@ -60,7 +60,7 @@ <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '0')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty1"/> <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty2"/> - <actionGroup ref="AncillaryPrepBundleProduct" stepKey="createBundledProductForTwoSimpleProducts"/> + <actionGroup ref="AncillaryPrepBundleProductActionGroup" stepKey="createBundledProductForTwoSimpleProducts"/> <!--Save the product--> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> @@ -80,11 +80,11 @@ <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle2"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption2"/> <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts2"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptionsx2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptionsx2"> <argument name="product" value="$$simpleProduct3$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRowx2"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions22"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions22"> <argument name="product" value="$$simpleProduct4$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow22"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/NewBundleProductSelectionTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/NewBundleProductSelectionTest.xml index e0a6a9afd648e..2b948ff02d38c 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/NewBundleProductSelectionTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/NewBundleProductSelectionTest.xml @@ -27,7 +27,7 @@ <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="GoToCatalogProductPage"/> <waitForPageLoad stepKey="WaitForPageToLoad"/> <!--Selecting new bundle product--> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateBundleProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateBundleProduct"> <argument name="product" value="BundleProduct"/> </actionGroup> <!--Testing if on the bundle product creation page--> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml index 8efe32a7d84c0..27369d38f0c35 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/NewProductsListWidgetBundleProductTest.xml @@ -52,11 +52,11 @@ <selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="checkbox" stepKey="selectInputType"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml index a1630128638d9..7ad97a8991349 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml @@ -43,7 +43,7 @@ <deleteData createDataKey="simpleProduct9" stepKey="deleteSimpleProduct9"/> <deleteData createDataKey="simpleProduct10" stepKey="deleteSimpleProduct10"/> <!--delete created bundle product--> - <actionGroup stepKey="deleteProduct1" ref="deleteProductBySku"> + <actionGroup stepKey="deleteProduct1" ref="DeleteProductBySkuActionGroup"> <argument name="sku" value="{{BundleProduct.sku}}"/> </actionGroup> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" @@ -54,15 +54,15 @@ <!-- Start creating a bundle product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> <waitForPageLoad stepKey="waitForProductList"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="BundleProduct"/> </actionGroup> - <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillNameAndSku"> + <actionGroup ref="FillProductNameAndSkuInProductFormActionGroup" stepKey="fillNameAndSku"> <argument name="product" value="BundleProduct"/> </actionGroup> <!-- Add Option One, a "Checkbox" type option, with tree products --> - <actionGroup ref="addBundleOptionWithTreeProducts" stepKey="addBundleOptionWithTreeProducts"> + <actionGroup ref="AddBundleOptionWithThreeProductsActionGroup" stepKey="addBundleOptionWithTreeProducts"> <argument name="x" value="0"/> <argument name="n" value="1"/> <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> @@ -73,7 +73,7 @@ </actionGroup> <!-- Add Option Two, a "Radio Buttons" type option, with one product --> - <actionGroup ref="addBundleOptionWithOneProduct" stepKey="addBundleOptionWithOneProduct"> + <actionGroup ref="AddBundleOptionWithOneProductActionGroup" stepKey="addBundleOptionWithOneProduct"> <argument name="x" value="1"/> <argument name="n" value="2"/> <argument name="prodOneSku" value="$$simpleProduct4.sku$$"/> @@ -83,7 +83,7 @@ </actionGroup> <!-- Add Option Tree, a "Checkbox" type option, with six products --> - <actionGroup ref="addBundleOptionWithSixProducts" stepKey="addBundleOptionWithSixProducts"> + <actionGroup ref="AddBundleOptionWithSixProductsActionGroup" stepKey="addBundleOptionWithSixProducts"> <argument name="x" value="2"/> <argument name="n" value="3"/> <argument name="prodOneSku" value="$$simpleProduct5.sku$$"/> @@ -97,7 +97,7 @@ </actionGroup> <!-- Save product--> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!--Go to Storefront and open Bundle Product page--> <amOnPage url="{{BundleProduct.sku}}.html" stepKey="goToStorefront"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml index 33181d6e920eb..64b786ac4ed7c 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml @@ -67,7 +67,7 @@ <!--Check subtotal in created order--> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderById"> <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml index 40132ea956584..05100284a3fe9 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAdminEditDataTest.xml @@ -32,11 +32,11 @@ <!-- Create a bundle product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPageBundle"/> <waitForPageLoad stepKey="waitForProductPageLoadBundle"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateBundleProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateBundleProduct"> <argument name="product" value="BundleProduct"/> </actionGroup> - <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillBundleProductNameAndSku"> + <actionGroup ref="FillProductNameAndSkuInProductFormActionGroup" stepKey="fillBundleProductNameAndSku"> <argument name="product" value="BundleProduct"/> </actionGroup> @@ -50,11 +50,11 @@ <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> @@ -79,7 +79,7 @@ <waitForPageLoad stepKey="waitForElementAdded"/> <!-- Go to the shopping cart page and grab the value of the option title --> - <amOnPage url="/checkout/cart/" stepKey="onPageShoppingCart"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="onPageShoppingCart"/> <waitForPageLoad stepKey="waitForCartPageLoad"/> <grabTextFrom selector="{{CheckoutCartProductSection.nthBundleOptionName('1')}}" stepKey="grabTotalBefore"/> @@ -87,7 +87,7 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> <waitForPageLoad stepKey="waitForAdminProductPageLoad"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <actionGroup ref="filterProductGridBySku" stepKey="findCreatedProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct"> <argument name="product" value="BundleProduct"/> </actionGroup> <waitForPageLoad stepKey="waitForProductFilterLoad"/> @@ -100,13 +100,13 @@ <see stepKey="assertSuccess2" selector="{{AdminProductMessagesSection.successMessage}}" userInput="You saved the product."/> <!-- Go to the shopping cart page and make sure the title has changed --> - <amOnPage url="/checkout/cart/" stepKey="onPageShoppingCart1"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="onPageShoppingCart1"/> <waitForPageLoad stepKey="waitForCartPageLoad1"/> <grabTextFrom selector="{{CheckoutCartProductSection.nthBundleOptionName('1')}}" stepKey="grabTotalAfter"/> <assertNotEquals expected="{$grabTotalBefore}" expectedType="string" actual="{$grabTotalAfter}" actualType="string" stepKey="assertNotEquals"/> <!-- Delete the bundled product --> - <actionGroup stepKey="deleteBundle" ref="deleteProductUsingProductGrid"> + <actionGroup stepKey="deleteBundle" ref="DeleteProductUsingProductGridActionGroup"> <argument name="product" value="BundleProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml index 695c3a8bf7dbb..d617ced82074e 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleCartTest.xml @@ -32,15 +32,15 @@ <!-- Start creating a bundle product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> <waitForPageLoad stepKey="waitForProductList"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="BundleProduct"/> </actionGroup> - <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillNameAndSku"> + <actionGroup ref="FillProductNameAndSkuInProductFormActionGroup" stepKey="fillNameAndSku"> <argument name="product" value="BundleProduct"/> </actionGroup> <!-- Add Option One, a "Drop-down" type option --> - <actionGroup ref="addBundleOptionWithTwoProducts" stepKey="addBundleOptionWithTwoProducts1"> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts1"> <argument name="x" value="0"/> <argument name="n" value="1"/> <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> @@ -50,7 +50,7 @@ </actionGroup> <!-- Add Option Two, a "Radio Buttons" type option --> - <actionGroup ref="addBundleOptionWithTwoProducts" stepKey="addBundleOptionWithTwoProducts2"> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts2"> <argument name="x" value="1"/> <argument name="n" value="2"/> <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> @@ -60,7 +60,7 @@ </actionGroup> <!-- Add Option Three, a "Checkbox" type option --> - <actionGroup ref="addBundleOptionWithTwoProducts" stepKey="addBundleOptionWithTwoProducts3"> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts3"> <argument name="x" value="2"/> <argument name="n" value="3"/> <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> @@ -70,7 +70,7 @@ </actionGroup> <!-- Add Option Four, a "Multi Select" type option --> - <actionGroup ref="addBundleOptionWithTwoProducts" stepKey="addBundleOptionWithTwoProducts4"> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts4"> <argument name="x" value="3"/> <argument name="n" value="4"/> <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> @@ -80,7 +80,7 @@ </actionGroup> <!-- Save product and go to storefront --> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <amOnPage url="{{BundleProduct.sku}}.html" stepKey="goToStorefront"/> <waitForPageLoad stepKey="waitForStorefront"/> <click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomize"/> @@ -149,15 +149,15 @@ <!-- Start creating a bundle product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> <waitForPageLoad stepKey="waitForProductList"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="BundleProduct"/> </actionGroup> - <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillNameAndSku"> + <actionGroup ref="FillProductNameAndSkuInProductFormActionGroup" stepKey="fillNameAndSku"> <argument name="product" value="BundleProduct"/> </actionGroup> <!-- Add Option One, a "Drop-down" type option --> - <actionGroup ref="addBundleOptionWithTwoProducts" stepKey="addBundleOptionWithTwoProducts1"> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts1"> <argument name="x" value="0"/> <argument name="n" value="1"/> <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> @@ -167,7 +167,7 @@ </actionGroup> <!-- Add Option Two, a "Radio Buttons" type option --> - <actionGroup ref="addBundleOptionWithTwoProducts" stepKey="addBundleOptionWithTwoProducts2"> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts2"> <argument name="x" value="1"/> <argument name="n" value="2"/> <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> @@ -177,7 +177,7 @@ </actionGroup> <!-- Add Option Three, a "Checkbox" type option --> - <actionGroup ref="addBundleOptionWithTwoProducts" stepKey="addBundleOptionWithTwoProducts3"> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts3"> <argument name="x" value="2"/> <argument name="n" value="3"/> <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> @@ -187,7 +187,7 @@ </actionGroup> <!-- Add Option Four, a "Multi Select" type option --> - <actionGroup ref="addBundleOptionWithTwoProducts" stepKey="addBundleOptionWithTwoProducts4"> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts4"> <argument name="x" value="3"/> <argument name="n" value="4"/> <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> @@ -197,7 +197,7 @@ </actionGroup> <!-- Save product and go to storefront --> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <amOnPage url="{{BundleProduct.sku}}.html" stepKey="goToStorefront"/> <waitForPageLoad stepKey="waitForStorefront"/> <click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomize"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml index d7394b2dbf32e..cc0064e87c3c1 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductDetailsTest.xml @@ -26,14 +26,14 @@ <!-- Admin Login--> <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> </before> <after> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToProductIndexPage"/> - <actionGroup ref="deleteProductsIfTheyExist" stepKey="deleteAllProducts"/> + <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteAllProducts"/> <actionGroup ref="logout" stepKey="logout"/> </after> <!-- go to bundle product creation page--> @@ -55,11 +55,11 @@ <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> @@ -68,7 +68,7 @@ <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty2"/> <!--Create a basic bundle product--> - <actionGroup ref="CreateBasicBundleProduct" stepKey="createBundledProductForTwoSimpleProducts"> + <actionGroup ref="CreateBasicBundleProductActionGroup" stepKey="createBundledProductForTwoSimpleProducts"> <argument name="bundleProduct" value="BundleProduct"/> </actionGroup> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGrid.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGrid.xml index 9ad4b6828d6e4..364d4fa68e590 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGrid.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundleProductShownInCategoryListAndGrid.xml @@ -37,7 +37,7 @@ <!--Make category--> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="goToCategoryPage"/> <waitForPageLoad stepKey="waitForCategoryPageLoad"/> - <actionGroup ref="CreateCategory" stepKey="createASubcategory"> + <actionGroup ref="CreateCategoryActionGroup" stepKey="createASubcategory"> <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> @@ -59,23 +59,27 @@ <selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="{{BundleProduct.optionInputType1}}" stepKey="selectInputType"/> <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> <click selector="{{AdminAddProductsToOptionPanel.addSelectedProducts}}" stepKey="clickAddSelectedBundleProducts"/> <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '0')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty1"/> <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty2"/> - <actionGroup ref="AncillaryPrepBundleProduct" stepKey="createBundledProductForTwoSimpleProducts"/> + <actionGroup ref="AncillaryPrepBundleProductActionGroup" stepKey="createBundledProductForTwoSimpleProducts"/> <!--Save the product--> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="messageYouSavedTheProductIsShown"/> + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + <!--Go to category page--> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> <waitForPageLoad stepKey="waitForHomePageToload"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPrices.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPrices.xml index 532af1ea76dca..0487668c10094 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPrices.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCheckBundleProductOptionTierPrices.xml @@ -28,7 +28,7 @@ <!-- Simple product 1 --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <amOnPage url="{{AdminProductEditPage.url($$simpleProduct1CreateBundleProduct.id$$)}}" stepKey="openAdminEditPageProduct1"/> - <actionGroup ref="ProductSetAdvancedPricing" stepKey="addTierPriceProduct1"> + <actionGroup ref="ProductSetAdvancedPricingActionGroup" stepKey="addTierPriceProduct1"> <argument name="group" value="ALL GROUPS"/> <argument name="quantity" value="5"/> <argument name="price" value="Discount"/> @@ -36,7 +36,7 @@ </actionGroup> <!-- Simple product 2 --> <amOnPage url="{{AdminProductEditPage.url($$simpleProduct2CreateBundleProduct.id$$)}}" stepKey="openAdminEditPageProduct2"/> - <actionGroup ref="ProductSetAdvancedPricing" stepKey="addTierPriceProduct2"> + <actionGroup ref="ProductSetAdvancedPricingActionGroup" stepKey="addTierPriceProduct2"> <argument name="group" value="ALL GROUPS"/> <argument name="quantity" value="7"/> <argument name="price" value="Discount"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSearchBundleProductsByKeywordsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSearchBundleProductsByKeywordsTest.xml index d27cd0df88239..ae9d01a50f46e 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSearchBundleProductsByKeywordsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSearchBundleProductsByKeywordsTest.xml @@ -39,7 +39,7 @@ <requiredEntity createDataKey="fixedBundleOption"/> <requiredEntity createDataKey="createSimpleProductTwo"/> </createData> - <magentoCLI command="indexer:reindex" arguments="cataloginventory_stock catalogsearch_fulltext" stepKey="reindex"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> </before> <after> <deleteData createDataKey="createDynamicBundle" stepKey="deleteDynamicBundleProduct"/> @@ -53,84 +53,84 @@ <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchDynamic"> <argument name="phrase" value="Dynamic"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchSeeProductByName" stepKey="assertDynamicBundleInSearchResultByDynamic"> + <actionGroup ref="StorefrontQuickSearchSeeProductByNameActionGroup" stepKey="assertDynamicBundleInSearchResultByDynamic"> <argument name="productName" value="$createDynamicBundle.name$"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchCheckProductNameNotInGrid" stepKey="assertFixedBundleInSearchResultByDynamic"> + <actionGroup ref="StorefrontQuickSearchCheckProductNameNotInGridActionGroup" stepKey="assertFixedBundleInSearchResultByDynamic"> <argument name="productName" value="$createFixedBundle.name$"/> </actionGroup> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchByDescription"> <argument name="phrase" value="Dynamicscription"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchSeeProductByName" stepKey="assertDynamicBundleInSearchResultByDescription"> + <actionGroup ref="StorefrontQuickSearchSeeProductByNameActionGroup" stepKey="assertDynamicBundleInSearchResultByDescription"> <argument name="productName" value="$createDynamicBundle.name$"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchCheckProductNameNotInGrid" stepKey="dontSeeFixedBundleInSearchResultByDescription"> + <actionGroup ref="StorefrontQuickSearchCheckProductNameNotInGridActionGroup" stepKey="dontSeeFixedBundleInSearchResultByDescription"> <argument name="productName" value="$createFixedBundle.name$"/> </actionGroup> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchShortDescription"> <argument name="phrase" value="Dynamictest"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchSeeProductByName" stepKey="assertDynamicBundleInSearchResultByShortDescription"> + <actionGroup ref="StorefrontQuickSearchSeeProductByNameActionGroup" stepKey="assertDynamicBundleInSearchResultByShortDescription"> <argument name="productName" value="$createDynamicBundle.name$"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchCheckProductNameNotInGrid" stepKey="dontSeeFixedBundleInSearchResultByShortDescription"> + <actionGroup ref="StorefrontQuickSearchCheckProductNameNotInGridActionGroup" stepKey="dontSeeFixedBundleInSearchResultByShortDescription"> <argument name="productName" value="$createFixedBundle.name$"/> </actionGroup> <!-- 3. Fill quick search bar with test values mutual for both products and click search --> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchTest123"> <argument name="phrase" value="Test 123"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchSeeProductByName" stepKey="seeDynamicBundleByTest123"> + <actionGroup ref="StorefrontQuickSearchSeeProductByNameActionGroup" stepKey="seeDynamicBundleByTest123"> <argument name="productName" value="$createDynamicBundle.name$"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchSeeProductByName" stepKey="seeFixedBundleByTest123"> + <actionGroup ref="StorefrontQuickSearchSeeProductByNameActionGroup" stepKey="seeFixedBundleByTest123"> <argument name="productName" value="$createFixedBundle.name$"/> </actionGroup> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchTesting321"> <argument name="phrase" value="Testing 321"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchSeeProductByName" stepKey="seeDynamicBundleByTesting321"> + <actionGroup ref="StorefrontQuickSearchSeeProductByNameActionGroup" stepKey="seeDynamicBundleByTesting321"> <argument name="productName" value="$createDynamicBundle.name$"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchSeeProductByName" stepKey="seeFixedBundleByTesting321"> + <actionGroup ref="StorefrontQuickSearchSeeProductByNameActionGroup" stepKey="seeFixedBundleByTesting321"> <argument name="productName" value="$createFixedBundle.name$"/> </actionGroup> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchShort555"> <argument name="phrase" value="Short 555"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchSeeProductByName" stepKey="seeDynamicBundleByShort555"> + <actionGroup ref="StorefrontQuickSearchSeeProductByNameActionGroup" stepKey="seeDynamicBundleByShort555"> <argument name="productName" value="$createDynamicBundle.name$"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchSeeProductByName" stepKey="seeFixedBundleByShort555"> + <actionGroup ref="StorefrontQuickSearchSeeProductByNameActionGroup" stepKey="seeFixedBundleByShort555"> <argument name="productName" value="$createFixedBundle.name$"/> </actionGroup> <!-- 4. Fill quick search bar with test values unique for fixed bundle product and click search --> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchByFixed"> <argument name="phrase" value="Fixed"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchSeeProductByName" stepKey="seeFixedBundleByFixed"> + <actionGroup ref="StorefrontQuickSearchSeeProductByNameActionGroup" stepKey="seeFixedBundleByFixed"> <argument name="productName" value="$createFixedBundle.name$"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchCheckProductNameNotInGrid" stepKey="dontSeeDynamicBundleByFixed"> + <actionGroup ref="StorefrontQuickSearchCheckProductNameNotInGridActionGroup" stepKey="dontSeeDynamicBundleByFixed"> <argument name="productName" value="$createDynamicBundle.name$"/> </actionGroup> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchByDescriptionForFixed"> <argument name="phrase" value="Fixedscription"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchSeeProductByName" stepKey="seeFixedBundleByDescriptionForFixed"> + <actionGroup ref="StorefrontQuickSearchSeeProductByNameActionGroup" stepKey="seeFixedBundleByDescriptionForFixed"> <argument name="productName" value="$createFixedBundle.name$"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchCheckProductNameNotInGrid" stepKey="dontSeeDynamicProductByDescriptionForFixed"> + <actionGroup ref="StorefrontQuickSearchCheckProductNameNotInGridActionGroup" stepKey="dontSeeDynamicProductByDescriptionForFixed"> <argument name="productName" value="$createDynamicBundle.name$"/> </actionGroup> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchByShortDescriptionForFixed"> <argument name="phrase" value="Fixedtest"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchSeeProductByName" stepKey="seeFixedBundleByShortDescriptionForFixed"> + <actionGroup ref="StorefrontQuickSearchSeeProductByNameActionGroup" stepKey="seeFixedBundleByShortDescriptionForFixed"> <argument name="productName" value="$createFixedBundle.name$"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchCheckProductNameNotInGrid" stepKey="dontSeeDynamicBundleByShortDescriptionForFixed"> + <actionGroup ref="StorefrontQuickSearchCheckProductNameNotInGridActionGroup" stepKey="dontSeeDynamicBundleByShortDescriptionForFixed"> <argument name="productName" value="$createDynamicBundle.name$"/> </actionGroup> </test> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml index 5e6e891541420..dc8cb24246567 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontCustomerSelectAndSetBundleOptionsTest.xml @@ -25,7 +25,7 @@ </before> <after> <!-- Delete the bundled product --> - <actionGroup stepKey="deleteBundle" ref="deleteProductUsingProductGrid"> + <actionGroup stepKey="deleteBundle" ref="DeleteProductUsingProductGridActionGroup"> <argument name="product" value="BundleProduct"/> </actionGroup> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="logout"/> @@ -36,15 +36,15 @@ <!-- Start creating a bundle product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> <waitForPageLoad stepKey="waitForProductList"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="BundleProduct"/> </actionGroup> - <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillNameAndSku"> + <actionGroup ref="FillProductNameAndSkuInProductFormActionGroup" stepKey="fillNameAndSku"> <argument name="product" value="BundleProduct"/> </actionGroup> <!-- Add Option One, a "Drop-down" type option --> - <actionGroup ref="addBundleOptionWithTwoProducts" stepKey="addBundleOptionWithTwoProducts1"> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts1"> <argument name="x" value="0"/> <argument name="n" value="1"/> <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> @@ -56,7 +56,7 @@ <checkOption selector="{{AdminProductFormBundleSection.userDefinedQuantity('0', '1')}}" stepKey="userDefinedQuantitiyOption0Product1"/> <!-- Add Option Two, a "Radio Buttons" type option --> - <actionGroup ref="addBundleOptionWithTwoProducts" stepKey="addBundleOptionWithTwoProducts2"> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts2"> <argument name="x" value="1"/> <argument name="n" value="2"/> <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> @@ -68,7 +68,7 @@ <checkOption selector="{{AdminProductFormBundleSection.userDefinedQuantity('1', '1')}}" stepKey="userDefinedQuantitiyOption1Product1"/> <!-- Add Option Three, a "Checkbox" type option --> - <actionGroup ref="addBundleOptionWithTwoProducts" stepKey="addBundleOptionWithTwoProducts3"> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts3"> <argument name="x" value="2"/> <argument name="n" value="3"/> <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> @@ -78,7 +78,7 @@ </actionGroup> <!-- Add Option Four, a "Multi Select" type option --> - <actionGroup ref="addBundleOptionWithTwoProducts" stepKey="addBundleOptionWithTwoProducts4"> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addBundleOptionWithTwoProducts4"> <argument name="x" value="3"/> <argument name="n" value="4"/> <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> @@ -88,7 +88,7 @@ </actionGroup> <!-- Save product and go to storefront --> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <amOnPage url="{{BundleProduct.sku}}.html" stepKey="goToStorefront"/> <waitForPageLoad stepKey="waitForStorefront"/> <click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomize"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml index 58806126aee30..9dbd6e26bddc4 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontEditBundleProductTest.xml @@ -32,11 +32,11 @@ <!-- Create a bundle product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPageBundle"/> <waitForPageLoad stepKey="waitForProductPageLoadBundle"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateBundleProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateBundleProduct"> <argument name="product" value="BundleProduct"/> </actionGroup> - <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillBundleProductNameAndSku"> + <actionGroup ref="FillProductNameAndSkuInProductFormActionGroup" stepKey="fillBundleProductNameAndSku"> <argument name="product" value="BundleProduct"/> </actionGroup> @@ -50,11 +50,11 @@ <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> <waitForPageLoad stepKey="waitForPageLoadAfterBundleProducts"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> @@ -86,7 +86,7 @@ <waitForPageLoad stepKey="waitForElementAdded2"/> <!-- Go to the shopping cart page and edit the first product --> - <amOnPage url="/checkout/cart/" stepKey="onPageShoppingCart"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="onPageShoppingCart"/> <waitForPageLoad stepKey="waitForCartPageLoad"/> <waitForElementVisible stepKey="waitForInfoDropdown" selector="{{CheckoutCartSummarySection.total}}"/> <waitForPageLoad stepKey="waitForCartPageLoad3"/> @@ -104,7 +104,7 @@ <waitForPageLoad stepKey="waitForElementAdded3"/> <!-- Go to the shopping cart page --> - <amOnPage url="/checkout/cart/" stepKey="onPageShoppingCart2"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="onPageShoppingCart2"/> <waitForPageLoad stepKey="waitForCartPageLoad2"/> <!-- Assert that the options are both there and the proce no longer matches --> @@ -116,7 +116,7 @@ <assertNotEquals expected="{$grabTotalBefore}" expectedType="string" actual="{$grabTotalAfter}" actualType="string" stepKey="assertNotEquals"/> <!-- Delete the bundled product --> - <actionGroup stepKey="deleteBundle" ref="deleteProductUsingProductGrid"> + <actionGroup stepKey="deleteBundle" ref="DeleteProductUsingProductGridActionGroup"> <argument name="product" value="BundleProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml index ccd6a58223b3c..7ced26bab2c96 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontGoToDetailsPageWhenAddingToCartTest.xml @@ -48,18 +48,18 @@ <selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="{{BundleProduct.optionInputType1}}" stepKey="selectInputType"/> <waitForElementVisible selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="waitForAddProductsToBundle"/> <click selector="{{AdminProductFormBundleSection.addProductsToOption}}" stepKey="clickAddProductsToOption"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterBundleProductOptions2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterBundleProductOptions2"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectFirstGridRow2"/> <click selector="{{AdminAddProductsToOptionPanel.addSelectedProducts}}" stepKey="clickAddSelectedBundleProducts"/> <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '0')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty1"/> <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity('0', '1')}}" userInput="{{BundleProduct.defaultQuantity}}" stepKey="fillProductDefaultQty2"/> - <actionGroup ref="AncillaryPrepBundleProduct" stepKey="createBundledProductForTwoSimpleProducts"/> + <actionGroup ref="AncillaryPrepBundleProductActionGroup" stepKey="createBundledProductForTwoSimpleProducts"/> <!--Save the product--> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontSortBundleProductsByPriceTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontSortBundleProductsByPriceTest.xml index 18316e41241e4..8bf0de7530e64 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontSortBundleProductsByPriceTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontSortBundleProductsByPriceTest.xml @@ -156,7 +156,7 @@ </actionGroup> <!-- Switch category view to List mode --> - <actionGroup ref="StorefrontSwitchCategoryViewToListMode" stepKey="switchCategoryViewToListMode"/> + <actionGroup ref="StorefrontSwitchCategoryViewToListModeActionGroup" stepKey="switchCategoryViewToListMode"/> <!-- Sort products By Price --> <actionGroup ref="StorefrontCategoryPageSortProductActionGroup" stepKey="sortProductByPrice"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml index 31a5f9bab7758..c78796d2fd8b4 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontVerifyDynamicBundleProductPricesForCombinationOfOptionsTest.xml @@ -42,7 +42,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <amOnPage url="{{AdminProductEditPage.url($$simpleProduct5.id$$)}}" stepKey="openAdminEditPage"/> <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="addSpecialPrice"/> - <actionGroup ref="saveProductForm" stepKey="saveProductForm"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> <!--Create Bundle product--> <createData entity="ApiBundleProductPriceViewRange" stepKey="createBundleProduct"> diff --git a/app/code/Magento/Bundle/Test/Unit/Observer/SetAttributeTabBlockObserverTest.php b/app/code/Magento/Bundle/Test/Unit/Observer/SetAttributeTabBlockObserverTest.php new file mode 100644 index 0000000000000..08f6a05bd10bf --- /dev/null +++ b/app/code/Magento/Bundle/Test/Unit/Observer/SetAttributeTabBlockObserverTest.php @@ -0,0 +1,117 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Test\Unit\Observer; + +use Magento\Bundle\Block\Adminhtml\Catalog\Product\Edit\Tab\Attributes; +use Magento\Bundle\Observer\SetAttributeTabBlockObserver; +use Magento\Catalog\Helper\Catalog; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Type; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Class SetAttributeTabBlockObserverTest + * + * Test setting attribute tab block for bundle products + */ +class SetAttributeTabBlockObserverTest extends TestCase +{ + /** + * @var SetAttributeTabBlockObserver + */ + private $observer; + + /** + * @var Catalog|MockObject + */ + private $helperCatalogMock; + + /** + * @var Observer|MockObject + */ + private $observerMock; + + /** + * @var Event|MockObject + */ + private $eventMock; + + /** + * @var Product|MockObject + */ + private $productMock; + + /** + * Set Up + */ + public function setUp() + { + $objectManager = new ObjectManager($this); + $this->helperCatalogMock = $this->createMock(Catalog::class); + $this->observerMock = $this->createMock(Observer::class); + $this->eventMock = $this->getMockBuilder(Event::class) + ->disableOriginalConstructor() + ->setMethods(['getProduct']) + ->getMock(); + $this->productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->observer = $objectManager->getObject( + SetAttributeTabBlockObserver::class, + [ + 'helperCatalog' => $this->helperCatalogMock + ] + ); + } + + /** + * Test setting attribute tab block for bundle product + */ + public function testAddingAttributeTabForBundleProduct() + { + $this->productMock->expects($this->any()) + ->method('getTypeId') + ->willReturn(Type::TYPE_BUNDLE); + $this->eventMock->expects($this->any()) + ->method('getProduct') + ->willReturn($this->productMock); + $this->observerMock->expects($this->any()) + ->method('getEvent') + ->willReturn($this->eventMock); + $this->helperCatalogMock->expects($this->once()) + ->method('setAttributeTabBlock') + ->with(Attributes::class); + + $this->observer->execute($this->observerMock); + } + + /** + * Test setting attribute tab block for a non bundle product + */ + public function testAddingAttributeTabForNonBundleProduct() + { + $this->productMock->expects($this->any()) + ->method('getTypeId') + ->willReturn(Type::TYPE_VIRTUAL); + $this->eventMock->expects($this->any()) + ->method('getProduct') + ->willReturn($this->productMock); + $this->observerMock->expects($this->any()) + ->method('getEvent') + ->willReturn($this->eventMock); + $this->helperCatalogMock->expects($this->never()) + ->method('setAttributeTabBlock'); + + $this->observer->execute($this->observerMock); + } +} diff --git a/app/code/Magento/Bundle/Test/Unit/Ui/DataProvider/Product/Form/Modifier/AbstractModifierTest.php b/app/code/Magento/Bundle/Test/Unit/Ui/DataProvider/Product/Form/Modifier/AbstractModifierTest.php index ccb6226ccd833..bfe0f04d59cb2 100644 --- a/app/code/Magento/Bundle/Test/Unit/Ui/DataProvider/Product/Form/Modifier/AbstractModifierTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Ui/DataProvider/Product/Form/Modifier/AbstractModifierTest.php @@ -45,6 +45,7 @@ protected function setUp() $this->locatorMock = $this->getMockBuilder(LocatorInterface::class) ->getMockForAbstractClass(); $this->productMock = $this->getMockBuilder(ProductInterface::class) + ->setMethods(['getPriceType']) ->getMockForAbstractClass(); $this->locatorMock->expects($this->any()) diff --git a/app/code/Magento/Bundle/Test/Unit/Ui/DataProvider/Product/Form/Modifier/BundlePriceTest.php b/app/code/Magento/Bundle/Test/Unit/Ui/DataProvider/Product/Form/Modifier/BundlePriceTest.php new file mode 100644 index 0000000000000..b0519f1ebddba --- /dev/null +++ b/app/code/Magento/Bundle/Test/Unit/Ui/DataProvider/Product/Form/Modifier/BundlePriceTest.php @@ -0,0 +1,173 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Test\Unit\Ui\DataProvider\Product\Form\Modifier; + +use Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundlePrice; +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Framework\Stdlib\ArrayManager; + +class BundlePriceTest extends AbstractModifierTest +{ + /** + * @return BundlePrice + */ + protected function createModel() + { + return $this->objectManager->getObject( + BundlePrice::class, + [ + 'locator' => $this->locatorMock, + 'arrayManager' => $this->arrayManagerMock + ] + ); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testModifyMeta() + { + $this->productMock->expects($this->any()) + ->method('getId') + ->willReturn(true); + $this->productMock->expects($this->any()) + ->method('getPriceType') + ->willReturn(0); + $priceTypePath = 'bundle-items/children/' . BundlePrice::CODE_PRICE_TYPE; + $priceTypeConfigPath = $priceTypePath . BundlePrice::META_CONFIG_PATH; + $pricePath = 'product-details/children/' . ProductAttributeInterface::CODE_PRICE; + $priceConfigPath = $pricePath . BundlePrice::META_CONFIG_PATH; + $sourceMeta = [ + 'bundle-items' => [ + 'children' => [ + BundlePrice::CODE_PRICE_TYPE => [] + ] + ] + ]; + $priceTypeParams = [ + 'disabled' => true, + 'valueMap' => [ + 'false' => '1', + 'true' => '0' + ], + 'validation' => [ + 'required-entry' => false + ] + ]; + $priceTypeMeta = [ + 'bundle-items' => [ + 'children' => [ + BundlePrice::CODE_PRICE_TYPE => $priceTypeParams + ] + ] + ]; + $priceParams = [ + 'imports' => [ + 'disabled' => 'ns = ${ $.ns }, index = ' . BundlePrice::CODE_PRICE_TYPE . ':checked' + ] + ]; + $priceMeta = [ + 'product-details' => [ + 'children' => [ + BundlePrice::CODE_PRICE_TYPE => [] + ] + ], + 'bundle-items' => [ + 'children' => [ + ProductAttributeInterface::CODE_PRICE => $priceParams + ] + ] + ]; + $taxParams = [ + 'service' => [ + 'template' => '' + ] + ]; + + $this->arrayManagerMock->expects($this->any()) + ->method('findPath') + ->willReturnMap( + [ + [ + BundlePrice::CODE_PRICE_TYPE, + $sourceMeta, + null, + 'children', + ArrayManager::DEFAULT_PATH_DELIMITER, + $priceTypePath + ], + [ + ProductAttributeInterface::CODE_PRICE, + $priceTypeMeta, + BundlePrice::DEFAULT_GENERAL_PANEL . '/children', + 'children', + ArrayManager::DEFAULT_PATH_DELIMITER, + $pricePath + ], + [ + BundlePrice::CODE_TAX_CLASS_ID, + $priceMeta, + null, + 'children', + ArrayManager::DEFAULT_PATH_DELIMITER, + $pricePath + ], + [ + BundlePrice::CODE_TAX_CLASS_ID, + $priceMeta, + null, + 'children', + ArrayManager::DEFAULT_PATH_DELIMITER, + $pricePath + ] + ] + ); + $this->arrayManagerMock->expects($this->exactly(4)) + ->method('merge') + ->willReturnMap( + [ + [ + $priceTypeConfigPath, + $sourceMeta, + $priceTypeParams, + ArrayManager::DEFAULT_PATH_DELIMITER, + $priceTypeMeta + ], + [ + $priceConfigPath, + $priceTypeMeta, + $priceParams, + ArrayManager::DEFAULT_PATH_DELIMITER, + $priceMeta + ], + [ + $priceConfigPath, + $priceMeta, + $priceParams, + ArrayManager::DEFAULT_PATH_DELIMITER, + $priceMeta + ], + [ + $priceConfigPath, + $priceMeta, + $taxParams, + ArrayManager::DEFAULT_PATH_DELIMITER, + $priceMeta + ] + ] + ); + + $this->assertSame($priceMeta, $this->getModel()->modifyMeta($sourceMeta)); + } + + public function testModifyData() + { + $expectedData = []; + $this->assertEquals($expectedData, $this->getModel()->modifyData($expectedData)); + } +} diff --git a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php index 92326bb1521b4..d7da7513c3aac 100644 --- a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php +++ b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePrice.php @@ -5,6 +5,7 @@ */ namespace Magento\Bundle\Ui\DataProvider\Product\Form\Modifier; +use Magento\Bundle\Model\Product\Price; use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier; use Magento\Catalog\Api\Data\ProductAttributeInterface; use Magento\Framework\Stdlib\ArrayManager; @@ -39,7 +40,7 @@ public function __construct( $this->locator = $locator; $this->arrayManager = $arrayManager; } - + /** * @inheritdoc */ @@ -89,7 +90,22 @@ public function modifyMeta(array $meta) ] ] ); - + if ($this->locator->getProduct()->getPriceType() == Price::PRICE_TYPE_DYNAMIC) { + $meta = $this->arrayManager->merge( + $this->arrayManager->findPath( + static::CODE_TAX_CLASS_ID, + $meta, + null, + 'children' + ) . static::META_CONFIG_PATH, + $meta, + [ + 'service' => [ + 'template' => '' + ] + ] + ); + } return $meta; } diff --git a/app/code/Magento/Bundle/etc/di.xml b/app/code/Magento/Bundle/etc/di.xml index d0e956efee694..6c1a5ab2e7257 100644 --- a/app/code/Magento/Bundle/etc/di.xml +++ b/app/code/Magento/Bundle/etc/di.xml @@ -221,4 +221,17 @@ </argument> </arguments> </type> + <type name="Magento\Eav\Model\Config"> + <arguments> + <argument name="attributesForPreload" xsi:type="array"> + <item name="catalog_product" xsi:type="array"> + <item name="price_type" xsi:type="string">catalog_product</item> + <item name="price_view" xsi:type="string">catalog_product</item> + <item name="shipment_type" xsi:type="string">catalog_product</item> + <item name="sku_type" xsi:type="string">catalog_product</item> + <item name="weight_type" xsi:type="string">catalog_product</item> + </item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Bundle/registration.php b/app/code/Magento/Bundle/registration.php index 85d0b8d5c7a3e..bd7dc39cb4ad6 100644 --- a/app/code/Magento/Bundle/registration.php +++ b/app/code/Magento/Bundle/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Bundle', __DIR__); diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/checkbox.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/checkbox.phtml index 08e89699b1f71..236f15d6b376c 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/checkbox.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/checkbox.phtml @@ -10,7 +10,7 @@ <?php $_selections = $_option->getSelections(); ?> <?php $_skipSaleableCheck = $this->helper(Magento\Catalog\Helper\Product::class)->getSkipSaleableCheck(); ?> -<div class="field admin__field options<?php if ($_option->getRequired()) { echo ' required _required'; } ?>"> +<div class="field admin__field options<?php if ($_option->getRequired()) { echo ' _required'; } ?>"> <label class="label admin__field-label"> <span><?= $block->escapeHtml($_option->getTitle()) ?></span> </label> diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/multi.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/multi.phtml index f4c4e3e51ae09..28b94b21b7889 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/multi.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/multi.phtml @@ -9,7 +9,7 @@ <?php $_option = $block->getOption(); ?> <?php $_selections = $_option->getSelections(); ?> <?php $_skipSaleableCheck = $this->helper(Magento\Catalog\Helper\Product::class)->getSkipSaleableCheck(); ?> -<div class="field admin__field <?php if ($_option->getRequired()) { echo ' required'; } ?><?php if ($_option->getDecoratedIsLast()) :?> last<?php endif; ?>"> +<div class="field admin__field <?php if ($_option->getRequired()) { echo ' _required'; } ?><?php if ($_option->getDecoratedIsLast()) :?> last<?php endif; ?>"> <label class="label admin__field-label"><span><?= $block->escapeHtml($_option->getTitle()) ?></span></label> <div class="control admin__field-control"> <?php if (count($_selections) == 1 && $_option->getRequired()) : ?> diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/radio.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/radio.phtml index 0c3835fb32af8..185a9159c8ab3 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/radio.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/radio.phtml @@ -12,7 +12,7 @@ <?php $_skipSaleableCheck = $this->helper(Magento\Catalog\Helper\Product::class)->getSkipSaleableCheck(); ?> <?php list($_defaultQty, $_canChangeQty) = $block->getDefaultValues(); ?> -<div class="field admin__field options<?php if ($_option->getRequired()) { echo ' required'; } ?>"> +<div class="field admin__field options<?php if ($_option->getRequired()) { echo ' _required'; } ?>"> <label class="label admin__field-label"><span><?= $block->escapeHtml($_option->getTitle()) ?></span></label> <div class="control admin__field-control"> <div class="nested<?php if ($_option->getDecoratedIsLast()) :?> last<?php endif; ?>"> @@ -39,7 +39,7 @@ <?php foreach ($_selections as $_selection) : ?> <div class="field choice admin__field admin__field-option"> <input type="radio" - class="radio admin__control-radio <?= $_option->getRequired() ? ' validate-one-required-by-name' : '' ?> change-container-classname" + class="radio admin__control-radio <?= $_option->getRequired() ? ' required-entry' : '' ?> change-container-classname" id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>" name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" <?php if ($block->isSelected($_selection)) { echo ' checked="checked"'; } ?> @@ -66,7 +66,7 @@ </label> <div class="control admin__field-control"><input <?php if (!$_canChangeQty) { echo ' disabled="disabled"'; } ?> id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-qty-input" - class="input-text admin__control-text qty<?php if (!$_canChangeQty) { echo ' qty-disabled'; } ?>" + class="input-text admin__control-text qty validate-greater-than-zero<?php if (!$_canChangeQty) { echo ' qty-disabled'; } ?>" type="text" name="bundle_option_qty[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" value="<?= $block->escapeHtmlAttr($_defaultQty) ?>" /> diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/select.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/select.phtml index fbb7f7fbb7b38..047e25a65af2b 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/select.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/select.phtml @@ -12,7 +12,7 @@ <?php $_skipSaleableCheck = $this->helper(Magento\Catalog\Helper\Product::class)->getSkipSaleableCheck(); ?> <?php list($_defaultQty, $_canChangeQty) = $block->getDefaultValues(); ?> -<div class="field admin__field option<?php if ($_option->getDecoratedIsLast()) :?> last<?php endif; ?><?php if ($_option->getRequired()) { echo ' required _required'; } ?>"> +<div class="field admin__field option<?php if ($_option->getDecoratedIsLast()) :?> last<?php endif; ?><?php if ($_option->getRequired()) { echo ' _required'; } ?>"> <label class="label admin__field-label"><span><?= $block->escapeHtml($_option->getTitle()) ?></span></label> <div class="control admin__field-control"> <?php if ($block->showSingle()) : ?> @@ -49,7 +49,7 @@ <div class="control admin__field-control"> <input <?php if (!$_canChangeQty) { echo ' disabled="disabled"'; } ?> id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-qty-input" - class="input-text admin__control-text qty<?php if (!$_canChangeQty) { echo ' qty-disabled'; } ?>" + class="input-text admin__control-text qty validate-greater-than-zero<?php if (!$_canChangeQty) { echo ' qty-disabled'; } ?>" type="text" name="bundle_option_qty[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" value="<?= $block->escapeHtmlAttr($_defaultQty) ?>" /> diff --git a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js index 49ee253ad1e88..207a97c270eeb 100644 --- a/app/code/Magento/Bundle/view/base/web/js/price-bundle.js +++ b/app/code/Magento/Bundle/view/base/web/js/price-bundle.js @@ -28,7 +28,8 @@ define([ controlContainer: 'dd', // should be eliminated priceFormat: {}, isFixedPrice: false, - optionTierPricesBlocksSelector: '#option-tier-prices-{1} [data-role="selection-tier-prices"]' + optionTierPricesBlocksSelector: '#option-tier-prices-{1} [data-role="selection-tier-prices"]', + isOptionsInitialized: false }; $.widget('mage.priceBundle', { @@ -53,20 +54,37 @@ define([ priceBox = $(this.options.priceBoxSelector, form), qty = $(this.options.qtyFieldSelector, form); - if (priceBox.data('magePriceBox') && - priceBox.priceBox('option') && - priceBox.priceBox('option').priceConfig - ) { - if (priceBox.priceBox('option').priceConfig.optionTemplate) { - this._setOption('optionTemplate', priceBox.priceBox('option').priceConfig.optionTemplate); + this._updatePriceBox(); + priceBox.on('price-box-initialized', this._updatePriceBox.bind(this)); + options.on('change', this._onBundleOptionChanged.bind(this)); + qty.on('change', this._onQtyFieldChanged.bind(this)); + }, + + /** + * Update price box config with bundle option prices + * @private + */ + _updatePriceBox: function () { + var form = this.element, + options = $(this.options.productBundleSelector, form), + priceBox = $(this.options.priceBoxSelector, form); + + if (!this.options.isOptionsInitialized) { + if (priceBox.data('magePriceBox') && + priceBox.priceBox('option') && + priceBox.priceBox('option').priceConfig + ) { + if (priceBox.priceBox('option').priceConfig.optionTemplate) { //eslint-disable-line max-depth + this._setOption('optionTemplate', priceBox.priceBox('option').priceConfig.optionTemplate); + } + this._setOption('priceFormat', priceBox.priceBox('option').priceConfig.priceFormat); + priceBox.priceBox('setDefault', this.options.optionConfig.prices); + this.options.isOptionsInitialized = true; } - this._setOption('priceFormat', priceBox.priceBox('option').priceConfig.priceFormat); - priceBox.priceBox('setDefault', this.options.optionConfig.prices); + this._applyOptionNodeFix(options); } - this._applyOptionNodeFix(options); - options.on('change', this._onBundleOptionChanged.bind(this)); - qty.on('change', this._onQtyFieldChanged.bind(this)); + return this; }, /** diff --git a/app/code/Magento/BundleGraphQl/Model/Cart/BundleOptionDataProvider.php b/app/code/Magento/BundleGraphQl/Model/Cart/BundleOptionDataProvider.php new file mode 100644 index 0000000000000..5cdfdc88e7dc1 --- /dev/null +++ b/app/code/Magento/BundleGraphQl/Model/Cart/BundleOptionDataProvider.php @@ -0,0 +1,145 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\BundleGraphQl\Model\Cart; + +use Magento\Bundle\Helper\Catalog\Product\Configuration; +use Magento\Catalog\Model\Product; +use Magento\Quote\Model\Quote\Item; +use Magento\Framework\Pricing\Helper\Data; +use Magento\Framework\Serialize\SerializerInterface; + +/** + * Data provider for bundled product options + */ +class BundleOptionDataProvider +{ + /** + * @var Data + */ + private $pricingHelper; + + /** + * @var SerializerInterface + */ + private $serializer; + + /** + * @var Configuration + */ + private $configuration; + + /** + * @param Data $pricingHelper + * @param SerializerInterface $serializer + * @param Configuration $configuration + */ + public function __construct( + Data $pricingHelper, + SerializerInterface $serializer, + Configuration $configuration + ) { + $this->pricingHelper = $pricingHelper; + $this->serializer = $serializer; + $this->configuration = $configuration; + } + + /** + * Extract data for a bundled cart item + * + * @param Item $item + * @return array + */ + public function getData(Item $item): array + { + $options = []; + $product = $item->getProduct(); + + /** @var \Magento\Bundle\Model\Product\Type $typeInstance */ + $typeInstance = $product->getTypeInstance(); + + $optionsQuoteItemOption = $item->getOptionByCode('bundle_option_ids'); + $bundleOptionsIds = $optionsQuoteItemOption + ? $this->serializer->unserialize($optionsQuoteItemOption->getValue()) + : []; + + if ($bundleOptionsIds) { + /** @var \Magento\Bundle\Model\ResourceModel\Option\Collection $optionsCollection */ + $optionsCollection = $typeInstance->getOptionsByIds($bundleOptionsIds, $product); + + $selectionsQuoteItemOption = $item->getOptionByCode('bundle_selection_ids'); + + $bundleSelectionIds = $this->serializer->unserialize($selectionsQuoteItemOption->getValue()); + + if (!empty($bundleSelectionIds)) { + $selectionsCollection = $typeInstance->getSelectionsByIds($bundleSelectionIds, $product); + $bundleOptions = $optionsCollection->appendSelections($selectionsCollection, true); + + $options = $this->buildBundleOptions($bundleOptions, $item); + } + } + + return $options; + } + + /** + * Build bundle product options based on current selection + * + * @param \Magento\Bundle\Model\Option[] $bundleOptions + * @param Item $item + * @return array + */ + private function buildBundleOptions(array $bundleOptions, Item $item): array + { + $options = []; + foreach ($bundleOptions as $bundleOption) { + if (!$bundleOption->getSelections()) { + continue; + } + + $options[] = [ + 'id' => $bundleOption->getId(), + 'label' => $bundleOption->getTitle(), + 'type' => $bundleOption->getType(), + 'values' => $this->buildBundleOptionValues($bundleOption->getSelections(), $item), + ]; + } + + return $options; + } + + /** + * Build bundle product option values based on current selection + * + * @param Product[] $selections + * @param Item $item + * @return array + */ + private function buildBundleOptionValues(array $selections, Item $item): array + { + $values = []; + + $product = $item->getProduct(); + foreach ($selections as $selection) { + $qty = (float) $this->configuration->getSelectionQty($product, $selection->getSelectionId()); + if (!$qty) { + continue; + } + + $selectionPrice = $this->configuration->getSelectionFinalPrice($item, $selection); + + $values[] = [ + 'id' => $selection->getSelectionId(), + 'label' => $selection->getName(), + 'quantity' => $qty, + 'price' => $this->pricingHelper->currency($selectionPrice, false, false), + ]; + } + + return $values; + } +} diff --git a/app/code/Magento/BundleGraphQl/Model/Cart/BuyRequest/BundleDataProvider.php b/app/code/Magento/BundleGraphQl/Model/Cart/BuyRequest/BundleDataProvider.php new file mode 100644 index 0000000000000..37a9309092166 --- /dev/null +++ b/app/code/Magento/BundleGraphQl/Model/Cart/BuyRequest/BundleDataProvider.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\BundleGraphQl\Model\Cart\BuyRequest; + +use Magento\Framework\Stdlib\ArrayManager; +use Magento\QuoteGraphQl\Model\Cart\BuyRequest\BuyRequestDataProviderInterface; + +/** + * Data provider for bundle product buy requests + */ +class BundleDataProvider implements BuyRequestDataProviderInterface +{ + /** + * @var ArrayManager + */ + private $arrayManager; + + /** + * @param ArrayManager $arrayManager + */ + public function __construct( + ArrayManager $arrayManager + ) { + $this->arrayManager = $arrayManager; + } + + /** + * @inheritdoc + */ + public function execute(array $cartItemData): array + { + $bundleOptions = []; + $bundleInputs = $this->arrayManager->get('bundle_options', $cartItemData) ?? []; + foreach ($bundleInputs as $bundleInput) { + $bundleOptions['bundle_option'][$bundleInput['id']] = $bundleInput['value']; + $bundleOptions['bundle_option_qty'][$bundleInput['id']] = $bundleInput['quantity']; + } + + return $bundleOptions; + } +} diff --git a/app/code/Magento/BundleGraphQl/Model/Resolver/BundleOption.php b/app/code/Magento/BundleGraphQl/Model/Resolver/BundleOption.php new file mode 100644 index 0000000000000..6b64310fcb1e3 --- /dev/null +++ b/app/code/Magento/BundleGraphQl/Model/Resolver/BundleOption.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\BundleGraphQl\Model\Resolver; + +use Magento\BundleGraphQl\Model\Cart\BundleOptionDataProvider; +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; + +/** + * Resolver for bundle product options + */ +class BundleOption implements ResolverInterface +{ + /** + * @var BundleOptionDataProvider + */ + private $dataProvider; + + /** + * @param BundleOptionDataProvider $bundleOptionDataProvider + */ + public function __construct( + BundleOptionDataProvider $bundleOptionDataProvider + ) { + $this->dataProvider = $bundleOptionDataProvider; + } + + /** + * @inheritdoc + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (!isset($value['model'])) { + throw new GraphQlInputException(__('Value must contain "model" property.')); + } + return $this->dataProvider->getData($value['model']); + } +} diff --git a/app/code/Magento/BundleGraphQl/composer.json b/app/code/Magento/BundleGraphQl/composer.json index db85c2149ec18..74149f500df8e 100644 --- a/app/code/Magento/BundleGraphQl/composer.json +++ b/app/code/Magento/BundleGraphQl/composer.json @@ -7,6 +7,8 @@ "magento/module-catalog": "*", "magento/module-bundle": "*", "magento/module-catalog-graph-ql": "*", + "magento/module-quote": "*", + "magento/module-quote-graph-ql": "*", "magento/module-store": "*", "magento/framework": "*" }, diff --git a/app/code/Magento/BundleGraphQl/etc/di.xml b/app/code/Magento/BundleGraphQl/etc/di.xml index 4f41f3cb8dc80..15acad7c6bf06 100644 --- a/app/code/Magento/BundleGraphQl/etc/di.xml +++ b/app/code/Magento/BundleGraphQl/etc/di.xml @@ -16,4 +16,11 @@ </argument> </arguments> </type> + <type name="Magento\QuoteGraphQl\Model\Resolver\CartItemTypeResolver"> + <arguments> + <argument name="supportedTypes" xsi:type="array"> + <item name="bundle" xsi:type="string">BundleCartItem</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/BundleGraphQl/etc/graphql/di.xml b/app/code/Magento/BundleGraphQl/etc/graphql/di.xml index 98dbe012c9002..b847a6672e046 100644 --- a/app/code/Magento/BundleGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/BundleGraphQl/etc/graphql/di.xml @@ -13,6 +13,13 @@ </argument> </arguments> </type> + <type name="Magento\QuoteGraphQl\Model\Cart\BuyRequest\BuyRequestBuilder"> + <arguments> + <argument name="providers" xsi:type="array"> + <item name="bundle" xsi:type="object">Magento\BundleGraphQl\Model\Cart\BuyRequest\BundleDataProvider</item> + </argument> + </arguments> + </type> <type name="Magento\Framework\GraphQl\Schema\Type\Enum\DefaultDataMapper"> <arguments> <argument name="map" xsi:type="array"> diff --git a/app/code/Magento/BundleGraphQl/etc/module.xml b/app/code/Magento/BundleGraphQl/etc/module.xml index 352a46d7c171e..8d6725054867e 100644 --- a/app/code/Magento/BundleGraphQl/etc/module.xml +++ b/app/code/Magento/BundleGraphQl/etc/module.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Magento_BundleGraphQl" > <sequence> + <module name="Magento_QuoteGraphQl"/> <module name="Magento_Catalog"/> <module name="Magento_BundleProduct"/> <module name="Magento_Store"/> diff --git a/app/code/Magento/BundleGraphQl/etc/schema.graphqls b/app/code/Magento/BundleGraphQl/etc/schema.graphqls index 74e21d3feaba2..0eff0e086180e 100644 --- a/app/code/Magento/BundleGraphQl/etc/schema.graphqls +++ b/app/code/Magento/BundleGraphQl/etc/schema.graphqls @@ -1,6 +1,50 @@ # Copyright © Magento, Inc. All rights reserved. # See COPYING.txt for license details. +type Mutation { + addBundleProductsToCart(input: AddBundleProductsToCartInput): AddBundleProductsToCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddSimpleProductsToCart") +} + +input AddBundleProductsToCartInput { + cart_id: String! + cart_items: [BundleProductCartItemInput!]! +} + +input BundleProductCartItemInput { + data: CartItemInput! + bundle_options:[BundleOptionInput!]! + customizable_options:[CustomizableOptionInput!] +} + +input BundleOptionInput { + id: Int! + quantity: Float! + value: [String!]! +} + +type AddBundleProductsToCartOutput { + cart: Cart! +} + +type BundleCartItem implements CartItemInterface { + customizable_options: [SelectedCustomizableOption]! @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\CustomizableOptions") + bundle_options: [SelectedBundleOption!]! @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\BundleOption") +} + +type SelectedBundleOption { + id: Int! + label: String! + type: String! + values: [SelectedBundleOptionValue!]! +} + +type SelectedBundleOptionValue { + id: Int! + label: String! + quantity: Float! + price: Float! +} + type BundleItem @doc(description: "BundleItem defines an individual item in a bundle product.") { option_id: Int @doc(description: "An ID assigned to each type of item in a bundle product.") title: String @doc(description: "The display name of the item.") diff --git a/app/code/Magento/BundleImportExport/registration.php b/app/code/Magento/BundleImportExport/registration.php index 2f68e2e05c036..db96b4d9dd470 100644 --- a/app/code/Magento/BundleImportExport/registration.php +++ b/app/code/Magento/BundleImportExport/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_BundleImportExport', __DIR__); diff --git a/app/code/Magento/CacheInvalidate/registration.php b/app/code/Magento/CacheInvalidate/registration.php index 5910edade1092..1c1ac92e330b0 100644 --- a/app/code/Magento/CacheInvalidate/registration.php +++ b/app/code/Magento/CacheInvalidate/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CacheInvalidate', __DIR__); diff --git a/app/code/Magento/Captcha/Test/Mftf/Section/CaptchaFormsDisplayingSection.xml b/app/code/Magento/Captcha/Test/Mftf/Section/CaptchaFormsDisplayingSection.xml index 4c974e6fced05..9103c4191544c 100644 --- a/app/code/Magento/Captcha/Test/Mftf/Section/CaptchaFormsDisplayingSection.xml +++ b/app/code/Magento/Captcha/Test/Mftf/Section/CaptchaFormsDisplayingSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CaptchaFormsDisplayingSection"> <element name="store" type="button" selector="#menu-magento-backend-stores"/> <element name="config" type="button" selector="//li[@data-ui-id='menu-magento-config-system-config']//span"/> diff --git a/app/code/Magento/Captcha/Test/Unit/CustomerData/CaptchaTest.php b/app/code/Magento/Captcha/Test/Unit/CustomerData/CaptchaTest.php new file mode 100644 index 0000000000000..a791039fe27f9 --- /dev/null +++ b/app/code/Magento/Captcha/Test/Unit/CustomerData/CaptchaTest.php @@ -0,0 +1,98 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Captcha\Test\Unit\CustomerData; + +use Magento\Captcha\Helper\Data as CaptchaHelper; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Captcha\CustomerData\Captcha; +use Magento\Captcha\Model\DefaultModel; +use Magento\Customer\Api\Data\CustomerInterface as CustomerData; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use PHPUnit\Framework\TestCase; + +class CaptchaTest extends TestCase +{ + /** + * @var CaptchaHelper|\PHPUnit_Framework_MockObject_MockObject + */ + private $helperMock; + + /** + * @var CustomerSession|\PHPUnit_Framework_MockObject_MockObject + */ + private $customerSessionMock; + + /** + * @var Captcha + */ + private $model; + + /** + * @var array + */ + private $formIds; + + /** + * @var ObjectManagerHelper + */ + protected $objectManagerHelper; + + /** + * Create mocks and model + */ + protected function setUp() + { + $this->helperMock = $this->createMock(CaptchaHelper::class); + $this->customerSessionMock = $this->createMock(CustomerSession::class); + $this->formIds = [ + 'user_login' + ]; + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->model = $this->objectManagerHelper->getObject( + Captcha::class, + [ + 'helper' => $this->helperMock, + 'formIds' => $this->formIds, + 'customerSession' => $this->customerSessionMock + ] + ); + } + + /** + * Test getSectionData() when user is login and require captcha + */ + public function testGetSectionDataWhenLoginAndRequireCaptcha() + { + $emailLogin = 'test@localhost.com'; + + $userLoginModel = $this->createMock(DefaultModel::class); + $userLoginModel->expects($this->any())->method('isRequired')->with($emailLogin) + ->willReturn(true); + $this->helperMock->expects($this->any())->method('getCaptcha')->with('user_login')->willReturn($userLoginModel); + + $this->customerSessionMock->expects($this->any())->method('isLoggedIn') + ->willReturn(true); + + $customerDataMock = $this->createMock(CustomerData::class); + $customerDataMock->expects($this->any())->method('getEmail')->willReturn($emailLogin); + $this->customerSessionMock->expects($this->any())->method('getCustomerData') + ->willReturn($customerDataMock); + + /* Assert to test */ + $this->assertEquals( + [ + "user_login" => [ + "isRequired" => true, + "timestamp" => time() + ] + ], + $this->model->getSectionData() + ); + } +} diff --git a/app/code/Magento/Captcha/Test/Unit/Model/Config/FontTest.php b/app/code/Magento/Captcha/Test/Unit/Model/Config/FontTest.php new file mode 100644 index 0000000000000..42ab3146f1321 --- /dev/null +++ b/app/code/Magento/Captcha/Test/Unit/Model/Config/FontTest.php @@ -0,0 +1,101 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Captcha\Test\Unit\Model\Config; + +use PHPUnit\Framework\TestCase; +use Magento\Captcha\Helper\Data as HelperData; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Captcha\Model\Config\Font; + +class FontTest extends TestCase +{ + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var Font + */ + private $model; + + /** + * @var HelperData|\PHPUnit_Framework_MockObject_MockObject + */ + private $helperDataMock; + + /** + * Setup Environment For Testing + */ + protected function setUp() + { + $this->helperDataMock = $this->createMock(HelperData::class); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->model = $this->objectManagerHelper->getObject( + Font::class, + [ + 'captchaData' => $this->helperDataMock + ] + ); + } + + /** + * Test toOptionArray() with data provider below + * + * @param array $fonts + * @param array $expectedResult + * @dataProvider toOptionArrayDataProvider + */ + public function testToOptionArray($fonts, $expectedResult) + { + $this->helperDataMock->expects($this->any())->method('getFonts') + ->willReturn($fonts); + + $this->assertEquals($expectedResult, $this->model->toOptionArray()); + } + + /** + * Data Provider for testing toOptionArray() + * + * @return array + */ + public function toOptionArrayDataProvider() + { + return [ + 'Empty get font' => [ + [], + [] + ], + 'Get font result' => [ + [ + 'arial' => [ + 'label' => 'Arial', + 'path' => '/www/magento/fonts/arial.ttf' + ], + 'verdana' => [ + 'label' => 'Verdana', + 'path' => '/www/magento/fonts/verdana.ttf' + ] + ], + [ + [ + 'label' => 'Arial', + 'value' => 'arial' + ], + [ + 'label' => 'Verdana', + 'value' => 'verdana' + ] + ] + ] + ]; + } +} diff --git a/app/code/Magento/Captcha/Test/Unit/Model/Config/Form/BackendTest.php b/app/code/Magento/Captcha/Test/Unit/Model/Config/Form/BackendTest.php new file mode 100644 index 0000000000000..054cc71af61bc --- /dev/null +++ b/app/code/Magento/Captcha/Test/Unit/Model/Config/Form/BackendTest.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Captcha\Test\Unit\Model\Config\Form; + +use Magento\Captcha\Model\Config\Form\Backend; +use PHPUnit\Framework\TestCase; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class BackendTest extends TestCase +{ + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var Backend + */ + private $model; + + /** + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $configMock; + + /** + * Setup Environment For Testing + */ + protected function setUp() + { + $this->configMock = $this->createMock(ScopeConfigInterface::class); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->model = $this->objectManagerHelper->getObject( + Backend::class, + [ + 'config' => $this->configMock + ] + ); + } + + /** + * Test toOptionArray() with data provider below + * + * @param string|array $config + * @param array $expectedResult + * @dataProvider toOptionArrayDataProvider + */ + public function testToOptionArray($config, $expectedResult) + { + $this->configMock->expects($this->any())->method('getValue') + ->with('captcha/backend/areas', 'default') + ->willReturn($config); + + $this->assertEquals($expectedResult, $this->model->toOptionArray()); + } + + /** + * Data Provider for testing toOptionArray() + * + * @return array + */ + public function toOptionArrayDataProvider() + { + return [ + 'Empty captcha backend areas' => [ + '', + [] + ], + 'With two captcha backend area' => [ + [ + 'backend_login' => [ + 'label' => 'Admin Login' + ], + 'backend_forgotpassword' => [ + 'label' => 'Admin Forgot Password' + ] + ], + [ + [ + 'label' => 'Admin Login', + 'value' => 'backend_login' + ], + [ + 'label' => 'Admin Forgot Password', + 'value' => 'backend_forgotpassword' + ] + ] + ] + ]; + } +} diff --git a/app/code/Magento/Captcha/Test/Unit/Model/Config/Form/FrontendTest.php b/app/code/Magento/Captcha/Test/Unit/Model/Config/Form/FrontendTest.php new file mode 100644 index 0000000000000..d3f40f5872a7d --- /dev/null +++ b/app/code/Magento/Captcha/Test/Unit/Model/Config/Form/FrontendTest.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Captcha\Test\Unit\Model\Config\Form; + +use Magento\Captcha\Model\Config\Form\Frontend; +use PHPUnit\Framework\TestCase; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class FrontendTest extends TestCase +{ + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var Frontend + */ + private $model; + + /** + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $configMock; + + /** + * Setup Environment For Testing + */ + protected function setUp() + { + $this->configMock = $this->createMock(ScopeConfigInterface::class); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->model = $this->objectManagerHelper->getObject( + Frontend::class, + [ + 'config' => $this->configMock + ] + ); + } + + /** + * Test toOptionArray() with data provider below + * + * @param string|array $config + * @param array $expectedResult + * @dataProvider toOptionArrayDataProvider + */ + public function testToOptionArray($config, $expectedResult) + { + $this->configMock->expects($this->any())->method('getValue') + ->with('captcha/frontend/areas', 'default') + ->willReturn($config); + + $this->assertEquals($expectedResult, $this->model->toOptionArray()); + } + + /** + * Data Provider for testing toOptionArray() + * + * @return array + */ + public function toOptionArrayDataProvider() + { + return [ + 'Empty captcha frontend areas' => [ + '', + [] + ], + 'With two captcha frontend area' => [ + [ + 'product_sendtofriend_form' => [ + 'label' => 'Send To Friend Form' + ], + 'sales_rule_coupon_request' => [ + 'label' => 'Applying coupon code' + ] + ], + [ + [ + 'label' => 'Send To Friend Form', + 'value' => 'product_sendtofriend_form' + ], + [ + 'label' => 'Applying coupon code', + 'value' => 'sales_rule_coupon_request' + ] + ] + ] + ]; + } +} diff --git a/app/code/Magento/Captcha/registration.php b/app/code/Magento/Captcha/registration.php index d6c49c719c969..6721e4abcec61 100644 --- a/app/code/Magento/Captcha/registration.php +++ b/app/code/Magento/Captcha/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Captcha', __DIR__); diff --git a/app/code/Magento/CardinalCommerce/Test/Mftf/Page/AdminThreeDSecurePage.xml b/app/code/Magento/CardinalCommerce/Test/Mftf/Page/AdminThreeDSecurePage.xml new file mode 100644 index 0000000000000..dae6869dbfe79 --- /dev/null +++ b/app/code/Magento/CardinalCommerce/Test/Mftf/Page/AdminThreeDSecurePage.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="AdminThreeDSecurePage" url="admin/system_config/edit/section/three_d_secure/" area="admin" module="Magento_CardinalCommerce"> + <section name="AdminCardinalCommerceSection"/> + </page> +</pages> diff --git a/app/code/Magento/CardinalCommerce/Test/Mftf/Section/AdminCardinalCommerceSection.xml b/app/code/Magento/CardinalCommerce/Test/Mftf/Section/AdminCardinalCommerceSection.xml new file mode 100644 index 0000000000000..1016fbaefb0ab --- /dev/null +++ b/app/code/Magento/CardinalCommerce/Test/Mftf/Section/AdminCardinalCommerceSection.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="AdminCardinalCommerceSection"> + <element name="head" type="button" selector="#three_d_secure_cardinal_config-link"/> + <element name="enabled" type="input" selector="#three_d_secure_cardinal_config_enabled_authorize"/> + <element name="environment" type="input" selector="#three_d_secure_cardinal_config_environment"/> + </section> +</sections> diff --git a/app/code/Magento/CardinalCommerce/Test/Mftf/Test/AdminCardinalCommerceSettingsHiddenTest.xml b/app/code/Magento/CardinalCommerce/Test/Mftf/Test/AdminCardinalCommerceSettingsHiddenTest.xml new file mode 100644 index 0000000000000..a41b96f0db6e4 --- /dev/null +++ b/app/code/Magento/CardinalCommerce/Test/Mftf/Test/AdminCardinalCommerceSettingsHiddenTest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCardinalCommerceSettingsHiddenTest"> + <annotations> + <features value="CardinalCommerce"/> + <title value="CardinalCommerce settings hidden" /> + <description value="CardinalCommerce config shouldn't be visible if the 3D secure is disabled for Authorize.Net."/> + <severity value="MINOR"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <magentoCLI command="config:set three_d_secure/cardinal/enabled_authorizenet 1" stepKey="enableCardinalCommerce"/> + </before> + + <after> + <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI command="config:set three_d_secure/cardinal/enabled_authorizenet 0" stepKey="disableCardinalCommerce"/> + </after> + + <amOnPage url="{{AdminThreeDSecurePage.url}}" stepKey="openCurrencyOptionsPage" /> + <conditionalClick dependentSelector="{{AdminCardinalCommerceSection.enabled}}" visible="false" selector="{{AdminCardinalCommerceSection.head}}" stepKey="openCollapsibleBlock"/> + <see selector="{{AdminCardinalCommerceSection.environment}}" userInput="Production" stepKey="seeEnvironmentProduction"/> + <selectOption selector="{{AdminCardinalCommerceSection.enabled}}" userInput="0" stepKey="disableCardinalCommerceOption"/> + <dontSeeElement selector="{{AdminCardinalCommerceSection.environment}}" stepKey="dontSeeEnvironmentProduction"/> + </test> +</tests> diff --git a/app/code/Magento/CardinalCommerce/Test/Unit/Model/Response/JwtParserTest.php b/app/code/Magento/CardinalCommerce/Test/Unit/Model/Response/JwtParserTest.php new file mode 100644 index 0000000000000..7c17c4e2e87d5 --- /dev/null +++ b/app/code/Magento/CardinalCommerce/Test/Unit/Model/Response/JwtParserTest.php @@ -0,0 +1,131 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CardinalCommerce\Test\Unit\Model\Response; + +use Magento\CardinalCommerce\Model\Response\JwtParser; +use Magento\CardinalCommerce\Model\Config; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\CardinalCommerce\Model\JwtManagement; +use Magento\CardinalCommerce\Model\Response\JwtPayloadValidatorInterface; +use Magento\Framework\Exception\LocalizedException; + +/** + * Class \Magento\CardinalCommerce\Test\Unit\Model\Response\JwtParserTest + */ +class JwtParserTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var JwtParser + */ + private $model; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject | Config + */ + private $configMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject | JwtManagement + */ + private $jwtManagementMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject | JwtPayloadValidatorInterface + */ + private $jwtPayloadValidatorMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = new ObjectManager($this); + + $this->configMock = $this->getMockBuilder(Config::class) + ->setMethods(['getApiKey', 'isDebugModeEnabled']) + ->disableOriginalConstructor() + ->getMock(); + + $this->jwtManagementMock = $this->getMockBuilder(JwtManagement::class) + ->setMethods(['decode']) + ->disableOriginalConstructor() + ->getMock(); + + $this->jwtPayloadValidatorMock = $this->getMockBuilder(JwtPayloadValidatorInterface::class) + ->setMethods(['validate']) + ->disableOriginalConstructor() + ->getMock(); + + $this->model = $this->objectManager->getObject( + JwtParser::class, + [ + 'jwtManagement' => $this->jwtManagementMock, + 'config' => $this->configMock, + 'tokenValidator' => $this->jwtPayloadValidatorMock + ] + ); + + $this->configMock->expects($this->any()) + ->method('getApiKey') + ->willReturn('API Key'); + + $this->configMock->expects($this->any()) + ->method('isDebugModeEnabled') + ->willReturn(false); + + $this->jwtManagementMock->expects($this->any()) + ->method('decode') + ->with('string_to_test', 'API Key') + ->willReturn(['mockResult' => 'jwtPayload']); + } + + /** + * Tests Jwt Parser execute with the result and no exception. + */ + public function testExecuteWithNoException() + { + /* Validate Success */ + $this->jwtPayloadValidatorMock->expects($this->any()) + ->method('validate') + ->with(['mockResult' => 'jwtPayload']) + ->willReturn(true); + + /* Assert the result of function */ + $jwtPayload = $this->model->execute('string_to_test'); + $this->assertEquals( + ['mockResult' => 'jwtPayload'], + $jwtPayload + ); + } + + /** + * Tests Jwt Parser execute with exception and no result. + */ + public function testExecuteWithException() + { + /* Validate Fail */ + $this->jwtPayloadValidatorMock->expects($this->any()) + ->method('validate') + ->with(['mockResult' => 'jwtPayload']) + ->willReturn(false); + + $this->expectException(LocalizedException::class); + $this->expectExceptionMessage( + 'Authentication Failed. Your card issuer cannot authenticate this card. ' . + 'Please select another card or form of payment to complete your purchase.' + ); + + /* Execute function */ + $this->model->execute('string_to_test'); + } +} diff --git a/app/code/Magento/CardinalCommerce/etc/adminhtml/system.xml b/app/code/Magento/CardinalCommerce/etc/adminhtml/system.xml index 532fcdd0f598f..046475baba676 100644 --- a/app/code/Magento/CardinalCommerce/etc/adminhtml/system.xml +++ b/app/code/Magento/CardinalCommerce/etc/adminhtml/system.xml @@ -19,26 +19,41 @@ <label>Environment</label> <source_model>Magento\CardinalCommerce\Model\Adminhtml\Source\Environment</source_model> <config_path>three_d_secure/cardinal/environment</config_path> + <depends> + <field id="enabled_authorize">1</field> + </depends> </field> <field id="org_unit_id" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Org Unit Id</label> <config_path>three_d_secure/cardinal/org_unit_id</config_path> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> + <depends> + <field id="enabled_authorize">1</field> + </depends> </field> <field id="api_key" translate="label" type="obscure" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0"> <label>API Key</label> <config_path>three_d_secure/cardinal/api_key</config_path> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> + <depends> + <field id="enabled_authorize">1</field> + </depends> </field> <field id="api_identifier" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> <label>API Identifier</label> <config_path>three_d_secure/cardinal/api_identifier</config_path> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> + <depends> + <field id="enabled_authorize">1</field> + </depends> </field> <field id="debug" translate="label" type="select" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Debug</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>three_d_secure/cardinal/debug</config_path> + <depends> + <field id="enabled_authorize">1</field> + </depends> </field> </group> </group> diff --git a/app/code/Magento/CardinalCommerce/registration.php b/app/code/Magento/CardinalCommerce/registration.php index 26fb168fb0ae2..714c7692cf4b5 100644 --- a/app/code/Magento/CardinalCommerce/registration.php +++ b/app/code/Magento/CardinalCommerce/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CardinalCommerce', __DIR__); diff --git a/app/code/Magento/Catalog/Api/Data/ProductRender/FormattedPriceInfoInterface.php b/app/code/Magento/Catalog/Api/Data/ProductRender/FormattedPriceInfoInterface.php index 43e0de4f20176..d111de1b04b94 100644 --- a/app/code/Magento/Catalog/Api/Data/ProductRender/FormattedPriceInfoInterface.php +++ b/app/code/Magento/Catalog/Api/Data/ProductRender/FormattedPriceInfoInterface.php @@ -29,6 +29,7 @@ public function getFinalPrice(); /** * Set the final price: usually it calculated as minimal price of the product + * * Can be different depends on type of product * * @param string $finalPrice @@ -39,6 +40,7 @@ public function setFinalPrice($finalPrice); /** * Retrieve max price of a product + * * E.g. for product with custom options is price with the most expensive custom option * * @return string @@ -57,6 +59,7 @@ public function setMaxPrice($maxPrice); /** * Retrieve the minimal price of the product or variation + * * The minimal price is for example, the lowest price of all variations for complex product * * @return string @@ -66,7 +69,7 @@ public function getMinimalPrice(); /** * Set max regular price - * Max regular price is the same, as maximum price, except of excluding calculating special price and catalogules + * Max regular price is the same, as maximum price, except of excluding calculating special price and catalog rules * in it * * @param string $maxRegularPrice @@ -130,6 +133,7 @@ public function setMinimalPrice($minimalPrice); /** * Regular price - is price of product without discounts and special price with taxes and fixed product tax + * * Usually this price is corresponding to price in admin panel of product * * @return string diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php b/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php index 9a4a9fa768006..929c181bf820c 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Category/Tree.php @@ -407,6 +407,7 @@ protected function _getNodeJson($node, $level = 0) public function buildNodeName($node) { $result = $this->escapeHtml($node->getName()); + $result .= ' (ID: ' . $node->getId() . ')'; if ($this->_withProductCount) { $result .= ' (' . $node->getProductCount() . ')'; } diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php index 1b6756968662f..89239a2e3e608 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Advanced.php @@ -7,16 +7,20 @@ namespace Magento\Catalog\Block\Adminhtml\Product\Attribute\Edit\Tab; use Magento\Backend\Block\Widget\Form\Generic; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; use Magento\Config\Model\Config\Source\Yesno; use Magento\Eav\Block\Adminhtml\Attribute\PropertyLocker; use Magento\Eav\Helper\Data; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Stdlib\DateTime; /** - * Product attribute add/edit form main tab + * Product attribute add/edit advanced form tab * * @api * @since 100.0.2 + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Advanced extends Generic { @@ -70,7 +74,7 @@ public function __construct( * Adding product form elements for editing attribute * * @return $this - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException * @SuppressWarnings(PHPMD) */ protected function _prepareForm() @@ -139,7 +143,21 @@ protected function _prepareForm() 'label' => __('Default Value'), 'title' => __('Default Value'), 'value' => $attributeObject->getDefaultValue(), - 'date_format' => $dateFormat + 'date_format' => $dateFormat, + ] + ); + + $timeFormat = $this->_localeDate->getTimeFormat(\IntlDateFormatter::SHORT); + $fieldset->addField( + 'default_value_datetime', + 'date', + [ + 'name' => 'default_value_datetime', + 'label' => __('Default Value'), + 'title' => __('Default Value'), + 'value' => $this->getLocalizedDateDefaultValue(), + 'date_format' => $dateFormat, + 'time_format' => $timeFormat, ] ); @@ -266,7 +284,7 @@ protected function _initFormValues() /** * Retrieve attribute object from registry * - * @return mixed + * @return Attribute */ private function getAttributeObject() { @@ -285,4 +303,28 @@ private function getPropertyLocker() } return $this->propertyLocker; } + + /** + * Get localized date default value + * + * @return string + * @throws LocalizedException + */ + private function getLocalizedDateDefaultValue(): string + { + $attributeObject = $this->getAttributeObject(); + if (empty($attributeObject->getDefaultValue()) || $attributeObject->getFrontendInput() !== 'datetime') { + return (string)$attributeObject->getDefaultValue(); + } + + try { + $localizedDate = $this->_localeDate->date($attributeObject->getDefaultValue(), null, false); + $localizedDate->setTimezone(new \DateTimeZone($this->_localeDate->getConfigTimezone())); + $localizedDate = $localizedDate->format(DateTime::DATETIME_PHP_FORMAT); + } catch (\Exception $e) { + throw new LocalizedException(__('The default date is invalid.')); + } + + return $localizedDate; + } } diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Main.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Main.php index 85cf37a1214b5..955ea259ec9a8 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Main.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Attribute/Edit/Tab/Main.php @@ -7,60 +7,60 @@ /** * Product attribute add/edit form main tab * - * @author Magento Core Team <core@magentocommerce.com> + * @author Magento Core Team <core@magentocommerce.com> */ namespace Magento\Catalog\Block\Adminhtml\Product\Attribute\Edit\Tab; +use Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Apply as HelperApply; use Magento\Eav\Block\Adminhtml\Attribute\Edit\Main\AbstractMain; +use Magento\Framework\Data\Form\Element\AbstractElement; +use Magento\Framework\Data\Form\Element\Fieldset; +use Magento\Framework\DataObject; /** + * Product attribute add/edit form main tab + * * @api - * @SuppressWarnings(PHPMD.DepthOfInheritance) * @since 100.0.2 */ class Main extends AbstractMain { /** - * Adding product form elements for editing attribute - * - * @return $this - * @SuppressWarnings(PHPMD.UnusedLocalVariable) + * @inheritdoc */ protected function _prepareForm() { parent::_prepareForm(); - /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attributeObject */ - $attributeObject = $this->getAttributeObject(); - /* @var $form \Magento\Framework\Data\Form */ - $form = $this->getForm(); - /* @var $fieldset \Magento\Framework\Data\Form\Element\Fieldset */ - $fieldset = $form->getElement('base_fieldset'); - $fieldsToRemove = ['attribute_code', 'is_unique', 'frontend_class']; - foreach ($fieldset->getElements() as $element) { - /** @var \Magento\Framework\Data\Form\AbstractForm $element */ - if (substr($element->getId(), 0, strlen('default_value')) == 'default_value') { - $fieldsToRemove[] = $element->getId(); - } - } - foreach ($fieldsToRemove as $id) { - $fieldset->removeField($id); - } + $this->removeUnusedFields(); + $this->processFrontendInputTypes(); + + $this->_eventManager->dispatch('product_attribute_form_build_main_tab', ['form' => $this->getForm()]); + + return $this; + } + /** + * @inheritdoc + */ + protected function _getAdditionalElementTypes() + { + return ['apply' => HelperApply::class]; + } + + /** + * Process frontend input types for product attributes + * + * @return void + */ + private function processFrontendInputTypes(): void + { + $form = $this->getForm(); + /** @var AbstractElement $frontendInputElm */ $frontendInputElm = $form->getElement('frontend_input'); - $additionalTypes = [ - ['value' => 'price', 'label' => __('Price')], - ['value' => 'media_image', 'label' => __('Media Image')], - ]; - $additionalReadOnlyTypes = ['gallery' => __('Gallery')]; - if (isset($additionalReadOnlyTypes[$attributeObject->getFrontendInput()])) { - $additionalTypes[] = [ - 'value' => $attributeObject->getFrontendInput(), - 'label' => $additionalReadOnlyTypes[$attributeObject->getFrontendInput()], - ]; - } + $additionalTypes = $this->getAdditionalFrontendInputTypes(); - $response = new \Magento\Framework\DataObject(); + $response = new DataObject(); $response->setTypes([]); $this->_eventManager->dispatch('adminhtml_product_attribute_types', ['response' => $response]); $_hiddenFields = []; @@ -74,19 +74,51 @@ protected function _prepareForm() $frontendInputValues = array_merge($frontendInputElm->getValues(), $additionalTypes); $frontendInputElm->setValues($frontendInputValues); + } - $this->_eventManager->dispatch('product_attribute_form_build_main_tab', ['form' => $form]); + /** + * Get additional Frontend Input Types for product attributes + * + * @return array + */ + private function getAdditionalFrontendInputTypes(): array + { + $additionalTypes = [ + ['value' => 'price', 'label' => __('Price')], + ['value' => 'media_image', 'label' => __('Media Image')], + ]; - return $this; + $additionalReadOnlyTypes = ['gallery' => __('Gallery')]; + $attributeObject = $this->getAttributeObject(); + if (isset($additionalReadOnlyTypes[$attributeObject->getFrontendInput()])) { + $additionalTypes[] = [ + 'value' => $attributeObject->getFrontendInput(), + 'label' => $additionalReadOnlyTypes[$attributeObject->getFrontendInput()], + ]; + } + + return $additionalTypes; } /** - * Retrieve additional element types for product attributes + * Remove unused form fields * - * @return array + * @return void */ - protected function _getAdditionalElementTypes() + private function removeUnusedFields(): void { - return ['apply' => \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Apply::class]; + $form = $this->getForm(); + /* @var $fieldset Fieldset */ + $fieldset = $form->getElement('base_fieldset'); + $fieldsToRemove = ['attribute_code', 'is_unique', 'frontend_class']; + foreach ($fieldset->getElements() as $element) { + /** @var AbstractElement $element */ + if (substr($element->getId(), 0, strlen('default_value')) === 'default_value') { + $fieldsToRemove[] = $element->getId(); + } + } + foreach ($fieldsToRemove as $id) { + $fieldset->removeField($id); + } } } diff --git a/app/code/Magento/Catalog/Block/Product/View/Options/AbstractOptions.php b/app/code/Magento/Catalog/Block/Product/View/Options/AbstractOptions.php index 030b6e1d2204c..0bfdcc678e9f7 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Options/AbstractOptions.php +++ b/app/code/Magento/Catalog/Block/Product/View/Options/AbstractOptions.php @@ -3,16 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -/** - * Product options abstract type block - * - * @author Magento Core Team <core@magentocommerce.com> - */ +declare(strict_types=1); namespace Magento\Catalog\Block\Product\View\Options; +use Magento\Catalog\Pricing\Price\BasePrice; +use Magento\Catalog\Pricing\Price\CalculateCustomOptionCatalogRule; use Magento\Catalog\Pricing\Price\CustomOptionPriceInterface; +use Magento\Framework\App\ObjectManager; /** * Product options section abstract block. @@ -47,20 +45,29 @@ abstract class AbstractOptions extends \Magento\Framework\View\Element\Template */ protected $_catalogHelper; + /** + * @var CalculateCustomOptionCatalogRule + */ + private $calculateCustomOptionCatalogRule; + /** * @param \Magento\Framework\View\Element\Template\Context $context * @param \Magento\Framework\Pricing\Helper\Data $pricingHelper * @param \Magento\Catalog\Helper\Data $catalogData * @param array $data + * @param CalculateCustomOptionCatalogRule $calculateCustomOptionCatalogRule */ public function __construct( \Magento\Framework\View\Element\Template\Context $context, \Magento\Framework\Pricing\Helper\Data $pricingHelper, \Magento\Catalog\Helper\Data $catalogData, - array $data = [] + array $data = [], + CalculateCustomOptionCatalogRule $calculateCustomOptionCatalogRule = null ) { $this->pricingHelper = $pricingHelper; $this->_catalogHelper = $catalogData; + $this->calculateCustomOptionCatalogRule = $calculateCustomOptionCatalogRule + ?? ObjectManager::getInstance()->get(CalculateCustomOptionCatalogRule::class); parent::__construct($context, $data); } @@ -161,6 +168,15 @@ protected function _formatPrice($value, $flag = true) $priceStr = $sign; $customOptionPrice = $this->getProduct()->getPriceInfo()->getPrice('custom_option_price'); + + if (!$value['is_percent']) { + $value['pricing_value'] = $this->calculateCustomOptionCatalogRule->execute( + $this->getProduct(), + (float)$value['pricing_value'], + (bool)$value['is_percent'] + ); + } + $context = [CustomOptionPriceInterface::CONFIGURATION_OPTION_FLAG => true]; $optionAmount = $customOptionPrice->getCustomAmount($value['pricing_value'], null, $context); $priceStr .= $this->getLayout()->getBlock('product.price.render.default')->renderAmount( diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category.php index f45f854be0761..de8c402c7e761 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category.php @@ -82,7 +82,7 @@ public function __construct( } /** - * Initialize requested category and put it into registry + * Initialize requested category and put it into registry. * * Root category can be returned, if inappropriate store/category is specified * @@ -111,6 +111,8 @@ protected function _initCategory($getRootInstead = false) } } + $this->registry->unregister('category'); + $this->registry->unregister('current_category'); $this->registry->register('category', $category); $this->registry->register('current_category', $category); $this->wysiwigConfig->setStoreId($storeId); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php index ee8abfdb7ab48..9684938d38477 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php @@ -183,29 +183,29 @@ public function execute() $products = json_decode($categoryPostData['category_products'], true); $category->setPostedProducts($products); } - $this->_eventManager->dispatch( - 'catalog_category_prepare_save', - ['category' => $category, 'request' => $this->getRequest()] - ); - /** - * Check "Use Default Value" checkboxes values - */ - if (isset($categoryPostData['use_default']) && !empty($categoryPostData['use_default'])) { - foreach ($categoryPostData['use_default'] as $attributeCode => $attributeValue) { - if ($attributeValue) { - $category->setData($attributeCode, null); + try { + $this->_eventManager->dispatch( + 'catalog_category_prepare_save', + ['category' => $category, 'request' => $this->getRequest()] + ); + /** + * Check "Use Default Value" checkboxes values + */ + if (isset($categoryPostData['use_default']) && !empty($categoryPostData['use_default'])) { + foreach ($categoryPostData['use_default'] as $attributeCode => $attributeValue) { + if ($attributeValue) { + $category->setData($attributeCode, null); + } } } - } - /** - * Proceed with $_POST['use_config'] - * set into category model for processing through validation - */ - $category->setData('use_post_data_config', $useConfig); + /** + * Proceed with $_POST['use_config'] + * set into category model for processing through validation + */ + $category->setData('use_post_data_config', $useConfig); - try { $categoryResource = $category->getResource(); if ($category->hasCustomDesignTo()) { $categoryResource->getAttribute('custom_design_from')->setMaxValue($category->getCustomDesignTo()); @@ -231,10 +231,16 @@ public function execute() $category->save(); $this->messageManager->addSuccessMessage(__('You saved the category.')); + // phpcs:disable Magento2.Exceptions.ThrowCatch } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->messageManager->addExceptionMessage($e); $this->logger->critical($e); $this->_getSession()->setCategoryData($categoryPostData); + // phpcs:disable Magento2.Exceptions.ThrowCatch + } catch (\Throwable $e) { + $this->messageManager->addErrorMessage(__('Something went wrong while saving the category.')); + $this->logger->critical($e); + $this->_getSession()->setCategoryData($categoryPostData); } } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php index ce6668229d658..dcb7074c0d036 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php @@ -252,7 +252,7 @@ private function checkUniqueOption(DataObject $response, array $options = null) private function checkEmptyOption(DataObject $response, array $optionsForCheck = null) { foreach ($optionsForCheck as $optionValues) { - if (isset($optionValues[0]) && $optionValues[0] == '') { + if (isset($optionValues[0]) && trim($optionValues[0]) == '') { $this->setMessageToResponse($response, [__("The value of Admin scope can't be empty.")]); $response->setError(true); } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Builder.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Builder.php index 78ad9f423871f..1f959a22b1ba1 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Builder.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Builder.php @@ -115,6 +115,9 @@ public function build(RequestInterface $request): ProductInterface $store = $this->storeFactory->create(); $store->load($storeId); + $this->registry->unregister('product'); + $this->registry->unregister('current_product'); + $this->registry->unregister('current_store'); $this->registry->register('product', $product); $this->registry->register('current_product', $product); $this->registry->register('current_store', $store); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php index f4c7891d00849..d43b313c43b3e 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Gallery/Upload.php @@ -89,11 +89,6 @@ public function execute() ['fileId' => 'image'] ); $uploader->setAllowedExtensions($this->getAllowedExtensions()); - - if (!$uploader->checkMimeType($this->getAllowedMimeTypes())) { - throw new LocalizedException(__('Disallowed File Type.')); - } - $imageAdapter = $this->adapterFactory->create(); $uploader->addValidateCallback('catalog_product_image', $imageAdapter, 'validateUploadFile'); $uploader->setAllowRenameFiles(true); @@ -133,14 +128,4 @@ private function getAllowedExtensions() { return array_keys($this->allowedMimeTypes); } - - /** - * Get the set of allowed mime types. - * - * @return array - */ - private function getAllowedMimeTypes() - { - return array_values($this->allowedMimeTypes); - } } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php index a29d02f5e545d..2ae97223d6359 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php @@ -6,7 +6,6 @@ namespace Magento\Catalog\Controller\Adminhtml\Product\Initialization; -use DateTime; use Magento\Backend\Helper\Js; use Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory as CustomOptionFactory; use Magento\Catalog\Api\Data\ProductLinkInterfaceFactory as ProductLinkFactory; @@ -15,6 +14,8 @@ use Magento\Catalog\Api\ProductRepositoryInterface\Proxy as ProductRepository; use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper\AttributeFilter; use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Authorization as ProductAuthorization; +use Magento\Catalog\Model\Product\Filter\DateTime as DateTimeFilter; use Magento\Catalog\Model\Product\Initialization\Helper\ProductLinks; use Magento\Catalog\Model\Product\Link\Resolver as LinkResolver; use Magento\Catalog\Model\Product\LinkTypeProvider; @@ -30,6 +31,7 @@ * * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyFields) * @since 100.0.2 */ class Helper @@ -88,11 +90,6 @@ class Helper */ private $linkResolver; - /** - * @var \Magento\Framework\Stdlib\DateTime\Filter\DateTime - */ - private $dateTimeFilter; - /** * @var LinkTypeProvider */ @@ -103,11 +100,21 @@ class Helper */ private $attributeFilter; + /** + * @var ProductAuthorization + */ + private $productAuthorization; + /** * @var FormatInterface */ private $localeFormat; + /** + * @var DateTimeFilter + */ + private $dateTimeFilter; + /** * Constructor * @@ -123,6 +130,8 @@ class Helper * @param LinkTypeProvider|null $linkTypeProvider * @param AttributeFilter|null $attributeFilter * @param FormatInterface|null $localeFormat + * @param ProductAuthorization|null $productAuthorization + * @param DateTimeFilter|null $dateTimeFilter * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -137,7 +146,9 @@ public function __construct( ProductRepositoryInterface $productRepository = null, LinkTypeProvider $linkTypeProvider = null, AttributeFilter $attributeFilter = null, - FormatInterface $localeFormat = null + FormatInterface $localeFormat = null, + ?ProductAuthorization $productAuthorization = null, + ?DateTimeFilter $dateTimeFilter = null ) { $this->request = $request; $this->storeManager = $storeManager; @@ -153,6 +164,8 @@ public function __construct( $this->linkTypeProvider = $linkTypeProvider ?: $objectManager->get(LinkTypeProvider::class); $this->attributeFilter = $attributeFilter ?: $objectManager->get(AttributeFilter::class); $this->localeFormat = $localeFormat ?: $objectManager->get(FormatInterface::class); + $this->productAuthorization = $productAuthorization ?? $objectManager->get(ProductAuthorization::class); + $this->dateTimeFilter = $dateTimeFilter ?? $objectManager->get(DateTimeFilter::class); } /** @@ -176,7 +189,6 @@ public function initializeFromData(Product $product, array $productData) } $productData = $this->normalize($productData); - $productData = $this->convertSpecialFromDateStringToObject($productData); if (!empty($productData['is_downloadable'])) { $productData['product_has_weight'] = 0; @@ -200,7 +212,7 @@ public function initializeFromData(Product $product, array $productData) foreach ($attributes as $attrKey => $attribute) { if ($attribute->getBackend()->getType() == 'datetime') { if (array_key_exists($attrKey, $productData) && $productData[$attrKey] != '') { - $dateFieldFilters[$attrKey] = $this->getDateTimeFilter(); + $dateFieldFilters[$attrKey] = $this->dateTimeFilter; } } } @@ -243,8 +255,10 @@ public function initializeFromData(Product $product, array $productData) public function initialize(Product $product) { $productData = $this->request->getPost('product', []); + $product = $this->initializeFromData($product, $productData); + $this->productAuthorization->authorizeSavingOf($product); - return $this->initializeFromData($product, $productData); + return $product; } /** @@ -397,22 +411,6 @@ private function getLinkResolver() return $this->linkResolver; } - /** - * Get DateTimeFilter instance - * - * @return \Magento\Framework\Stdlib\DateTime\Filter\DateTime - * @deprecated 101.0.0 - */ - private function getDateTimeFilter() - { - if ($this->dateTimeFilter === null) { - $this->dateTimeFilter = ObjectManager::getInstance() - ->get(\Magento\Framework\Stdlib\DateTime\Filter\DateTime::class); - } - - return $this->dateTimeFilter; - } - /** * Remove ids of non selected websites from $websiteIds array and return filtered data * @@ -486,20 +484,4 @@ function ($valueData) { return $product->setOptions($customOptions); } - - /** - * Convert string date presentation into object - * - * @param array $productData - * @return array - */ - private function convertSpecialFromDateStringToObject($productData) - { - if (isset($productData['special_from_date']) && $productData['special_from_date'] != '') { - $productData['special_from_date'] = $this->getDateTimeFilter()->filter($productData['special_from_date']); - $productData['special_from_date'] = new DateTime($productData['special_from_date']); - } - - return $productData; - } } diff --git a/app/code/Magento/Catalog/Controller/Category/View.php b/app/code/Magento/Catalog/Controller/Category/View.php index 770a306431b7a..eea448e0fdb0b 100644 --- a/app/code/Magento/Catalog/Controller/Category/View.php +++ b/app/code/Magento/Catalog/Controller/Category/View.php @@ -30,6 +30,7 @@ use Magento\Framework\View\Result\PageFactory; use Magento\Store\Model\StoreManagerInterface; use Psr\Log\LoggerInterface; +use Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager; /** * View a category on storefront. Needs to be accessible by POST because of the store switching. @@ -96,6 +97,11 @@ class View extends Action implements HttpGetActionInterface, HttpPostActionInter */ private $toolbarMemorizer; + /** + * @var LayoutUpdateManager + */ + private $customLayoutManager; + /** * @var CategoryHelper */ @@ -119,7 +125,8 @@ class View extends Action implements HttpGetActionInterface, HttpPostActionInter * @param ForwardFactory $resultForwardFactory * @param Resolver $layerResolver * @param CategoryRepositoryInterface $categoryRepository - * @param ToolbarMemorizer $toolbarMemorizer + * @param ToolbarMemorizer|null $toolbarMemorizer + * @param LayoutUpdateManager|null $layoutUpdateManager * @param CategoryHelper $categoryHelper * @param LoggerInterface $logger * @SuppressWarnings(PHPMD.ExcessiveParameterList) @@ -136,6 +143,7 @@ public function __construct( Resolver $layerResolver, CategoryRepositoryInterface $categoryRepository, ToolbarMemorizer $toolbarMemorizer = null, + ?LayoutUpdateManager $layoutUpdateManager = null, CategoryHelper $categoryHelper = null, LoggerInterface $logger = null ) { @@ -149,8 +157,9 @@ public function __construct( $this->resultForwardFactory = $resultForwardFactory; $this->layerResolver = $layerResolver; $this->categoryRepository = $categoryRepository; - $this->toolbarMemorizer = $toolbarMemorizer ?: ObjectManager::getInstance() - ->get(ToolbarMemorizer::class); + $this->toolbarMemorizer = $toolbarMemorizer ?: ObjectManager::getInstance()->get(ToolbarMemorizer::class); + $this->customLayoutManager = $layoutUpdateManager + ?? ObjectManager::getInstance()->get(LayoutUpdateManager::class); $this->categoryHelper = $categoryHelper ?: ObjectManager::getInstance() ->get(CategoryHelper::class); $this->logger = $logger ?: ObjectManager::getInstance() @@ -279,5 +288,10 @@ private function applyLayoutUpdates( $page->addPageLayoutHandles(['layout_update' => sha1($layoutUpdate)], null, false); } } + + //Selected files + if ($settings->getPageLayoutHandles()) { + $page->addPageLayoutHandles($settings->getPageLayoutHandles()); + } } } diff --git a/app/code/Magento/Catalog/Helper/Product/Edit/Action/Attribute.php b/app/code/Magento/Catalog/Helper/Product/Edit/Action/Attribute.php index 2c1bacdb99e12..09d53427a3043 100644 --- a/app/code/Magento/Catalog/Helper/Product/Edit/Action/Attribute.php +++ b/app/code/Magento/Catalog/Helper/Product/Edit/Action/Attribute.php @@ -3,15 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -/** - * Adminhtml catalog product action attribute update helper - */ namespace Magento\Catalog\Helper\Product\Edit\Action; /** - * Class Attribute + * Adminhtml catalog product action attribute update helper. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class Attribute extends \Magento\Backend\Helper\Data { @@ -32,7 +32,7 @@ class Attribute extends \Magento\Backend\Helper\Data /** * Excluded from batch update attribute codes * - * @var string[] + * @var array */ protected $_excludedAttributes = ['url_key']; @@ -92,6 +92,7 @@ public function __construct( /** * Return product collection with selected product filter + * * Product collection didn't load * * @return \Magento\Catalog\Model\ResourceModel\Product\Collection @@ -171,8 +172,8 @@ public function getAttributes() $this->getProductsSetIds() ); - if ($this->_excludedAttributes) { - $this->_attributes->addFieldToFilter('attribute_code', ['nin' => $this->_excludedAttributes]); + if ($excludedAttributes = $this->getExcludedAttributes()) { + $this->_attributes->addFieldToFilter('attribute_code', ['nin' => $excludedAttributes]); } // check product type apply to limitation and remove attributes that impossible to change in mass-update @@ -193,11 +194,24 @@ public function getAttributes() } /** + * Gets website id. + * * @param int $storeId * @return int + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function getStoreWebsiteId($storeId) { return $this->_storeManager->getStore($storeId)->getWebsiteId(); } + + /** + * Retrieve excluded attributes. + * + * @return array + */ + public function getExcludedAttributes(): array + { + return $this->_excludedAttributes; + } } diff --git a/app/code/Magento/Catalog/Helper/Product/View.php b/app/code/Magento/Catalog/Helper/Product/View.php index 74f40a18971d5..cf5b15cadc997 100644 --- a/app/code/Magento/Catalog/Helper/Product/View.php +++ b/app/code/Magento/Catalog/Helper/Product/View.php @@ -6,6 +6,8 @@ namespace Magento\Catalog\Helper\Product; +use Magento\Catalog\Model\Product\Attribute\LayoutUpdateManager; +use Magento\Framework\App\ObjectManager; use Magento\Framework\View\Result\Page as ResultPage; /** @@ -66,6 +68,11 @@ class View extends \Magento\Framework\App\Helper\AbstractHelper */ private $string; + /** + * @var LayoutUpdateManager + */ + private $layoutUpdateManager; + /** * Constructor * @@ -78,6 +85,8 @@ class View extends \Magento\Framework\App\Helper\AbstractHelper * @param \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator $categoryUrlPathGenerator * @param array $messageGroups * @param \Magento\Framework\Stdlib\StringUtils|null $string + * @param LayoutUpdateManager|null $layoutUpdateManager + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Framework\App\Helper\Context $context, @@ -88,7 +97,8 @@ public function __construct( \Magento\Framework\Message\ManagerInterface $messageManager, \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator $categoryUrlPathGenerator, array $messageGroups = [], - \Magento\Framework\Stdlib\StringUtils $string = null + \Magento\Framework\Stdlib\StringUtils $string = null, + ?LayoutUpdateManager $layoutUpdateManager = null ) { $this->_catalogSession = $catalogSession; $this->_catalogDesign = $catalogDesign; @@ -97,8 +107,9 @@ public function __construct( $this->messageGroups = $messageGroups; $this->messageManager = $messageManager; $this->categoryUrlPathGenerator = $categoryUrlPathGenerator; - $this->string = $string ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\Stdlib\StringUtils::class); + $this->string = $string ?: ObjectManager::getInstance()->get(\Magento\Framework\Stdlib\StringUtils::class); + $this->layoutUpdateManager = $layoutUpdateManager + ?? ObjectManager::getInstance()->get(LayoutUpdateManager::class); parent::__construct($context); } @@ -203,6 +214,9 @@ public function initProductLayout(ResultPage $resultPage, $product, $params = nu } } } + if ($settings->getPageLayoutHandles()) { + $resultPage->addPageLayoutHandles($settings->getPageLayoutHandles()); + } $currentCategory = $this->_coreRegistry->registry('current_category'); $controllerClass = $this->_request->getFullActionName(); diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdate.php b/app/code/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdate.php new file mode 100644 index 0000000000000..1aa7ab7e5880f --- /dev/null +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdate.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\Model\Attribute\Backend; + +use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend; +use Magento\Framework\Exception\LocalizedException; +use Magento\Catalog\Model\AbstractModel; + +/** + * Custom layout file attribute. + */ +abstract class AbstractLayoutUpdate extends AbstractBackend +{ + public const VALUE_USE_UPDATE_XML = '__existing__'; + + public const VALUE_NO_UPDATE = '__no_update__'; + + /** + * Extract attribute value. + * + * @param AbstractModel $model + * @return mixed + */ + private function extractAttributeValue(AbstractModel $model) + { + $code = $this->getAttribute()->getAttributeCode(); + + return $model->getData($code); + } + + /** + * Compose list of available files (layout handles) for given entity. + * + * @param AbstractModel $forModel + * @return string[] + */ + abstract protected function listAvailableValues(AbstractModel $forModel): array; + + /** + * Extracts prepare attribute value to be saved. + * + * @throws LocalizedException + * @param AbstractModel $model + * @return string|null + */ + private function prepareValue(AbstractModel $model): ?string + { + $value = $this->extractAttributeValue($model); + if (!is_string($value)) { + $value = null; + } + if ($value + && $value !== self::VALUE_USE_UPDATE_XML + && $value !== self::VALUE_NO_UPDATE + && !in_array($value, $this->listAvailableValues($model), true) + ) { + throw new LocalizedException(__('Selected layout update is not available')); + } + + return $value; + } + + /** + * Set value for the object. + * + * @param string|null $value + * @param AbstractModel $forObject + * @param string|null $attrCode + * @return void + */ + private function setAttributeValue(?string $value, AbstractModel $forObject, ?string $attrCode = null): void + { + $attrCode = $attrCode ?? $this->getAttribute()->getAttributeCode(); + if ($forObject->hasData(AbstractModel::CUSTOM_ATTRIBUTES)) { + $forObject->setCustomAttribute($attrCode, $value); + } + $forObject->setData($attrCode, $value); + } + + /** + * @inheritDoc + * + * @param AbstractModel $object + */ + public function validate($object) + { + $valid = parent::validate($object); + if ($valid) { + $this->prepareValue($object); + } + + return $valid; + } + + /** + * @inheritDoc + * @param AbstractModel $object + * @throws LocalizedException + */ + public function beforeSave($object) + { + $value = $this->prepareValue($object); + if ($value && ($value === self::VALUE_NO_UPDATE || $value !== self::VALUE_USE_UPDATE_XML)) { + $this->setAttributeValue(null, $object, 'custom_layout_update'); + } + if (!$value || $value === self::VALUE_USE_UPDATE_XML || $value === self::VALUE_NO_UPDATE) { + $value = null; + } + $this->setAttributeValue($value, $object); + + return $this; + } +} diff --git a/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php b/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php index a994446881189..b5aa5e2035100 100644 --- a/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php +++ b/app/code/Magento/Catalog/Model/Attribute/Backend/Customlayoutupdate.php @@ -5,8 +5,10 @@ */ namespace Magento\Catalog\Model\Attribute\Backend; +use Magento\Catalog\Model\AbstractModel; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\View\Model\Layout\Update\ValidatorFactory; -use Magento\Eav\Model\Entity\Attribute\Exception; +use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend; /** * Layout update attribute backend @@ -16,18 +18,15 @@ * @SuppressWarnings(PHPMD.LongVariable) * @since 100.0.2 */ -class Customlayoutupdate extends \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend +class Customlayoutupdate extends AbstractBackend { /** - * Layout update validator factory - * * @var ValidatorFactory + * @deprecated Is not used anymore. */ protected $_layoutUpdateValidatorFactory; /** - * Construct the custom layout update class - * * @param ValidatorFactory $layoutUpdateValidatorFactory */ public function __construct(ValidatorFactory $layoutUpdateValidatorFactory) @@ -36,31 +35,95 @@ public function __construct(ValidatorFactory $layoutUpdateValidatorFactory) } /** - * Validate the custom layout update + * Extract an attribute value. * - * @param \Magento\Framework\DataObject $object - * @return bool - * @throws Exception + * @param AbstractModel $object + * @return mixed */ - public function validate($object) + private function extractValue(AbstractModel $object) + { + $attributeCode = $attributeCode ?? $this->getAttribute()->getName(); + $value = $object->getData($attributeCode); + if (!$value || !is_string($value)) { + $value = null; + } + + return $value; + } + + /** + * Extract old attribute value. + * + * @param AbstractModel $object + * @return mixed Old value or null. + */ + private function extractOldValue(AbstractModel $object) { - $attributeName = $this->getAttribute()->getName(); - $xml = trim($object->getData($attributeName)); + if (!empty($object->getId())) { + $attr = $this->getAttribute()->getAttributeCode(); + + if ($object->getOrigData()) { + return $object->getOrigData($attr); + } - if (!$this->getAttribute()->getIsRequired() && empty($xml)) { - return true; + $oldObject = clone $object; + $oldObject->unsetData(); + $oldObject->load($object->getId()); + + return $oldObject->getData($attr); } - /** @var $validator \Magento\Framework\View\Model\Layout\Update\Validator */ - $validator = $this->_layoutUpdateValidatorFactory->create(); - if (!$validator->isValid($xml)) { - $messages = $validator->getMessages(); - //Add first message to exception - $message = array_shift($messages); - $eavExc = new Exception(__($message)); - $eavExc->setAttributeCode($attributeName); - throw $eavExc; + return null; + } + + /** + * @inheritDoc + * + * @param AbstractModel $object + */ + public function validate($object) + { + if (parent::validate($object)) { + if ($object instanceof AbstractModel) { + $value = $this->extractValue($object); + $oldValue = $this->extractOldValue($object); + if ($value && $oldValue !== $value) { + throw new LocalizedException(__('Custom layout update text cannot be changed, only removed')); + } + } } + return true; } + + /** + * Put an attribute value. + * + * @param AbstractModel $object + * @param string|null $value + * @return void + */ + private function putValue(AbstractModel $object, ?string $value): void + { + $attributeCode = $this->getAttribute()->getName(); + if ($object->hasData(AbstractModel::CUSTOM_ATTRIBUTES)) { + $object->setCustomAttribute($attributeCode, $value); + } + $object->setData($attributeCode, $value); + } + + /** + * @inheritDoc + * + * @param AbstractModel $object + * @throws LocalizedException + */ + public function beforeSave($object) + { + //Validate first, validation might have been skipped. + $this->validate($object); + $this->putValue($object, $this->extractValue($object)); + + return parent::beforeSave($object); + } } diff --git a/app/code/Magento/Catalog/Model/Attribute/ScopeOverriddenValue.php b/app/code/Magento/Catalog/Model/Attribute/ScopeOverriddenValue.php index 0940ca7a234a3..cf194615b1f3b 100644 --- a/app/code/Magento/Catalog/Model/Attribute/ScopeOverriddenValue.php +++ b/app/code/Magento/Catalog/Model/Attribute/ScopeOverriddenValue.php @@ -81,7 +81,7 @@ public function containsValue($entityType, $entity, $attributeCode, $storeId) if ((int)$storeId === Store::DEFAULT_STORE_ID) { return false; } - if ($this->attributesValues === null) { + if (!isset($this->attributesValues[$storeId])) { $this->initAttributeValues($entityType, $entity, (int)$storeId); } @@ -110,6 +110,8 @@ public function getDefaultValues($entityType, $entity) } /** + * Init attribute values. + * * @param string $entityType * @param \Magento\Catalog\Model\AbstractModel $entity * @param int $storeId @@ -158,6 +160,8 @@ private function initAttributeValues($entityType, $entity, $storeId) } /** + * Returns entity attributes. + * * @param string $entityType * @return \Magento\Eav\Api\Data\AttributeInterface[] */ diff --git a/app/code/Magento/Catalog/Model/Attribute/Source/AbstractLayoutUpdate.php b/app/code/Magento/Catalog/Model/Attribute/Source/AbstractLayoutUpdate.php new file mode 100644 index 0000000000000..0003b9996c84b --- /dev/null +++ b/app/code/Magento/Catalog/Model/Attribute/Source/AbstractLayoutUpdate.php @@ -0,0 +1,101 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Model\Attribute\Source; + +use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource; +use Magento\Eav\Model\Entity\Attribute\Source\SpecificSourceInterface; +use Magento\Framework\Api\CustomAttributesDataInterface; +use Magento\Catalog\Model\Attribute\Backend\AbstractLayoutUpdate as Backend; +use Magento\Framework\Model\AbstractExtensibleModel; + +/** + * List of layout updates available for a catalog entity. + */ +abstract class AbstractLayoutUpdate extends AbstractSource implements SpecificSourceInterface +{ + /** + * @var string[] + */ + private $optionsText; + + /** + * @inheritDoc + */ + public function getAllOptions() + { + $default = Backend::VALUE_NO_UPDATE; + $defaultText = 'No update'; + $this->optionsText[$default] = $defaultText; + + return [['label' => $defaultText, 'value' => $default]]; + } + + /** + * @inheritDoc + */ + public function getOptionText($value) + { + if (is_scalar($value) && array_key_exists($value, $this->optionsText)) { + return $this->optionsText[$value]; + } + + return false; + } + + /** + * Extract attribute value. + * + * @param CustomAttributesDataInterface|AbstractExtensibleModel $entity + * @return mixed + */ + private function extractAttributeValue(CustomAttributesDataInterface $entity) + { + $attrCode = 'custom_layout_update'; + if ($entity instanceof AbstractExtensibleModel + && !$entity->hasData(CustomAttributesDataInterface::CUSTOM_ATTRIBUTES) + ) { + //Custom attributes were not loaded yet, using data array + return $entity->getData($attrCode); + } + //Fallback to customAttribute method + $attr = $entity->getCustomAttribute($attrCode); + + return $attr ? $attr->getValue() : null; + } + + /** + * List available layout update options for the entity. + * + * @param CustomAttributesDataInterface $entity + * @return string[] + */ + abstract protected function listAvailableOptions(CustomAttributesDataInterface $entity): array; + + /** + * @inheritDoc + * + * @param CustomAttributesDataInterface|AbstractExtensibleModel $entity + */ + public function getOptionsFor(CustomAttributesDataInterface $entity): array + { + $options = $this->getAllOptions(); + if ($this->extractAttributeValue($entity)) { + $existingValue = Backend::VALUE_USE_UPDATE_XML; + $existingLabel = 'Use existing'; + $options[] = ['label' => $existingLabel, 'value' => $existingValue]; + $this->optionsText[$existingValue] = $existingLabel; + } + foreach ($this->listAvailableOptions($entity) as $handle) { + $options[] = ['label' => $handle, 'value' => $handle]; + $this->optionsText[$handle] = $handle; + } + + return $options; + } +} diff --git a/app/code/Magento/Catalog/Model/Category.php b/app/code/Magento/Catalog/Model/Category.php index 4ddfd1f3b63a8..330debdc32469 100644 --- a/app/code/Magento/Catalog/Model/Category.php +++ b/app/code/Magento/Catalog/Model/Category.php @@ -5,13 +5,10 @@ */ namespace Magento\Catalog\Model; -use Magento\Authorization\Model\UserContextInterface; use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Catalog\Api\Data\CategoryInterface; use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; use Magento\Framework\Api\AttributeValueFactory; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\AuthorizationInterface; use Magento\Framework\Convert\ConvertArray; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Profiler; @@ -131,7 +128,8 @@ class Category extends \Magento\Catalog\Model\AbstractModel implements 'page_layout', 'custom_layout_update', 'custom_apply_to_products', - 'custom_use_parent_settings', + 'custom_layout_update_file', + 'custom_use_parent_settings' ]; /** @@ -215,16 +213,6 @@ class Category extends \Magento\Catalog\Model\AbstractModel implements */ protected $metadataService; - /** - * @var UserContextInterface - */ - private $userContext; - - /** - * @var AuthorizationInterface - */ - private $authorization; - /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -495,7 +483,7 @@ public function getProductCollection() * Retrieve all customer attributes * * @param bool $noDesignAttributes - * @return array + * @return \Magento\Eav\Api\Data\AttributeInterface[] * @todo Use with Flat Resource * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ @@ -764,7 +752,7 @@ public function getCustomDesignDate() /** * Retrieve design attributes array * - * @return array + * @return \Magento\Eav\Api\Data\AttributeInterface[] */ public function getDesignAttributes() { @@ -936,60 +924,6 @@ public function beforeDelete() return parent::beforeDelete(); } - /** - * Get user context. - * - * @return UserContextInterface - */ - private function getUserContext(): UserContextInterface - { - if (!$this->userContext) { - $this->userContext = ObjectManager::getInstance()->get(UserContextInterface::class); - } - - return $this->userContext; - } - - /** - * Get authorization service. - * - * @return AuthorizationInterface - */ - private function getAuthorization(): AuthorizationInterface - { - if (!$this->authorization) { - $this->authorization = ObjectManager::getInstance()->get(AuthorizationInterface::class); - } - - return $this->authorization; - } - - /** - * @inheritDoc - */ - public function beforeSave() - { - //Validate changing of design. - $userType = $this->getUserContext()->getUserType(); - if (( - $userType === UserContextInterface::USER_TYPE_ADMIN - || $userType === UserContextInterface::USER_TYPE_INTEGRATION - ) - && !$this->getAuthorization()->isAllowed('Magento_Catalog::edit_category_design') - ) { - foreach ($this->_designAttributes as $attributeCode) { - $this->setData($attributeCode, $value = $this->getOrigData($attributeCode)); - if (!empty($this->_data[self::CUSTOM_ATTRIBUTES]) - && array_key_exists($attributeCode, $this->_data[self::CUSTOM_ATTRIBUTES])) { - //In case custom attribute were used to update the entity. - $this->_data[self::CUSTOM_ATTRIBUTES][$attributeCode]->setValue($value); - } - } - } - - return parent::beforeSave(); - } - /** * Retrieve anchors above * @@ -1201,8 +1135,6 @@ public function reindex() || $this->dataHasChangedFor('is_active')) { if (!$productIndexer->isScheduled()) { $productIndexer->reindexList($this->getPathIds()); - } else { - $productIndexer->invalidate(); } } } @@ -1360,6 +1292,7 @@ public function getChildrenData() //@codeCoverageIgnoreEnd + // phpcs:disable PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames /** * Return Data Object data in array format. * @@ -1368,6 +1301,7 @@ public function getChildrenData() */ public function __toArray() { + // phpcs:enable PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames $data = $this->_data; $hasToArray = function ($model) { return is_object($model) && method_exists($model, '__toArray') && is_callable([$model, '__toArray']); diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php index 4880214e5c6a6..865160da14a6f 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php @@ -5,8 +5,12 @@ */ namespace Magento\Catalog\Model\Category\Attribute\Backend; +use Magento\Catalog\Model\ImageUploader; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\ObjectManager; use Magento\Framework\File\Uploader; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; /** * Catalog category image attribute backend model @@ -45,7 +49,7 @@ class Image extends \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend protected $_logger; /** - * @var \Magento\Catalog\Model\ImageUploader + * @var ImageUploader */ private $imageUploader; @@ -54,19 +58,32 @@ class Image extends \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend */ private $additionalData = '_additional_data_'; + /** + * @var StoreManagerInterface + */ + private $storeManager; + /** * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Framework\Filesystem $filesystem * @param \Magento\MediaStorage\Model\File\UploaderFactory $fileUploaderFactory + * @param StoreManagerInterface $storeManager + * @param ImageUploader $imageUploader */ public function __construct( \Psr\Log\LoggerInterface $logger, \Magento\Framework\Filesystem $filesystem, - \Magento\MediaStorage\Model\File\UploaderFactory $fileUploaderFactory + \Magento\MediaStorage\Model\File\UploaderFactory $fileUploaderFactory, + StoreManagerInterface $storeManager = null, + ImageUploader $imageUploader = null ) { $this->_filesystem = $filesystem; $this->_fileUploaderFactory = $fileUploaderFactory; $this->_logger = $logger; + $this->storeManager = $storeManager ?? + ObjectManager::getInstance()->get(StoreManagerInterface::class); + $this->imageUploader = $imageUploader ?? + ObjectManager::getInstance()->get(ImageUploader::class); } /** @@ -94,13 +111,13 @@ private function getUploadedImageName($value) */ private function checkUniqueImageName(string $imageName): string { - $imageUploader = $this->getImageUploader(); $mediaDirectory = $this->_filesystem->getDirectoryWrite(DirectoryList::MEDIA); $imageAbsolutePath = $mediaDirectory->getAbsolutePath( - $imageUploader->getBasePath() . DIRECTORY_SEPARATOR . $imageName + $this->imageUploader->getBasePath() . DIRECTORY_SEPARATOR . $imageName ); - $imageName = Uploader::getNewFilename($imageAbsolutePath); + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $imageName = call_user_func([Uploader::class, 'getNewFilename'], $imageAbsolutePath); return $imageName; } @@ -119,7 +136,18 @@ public function beforeSave($object) $attributeName = $this->getAttribute()->getName(); $value = $object->getData($attributeName); - if ($this->fileResidesOutsideCategoryDir($value)) { + if ($this->isTmpFileAvailable($value) && $imageName = $this->getUploadedImageName($value)) { + try { + /** @var StoreInterface $store */ + $store = $this->storeManager->getStore(); + $baseMediaDir = $store->getBaseMediaDir(); + $newImgRelativePath = $this->imageUploader->moveFileFromTmp($imageName, true); + $value[0]['url'] = '/' . $baseMediaDir . '/' . $newImgRelativePath; + $value[0]['name'] = $value[0]['url']; + } catch (\Exception $e) { + $this->_logger->critical($e); + } + } elseif ($this->fileResidesOutsideCategoryDir($value)) { // use relative path for image attribute so we know it's outside of category dir when we fetch it // phpcs:ignore Magento2.Functions.DiscouragedFunction $value[0]['url'] = parse_url($value[0]['url'], PHP_URL_PATH); @@ -139,23 +167,6 @@ public function beforeSave($object) return parent::beforeSave($object); } - /** - * Get Instance of Category Image Uploader. - * - * @return \Magento\Catalog\Model\ImageUploader - * - * @deprecated 101.0.0 - */ - private function getImageUploader() - { - if ($this->imageUploader === null) { - $this->imageUploader = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Catalog\CategoryImageUpload::class); - } - - return $this->imageUploader; - } - /** * Check if temporary file is available for new image upload. * @@ -194,19 +205,10 @@ private function fileResidesOutsideCategoryDir($value) * * @param \Magento\Framework\DataObject $object * @return \Magento\Catalog\Model\Category\Attribute\Backend\Image + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function afterSave($object) { - $value = $object->getData($this->additionalData . $this->getAttribute()->getName()); - - if ($this->isTmpFileAvailable($value) && $imageName = $this->getUploadedImageName($value)) { - try { - $this->getImageUploader()->moveFileFromTmp($imageName); - } catch (\Exception $e) { - $this->_logger->critical($e); - } - } - return $this; } } diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php new file mode 100644 index 0000000000000..215fe1c19bd8d --- /dev/null +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/LayoutUpdate.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Model\Category\Attribute\Backend; + +use Magento\Catalog\Model\AbstractModel; +use Magento\Catalog\Model\Attribute\Backend\AbstractLayoutUpdate; +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager; + +/** + * Allows to select a layout file to merge when rendering the category's page. + */ +class LayoutUpdate extends AbstractLayoutUpdate +{ + + /** + * @var LayoutUpdateManager + */ + private $manager; + + /** + * @param LayoutUpdateManager $manager + */ + public function __construct(LayoutUpdateManager $manager) + { + $this->manager = $manager; + } + + /** + * @inheritDoc + * + * @param AbstractModel|Category $forModel + */ + protected function listAvailableValues(AbstractModel $forModel): array + { + return $this->manager->fetchAvailableFiles($forModel); + } +} diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/LayoutUpdateManager.php b/app/code/Magento/Catalog/Model/Category/Attribute/LayoutUpdateManager.php new file mode 100644 index 0000000000000..f5694a46d3fb2 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Category/Attribute/LayoutUpdateManager.php @@ -0,0 +1,154 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Model\Category\Attribute; + +use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Catalog\Model\Category; +use Magento\Framework\App\Area; +use Magento\Framework\DataObject; +use Magento\Framework\View\Design\Theme\FlyweightFactory; +use Magento\Framework\View\DesignInterface; +use Magento\Framework\View\Model\Layout\Merge as LayoutProcessor; +use Magento\Framework\View\Model\Layout\MergeFactory as LayoutProcessorFactory; + +/** + * Manage available layout updates for categories. + */ +class LayoutUpdateManager +{ + + /** + * @var FlyweightFactory + */ + private $themeFactory; + + /** + * @var DesignInterface + */ + private $design; + + /** + * @var LayoutProcessorFactory + */ + private $layoutProcessorFactory; + + /** + * @var LayoutProcessor|null + */ + private $layoutProcessor; + + /** + * @param FlyweightFactory $themeFactory + * @param DesignInterface $design + * @param LayoutProcessorFactory $layoutProcessorFactory + */ + public function __construct( + FlyweightFactory $themeFactory, + DesignInterface $design, + LayoutProcessorFactory $layoutProcessorFactory + ) { + $this->themeFactory = $themeFactory; + $this->design = $design; + $this->layoutProcessorFactory = $layoutProcessorFactory; + } + + /** + * Get the processor instance. + * + * @return LayoutProcessor + */ + private function getLayoutProcessor(): LayoutProcessor + { + if (!$this->layoutProcessor) { + $this->layoutProcessor = $this->layoutProcessorFactory->create( + [ + 'theme' => $this->themeFactory->create( + $this->design->getConfigurationDesignTheme(Area::AREA_FRONTEND) + ) + ] + ); + $this->themeFactory = null; + $this->design = null; + } + + return $this->layoutProcessor; + } + + /** + * Fetch list of available files/handles for the category. + * + * @param CategoryInterface $category + * @return string[] + */ + public function fetchAvailableFiles(CategoryInterface $category): array + { + if (!$category->getId()) { + return []; + } + + $handles = $this->getLayoutProcessor()->getAvailableHandles(); + + return array_filter( + array_map( + function (string $handle) use ($category) : ?string { + preg_match( + '/^catalog\_category\_view\_selectable\_' .$category->getId() .'\_([a-z0-9]+)/i', + $handle, + $selectable + ); + if (!empty($selectable[1])) { + return $selectable[1]; + } + + return null; + }, + $handles + ) + ); + } + + /** + * Extract custom layout attribute value. + * + * @param CategoryInterface $category + * @return mixed + */ + private function extractAttributeValue(CategoryInterface $category) + { + if ($category instanceof Category && !$category->hasData(CategoryInterface::CUSTOM_ATTRIBUTES)) { + return $category->getData('custom_layout_update_file'); + } + if ($attr = $category->getCustomAttribute('custom_layout_update_file')) { + return $attr->getValue(); + } + + return null; + } + + /** + * Extract selected custom layout settings. + * + * If no update is selected none will apply. + * + * @param CategoryInterface $category + * @param DataObject $intoSettings + * @return void + */ + public function extractCustomSettings(CategoryInterface $category, DataObject $intoSettings): void + { + if ($category->getId() && $value = $this->extractAttributeValue($category)) { + $handles = $intoSettings->getPageLayoutHandles() ?? []; + $handles = array_merge_recursive( + $handles, + ['selectable' => $category->getId() . '_' . $value] + ); + $intoSettings->setPageLayoutHandles($handles); + } + } +} diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php new file mode 100644 index 0000000000000..1c307220aa9f8 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Source/LayoutUpdate.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Model\Category\Attribute\Source; + +use Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager; +use Magento\Framework\Api\CustomAttributesDataInterface; +use Magento\Catalog\Model\Attribute\Source\AbstractLayoutUpdate; + +/** + * List of layout updates available for a category. + */ +class LayoutUpdate extends AbstractLayoutUpdate +{ + /** + * @var LayoutUpdateManager + */ + private $manager; + + /** + * @param LayoutUpdateManager $manager + */ + public function __construct(LayoutUpdateManager $manager) + { + $this->manager = $manager; + } + + /** + * @inheritDoc + */ + protected function listAvailableOptions(CustomAttributesDataInterface $entity): array + { + return $this->manager->fetchAvailableFiles($entity); + } +} diff --git a/app/code/Magento/Catalog/Model/Category/Authorization.php b/app/code/Magento/Catalog/Model/Category/Authorization.php new file mode 100644 index 0000000000000..629a3c2319472 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Category/Authorization.php @@ -0,0 +1,158 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Model\Category; + +use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Catalog\Model\Category as CategoryModel; +use Magento\Catalog\Model\CategoryFactory; +use Magento\Eav\Api\Data\AttributeInterface; +use Magento\Framework\AuthorizationInterface; +use Magento\Framework\Exception\AuthorizationException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate; + +/** + * Additional authorization for category operations. + */ +class Authorization +{ + /** + * @var AuthorizationInterface + */ + private $authorization; + + /** + * @var CategoryFactory + */ + private $categoryFactory; + + /** + * @param AuthorizationInterface $authorization + * @param CategoryFactory $factory + */ + public function __construct(AuthorizationInterface $authorization, CategoryFactory $factory) + { + $this->authorization = $authorization; + $this->categoryFactory = $factory; + } + + /** + * Extract attribute value from the model. + * + * @param CategoryModel $category + * @param AttributeInterface $attr + * @throws \RuntimeException When no new value is present. + * @return mixed + */ + private function extractAttributeValue(CategoryModel $category, AttributeInterface $attr) + { + if ($category->hasData($attr->getAttributeCode())) { + $newValue = $category->getData($attr->getAttributeCode()); + } elseif ($category->hasData(CategoryModel::CUSTOM_ATTRIBUTES) + && $attrValue = $category->getCustomAttribute($attr->getAttributeCode()) + ) { + $newValue = $attrValue->getValue(); + } else { + throw new \RuntimeException('New value is not set'); + } + + if (empty($newValue) + || ($attr->getBackend() instanceof LayoutUpdate + && ($newValue === LayoutUpdate::VALUE_USE_UPDATE_XML || $newValue === LayoutUpdate::VALUE_NO_UPDATE) + ) + ) { + $newValue = null; + } + + return $newValue; + } + + /** + * Find values to compare the new one. + * + * @param AttributeInterface $attribute + * @param array|null $oldCategory + * @return mixed[] + */ + private function fetchOldValue(AttributeInterface $attribute, ?array $oldCategory): array + { + $oldValues = [null]; + $attrCode = $attribute->getAttributeCode(); + if ($oldCategory) { + //New value must match saved value exactly + $oldValues = [!empty($oldCategory[$attrCode]) ? $oldCategory[$attrCode] : null]; + if (empty($oldValues[0])) { + $oldValues[0] = null; + } + } else { + //New value can be either empty or default value. + $oldValues[] = $attribute->getDefaultValue(); + } + + return $oldValues; + } + + /** + * Determine whether a category has design properties changed. + * + * @param CategoryModel $category + * @param array|null $oldCategory + * @return bool + */ + private function hasChanges(CategoryModel $category, ?array $oldCategory): bool + { + foreach ($category->getDesignAttributes() as $designAttribute) { + $oldValues = $this->fetchOldValue($designAttribute, $oldCategory); + try { + $newValue = $this->extractAttributeValue($category, $designAttribute); + } catch (\RuntimeException $exception) { + //No new value + continue; + } + + if (!in_array($newValue, $oldValues, true)) { + return true; + } + } + + return false; + } + + /** + * Authorize saving of a category. + * + * @throws AuthorizationException + * @throws NoSuchEntityException When a category with invalid ID given. + * @param CategoryInterface|CategoryModel $category + * @return void + */ + public function authorizeSavingOf(CategoryInterface $category): void + { + if (!$this->authorization->isAllowed('Magento_Catalog::edit_category_design')) { + $oldData = null; + if ($category->getId()) { + if ($category->getOrigData()) { + $oldData = $category->getOrigData(); + } else { + /** @var CategoryModel $savedCategory */ + $savedCategory = $this->categoryFactory->create(); + $savedCategory->load($category->getId()); + if (!$savedCategory->getName()) { + throw NoSuchEntityException::singleField('id', $category->getId()); + } + $oldData = $savedCategory->getData(); + } + } + + if ($this->hasChanges($category, $oldData)) { + throw new AuthorizationException(__('Not allowed to edit the category\'s design attributes')); + } + } + } +} diff --git a/app/code/Magento/Catalog/Model/Category/DataProvider.php b/app/code/Magento/Catalog/Model/Category/DataProvider.php index d2e237779e2a8..fe7258398d191 100644 --- a/app/code/Magento/Catalog/Model/Category/DataProvider.php +++ b/app/code/Magento/Catalog/Model/Category/DataProvider.php @@ -12,15 +12,19 @@ use Magento\Catalog\Model\Attribute\ScopeOverriddenValue; use Magento\Catalog\Model\Category; use Magento\Catalog\Model\Category\Attribute\Backend\Image as ImageBackendModel; +use Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate; use Magento\Catalog\Model\CategoryFactory; use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory; use Magento\Catalog\Model\ResourceModel\Eav\Attribute as EavAttribute; use Magento\Eav\Api\Data\AttributeInterface; use Magento\Eav\Model\Config; +use Magento\Eav\Model\Entity\Attribute\Source\SpecificSourceInterface; use Magento\Eav\Model\Entity\Type; use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Framework\Filesystem; +use Magento\Framework\Registry; use Magento\Framework\Stdlib\ArrayManager; use Magento\Framework\Stdlib\ArrayUtils; use Magento\Store\Model\Store; @@ -29,6 +33,7 @@ use Magento\Ui\DataProvider\EavValidationRules; use Magento\Ui\DataProvider\Modifier\PoolInterface; use Magento\Framework\AuthorizationInterface; +use Magento\Ui\DataProvider\ModifierPoolDataProvider; /** * Category form data provider. @@ -38,7 +43,7 @@ * @SuppressWarnings(PHPMD.TooManyFields) * @since 101.0.0 */ -class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider +class DataProvider extends ModifierPoolDataProvider { /** * @var string @@ -69,6 +74,8 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider 'size' => 'multiline_count', ]; + private $boolMetaProperties = ['visible', 'required']; + /** * Form element mapping * @@ -103,6 +110,15 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider 'position' ]; + /** + * Elements with currency symbol + * + * @var array + */ + private $elementsWithCurrencySymbol = [ + 'filter_price_range', + ]; + /** * @var EavValidationRules * @since 101.0.0 @@ -110,13 +126,13 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider protected $eavValidationRules; /** - * @var \Magento\Framework\Registry + * @var Registry * @since 101.0.0 */ protected $registry; /** - * @var \Magento\Framework\App\RequestInterface + * @var RequestInterface * @since 101.0.0 */ protected $request; @@ -152,7 +168,7 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider private $arrayUtils; /** - * @var Filesystem + * @var FileInfo */ private $fileInfo; @@ -168,16 +184,18 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider * @param EavValidationRules $eavValidationRules * @param CategoryCollectionFactory $categoryCollectionFactory * @param StoreManagerInterface $storeManager - * @param \Magento\Framework\Registry $registry + * @param Registry $registry * @param Config $eavConfig - * @param \Magento\Framework\App\RequestInterface $request + * @param RequestInterface $request * @param CategoryFactory $categoryFactory * @param array $meta * @param array $data * @param PoolInterface|null $pool * @param AuthorizationInterface|null $auth * @param ArrayUtils|null $arrayUtils - * @throws \Magento\Framework\Exception\LocalizedException + * @param ScopeOverriddenValue|null $scopeOverriddenValue + * @param ArrayManager|null $arrayManager + * @param FileInfo|null $fileInfo * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -187,15 +205,18 @@ public function __construct( EavValidationRules $eavValidationRules, CategoryCollectionFactory $categoryCollectionFactory, StoreManagerInterface $storeManager, - \Magento\Framework\Registry $registry, + Registry $registry, Config $eavConfig, - \Magento\Framework\App\RequestInterface $request, + RequestInterface $request, CategoryFactory $categoryFactory, array $meta = [], array $data = [], PoolInterface $pool = null, ?AuthorizationInterface $auth = null, - ?ArrayUtils $arrayUtils = null + ?ArrayUtils $arrayUtils = null, + ScopeOverriddenValue $scopeOverriddenValue = null, + ArrayManager $arrayManager = null, + FileInfo $fileInfo = null ) { $this->eavValidationRules = $eavValidationRules; $this->collection = $categoryCollectionFactory->create(); @@ -207,6 +228,10 @@ public function __construct( $this->categoryFactory = $categoryFactory; $this->auth = $auth ?? ObjectManager::getInstance()->get(AuthorizationInterface::class); $this->arrayUtils = $arrayUtils ?? ObjectManager::getInstance()->get(ArrayUtils::class); + $this->scopeOverriddenValue = $scopeOverriddenValue ?: + ObjectManager::getInstance()->get(ScopeOverriddenValue::class); + $this->arrayManager = $arrayManager ?: ObjectManager::getInstance()->get(ArrayManager::class); + $this->fileInfo = $fileInfo ?: ObjectManager::getInstance()->get(FileInfo::class); parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data, $pool); } @@ -244,7 +269,7 @@ private function addUseDefaultValueCheckbox(Category $category, array $meta): ar $canDisplayUseDefault = $attribute->getScope() != EavAttributeInterface::SCOPE_GLOBAL_TEXT && $category->getId() && $category->getStoreId(); - $attributePath = $this->getArrayManager()->findPath($attributeCode, $meta); + $attributePath = $this->arrayManager->findPath($attributeCode, $meta); if (!$attributePath || !$canDisplayUseDefault @@ -253,14 +278,14 @@ private function addUseDefaultValueCheckbox(Category $category, array $meta): ar continue; } - $meta = $this->getArrayManager()->merge( + $meta = $this->arrayManager->merge( [$attributePath, 'arguments/data/config'], $meta, [ 'service' => [ 'template' => 'ui/form/element/helper/service', ], - 'disabled' => !$this->getScopeOverriddenValue()->containsValue( + 'disabled' => !$this->scopeOverriddenValue->containsValue( CategoryInterface::class, $category, $attributeCode, @@ -351,7 +376,7 @@ public function getData() * * @param Type $entityType * @return array - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @since 101.0.0 @@ -369,16 +394,26 @@ public function getAttributesMeta(Type $entityType) foreach ($this->metaProperties as $metaName => $origName) { $value = $attribute->getDataUsingMethod($origName); $meta[$code][$metaName] = $value; + if (in_array($metaName, $this->boolMetaProperties, true)) { + $meta[$code][$metaName] = (bool)$meta[$code][$metaName]; + } if ('frontend_input' === $origName) { $meta[$code]['formElement'] = isset($this->formElement[$value]) ? $this->formElement[$value] : $value; } if ($attribute->usesSource()) { - $meta[$code]['options'] = $attribute->getSource()->getAllOptions(); - foreach ($meta[$code]['options'] as &$option) { + $source = $attribute->getSource(); + $currentCategory = $this->getCurrentCategory(); + if ($source instanceof SpecificSourceInterface && $currentCategory) { + $options = $source->getOptionsFor($currentCategory); + } else { + $options = $source->getAllOptions(); + } + foreach ($options as &$option) { $option['__disableTmpl'] = true; } + $meta[$code]['options'] = $options; } } @@ -394,11 +429,22 @@ public function getAttributesMeta(Type $entityType) if ($category) { $attributeIsLocked = $category->isLockedAttribute($code); $meta[$code]['disabled'] = $attributeIsLocked; - $hasUseConfigField = (bool) array_search('use_config.' . $code, $fields, true); + $hasUseConfigField = (bool)array_search('use_config.' . $code, $fields, true); if ($hasUseConfigField && $meta[$code]['disabled']) { $meta['use_config.' . $code]['disabled'] = true; } } + + if (in_array($code, $this->elementsWithCurrencySymbol, false)) { + $requestScope = $this->request->getParam( + $this->requestScopeFieldName, + Store::DEFAULT_STORE_ID + ); + + $meta[$code]['addbefore'] = $this->storeManager->getStore($requestScope) + ->getBaseCurrency() + ->getCurrencySymbol(); + } } $result = []; @@ -438,7 +484,7 @@ protected function addUseConfigSettings($categoryData) /** * Add use default settings * - * @param \Magento\Catalog\Model\Category $category + * @param Category $category * @param array $categoryData * @return array * @deprecated 101.1.0 @@ -526,13 +572,19 @@ protected function filterFields($categoryData) /** * Converts category image data to acceptable for rendering format * - * @param \Magento\Catalog\Model\Category $category + * @param Category $category * @param array $categoryData * @return array */ private function convertValues($category, $categoryData): array { foreach ($category->getAttributes() as $attributeCode => $attribute) { + if ($attributeCode === 'custom_layout_update_file') { + if (!empty($categoryData['custom_layout_update'])) { + $categoryData['custom_layout_update_file'] + = LayoutUpdate::VALUE_USE_UPDATE_XML; + } + } if (!isset($categoryData[$attributeCode])) { continue; } @@ -541,16 +593,15 @@ private function convertValues($category, $categoryData): array unset($categoryData[$attributeCode]); $fileName = $category->getData($attributeCode); - $fileInfo = $this->getFileInfo(); - if ($fileInfo->isExist($fileName)) { - $stat = $fileInfo->getStat($fileName); - $mime = $fileInfo->getMimeType($fileName); + if ($this->fileInfo->isExist($fileName)) { + $stat = $this->fileInfo->getStat($fileName); + $mime = $this->fileInfo->getMimeType($fileName); // phpcs:ignore Magento2.Functions.DiscouragedFunction $categoryData[$attributeCode][0]['name'] = basename($fileName); - if ($fileInfo->isBeginsWithMediaDirectoryPath($fileName)) { + if ($this->fileInfo->isBeginsWithMediaDirectoryPath($fileName)) { $categoryData[$attributeCode][0]['url'] = $fileName; } else { $categoryData[$attributeCode][0]['url'] = $category->getImageUrl($attributeCode); @@ -592,52 +643,53 @@ protected function getFieldsMap() { return [ 'general' => [ - 'parent', - 'path', - 'is_active', - 'include_in_menu', - 'name', - ], + 'parent', + 'path', + 'is_active', + 'include_in_menu', + 'name', + ], 'content' => [ - 'image', - 'description', - 'landing_page', - ], + 'image', + 'description', + 'landing_page', + ], 'display_settings' => [ - 'display_mode', - 'is_anchor', - 'available_sort_by', - 'use_config.available_sort_by', - 'default_sort_by', - 'use_config.default_sort_by', - 'filter_price_range', - 'use_config.filter_price_range', - ], + 'display_mode', + 'is_anchor', + 'available_sort_by', + 'use_config.available_sort_by', + 'default_sort_by', + 'use_config.default_sort_by', + 'filter_price_range', + 'use_config.filter_price_range', + ], 'search_engine_optimization' => [ - 'url_key', - 'url_key_create_redirect', - 'url_key_group', - 'meta_title', - 'meta_keywords', - 'meta_description', - ], + 'url_key', + 'url_key_create_redirect', + 'url_key_group', + 'meta_title', + 'meta_keywords', + 'meta_description', + ], 'assign_products' => [ - ], + ], 'design' => [ - 'custom_use_parent_settings', - 'custom_apply_to_products', - 'custom_design', - 'page_layout', - 'custom_layout_update', - ], + 'custom_use_parent_settings', + 'custom_apply_to_products', + 'custom_design', + 'page_layout', + 'custom_layout_update', + 'custom_layout_update_file' + ], 'schedule_design_update' => [ - 'custom_design_from', - 'custom_design_to', - ], + 'custom_design_from', + 'custom_design_to', + ], 'category_view_optimization' => [ - ], + ], 'category_permissions' => [ - ], + ], ]; } @@ -651,53 +703,4 @@ private function getFields(): array $fieldsMap = $this->getFieldsMap(); return $this->arrayUtils->flatten($fieldsMap); } - - /** - * Retrieve scope overridden value - * - * @return ScopeOverriddenValue - * @deprecated 101.1.0 - */ - private function getScopeOverriddenValue(): ScopeOverriddenValue - { - if (null === $this->scopeOverriddenValue) { - $this->scopeOverriddenValue = \Magento\Framework\App\ObjectManager::getInstance()->get( - ScopeOverriddenValue::class - ); - } - - return $this->scopeOverriddenValue; - } - - /** - * Retrieve array manager - * - * @return ArrayManager - * @deprecated 101.1.0 - */ - private function getArrayManager(): ArrayManager - { - if (null === $this->arrayManager) { - $this->arrayManager = \Magento\Framework\App\ObjectManager::getInstance()->get( - ArrayManager::class - ); - } - - return $this->arrayManager; - } - - /** - * Get FileInfo instance - * - * @return FileInfo - * - * @deprecated 101.1.0 - */ - private function getFileInfo(): FileInfo - { - if ($this->fileInfo === null) { - $this->fileInfo = ObjectManager::getInstance()->get(FileInfo::class); - } - return $this->fileInfo; - } } diff --git a/app/code/Magento/Catalog/Model/Config.php b/app/code/Magento/Catalog/Model/Config.php index 5dce940308a4f..c4ff12bbf0f94 100644 --- a/app/code/Magento/Catalog/Model/Config.php +++ b/app/code/Magento/Catalog/Model/Config.php @@ -9,6 +9,8 @@ use Magento\Framework\Serialize\SerializerInterface; /** + * Catalog config model. + * * @SuppressWarnings(PHPMD.LongVariable) * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -133,6 +135,7 @@ class Config extends \Magento\Eav\Model\Config * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Eav\Model\Config $eavConfig * @param SerializerInterface $serializer + * @param array $attributesForPreload * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -149,7 +152,8 @@ public function __construct( \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory $setCollectionFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Eav\Model\Config $eavConfig, - SerializerInterface $serializer = null + SerializerInterface $serializer = null, + $attributesForPreload = [] ) { $this->_scopeConfig = $scopeConfig; $this->_configFactory = $configFactory; @@ -165,7 +169,9 @@ public function __construct( $entityTypeCollectionFactory, $cacheState, $universalFactory, - $serializer + $serializer, + $scopeConfig, + $attributesForPreload ); } diff --git a/app/code/Magento/Catalog/Model/Design.php b/app/code/Magento/Catalog/Model/Design.php index 853bbeac8eb38..fed18a5a60913 100644 --- a/app/code/Magento/Catalog/Model/Design.php +++ b/app/code/Magento/Catalog/Model/Design.php @@ -5,6 +5,9 @@ */ namespace Magento\Catalog\Model; +use Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager as CategoryLayoutManager; +use Magento\Catalog\Model\Product\Attribute\LayoutUpdateManager as ProductLayoutManager; +use Magento\Framework\App\ObjectManager; use \Magento\Framework\TranslateInterface; /** @@ -14,6 +17,7 @@ * * @author Magento Core Team <core@magentocommerce.com> * @since 100.0.2 + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Design extends \Magento\Framework\Model\AbstractModel { @@ -38,6 +42,16 @@ class Design extends \Magento\Framework\Model\AbstractModel */ private $translator; + /** + * @var CategoryLayoutManager + */ + private $categoryLayoutUpdates; + + /** + * @var ProductLayoutManager + */ + private $productLayoutUpdates; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -47,6 +61,9 @@ class Design extends \Magento\Framework\Model\AbstractModel * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection * @param array $data * @param TranslateInterface|null $translator + * @param CategoryLayoutManager|null $categoryLayoutManager + * @param ProductLayoutManager|null $productLayoutManager + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Framework\Model\Context $context, @@ -56,12 +73,17 @@ public function __construct( \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [], - TranslateInterface $translator = null + TranslateInterface $translator = null, + ?CategoryLayoutManager $categoryLayoutManager = null, + ?ProductLayoutManager $productLayoutManager = null ) { $this->_localeDate = $localeDate; $this->_design = $design; - $this->translator = $translator ?: - \Magento\Framework\App\ObjectManager::getInstance()->get(TranslateInterface::class); + $this->translator = $translator ?? ObjectManager::getInstance()->get(TranslateInterface::class); + $this->categoryLayoutUpdates = $categoryLayoutManager + ?? ObjectManager::getInstance()->get(CategoryLayoutManager::class); + $this->productLayoutUpdates = $productLayoutManager + ?? ObjectManager::getInstance()->get(ProductLayoutManager::class); parent::__construct($context, $registry, $resource, $resourceCollection, $data); } @@ -81,12 +103,12 @@ public function applyCustomDesign($design) /** * Get custom layout settings * - * @param \Magento\Catalog\Model\Category|\Magento\Catalog\Model\Product $object + * @param Category|Product $object * @return \Magento\Framework\DataObject */ public function getDesignSettings($object) { - if ($object instanceof \Magento\Catalog\Model\Product) { + if ($object instanceof Product) { $currentCategory = $object->getCategory(); } else { $currentCategory = $object; @@ -97,7 +119,7 @@ public function getDesignSettings($object) $category = $currentCategory->getParentDesignCategory($currentCategory); } - if ($object instanceof \Magento\Catalog\Model\Product) { + if ($object instanceof Product) { if ($category && $category->getCustomApplyToProducts()) { return $this->_mergeSettings($this->_extractSettings($category), $this->_extractSettings($object)); } else { @@ -111,7 +133,7 @@ public function getDesignSettings($object) /** * Extract custom layout settings from category or product object * - * @param \Magento\Catalog\Model\Category|\Magento\Catalog\Model\Product $object + * @param Category|Product $object * @return \Magento\Framework\DataObject */ protected function _extractSettings($object) @@ -140,6 +162,11 @@ protected function _extractSettings($object) )->setLayoutUpdates( (array)$object->getCustomLayoutUpdate() ); + if ($object instanceof Category) { + $this->categoryLayoutUpdates->extractCustomSettings($object, $settings); + } elseif ($object instanceof Product) { + $this->productLayoutUpdates->extractCustomSettings($object, $settings); + } } return $settings; } diff --git a/app/code/Magento/Catalog/Model/Entity/Product/Attribute/Design/Options/Container.php b/app/code/Magento/Catalog/Model/Entity/Product/Attribute/Design/Options/Container.php index 22cb3c3264df5..d9893be3125fe 100644 --- a/app/code/Magento/Catalog/Model/Entity/Product/Attribute/Design/Options/Container.php +++ b/app/code/Magento/Catalog/Model/Entity/Product/Attribute/Design/Options/Container.php @@ -21,7 +21,7 @@ class Container extends \Magento\Eav\Model\Entity\Attribute\Source\Config public function getOptionText($value) { $options = $this->getAllOptions(); - if (sizeof($options) > 0) { + if (count($options) > 0) { foreach ($options as $option) { if (isset($option['value']) && $option['value'] == $value) { return __($option['label']); diff --git a/app/code/Magento/Catalog/Model/ImageUploader.php b/app/code/Magento/Catalog/Model/ImageUploader.php index 825823276261f..0c3e008fa8bb5 100644 --- a/app/code/Magento/Catalog/Model/ImageUploader.php +++ b/app/code/Magento/Catalog/Model/ImageUploader.php @@ -191,12 +191,12 @@ public function getFilePath($path, $imageName) * Checking file for moving and move it * * @param string $imageName - * + * @param bool $returnRelativePath * @return string * * @throws \Magento\Framework\Exception\LocalizedException */ - public function moveFileFromTmp($imageName) + public function moveFileFromTmp($imageName, $returnRelativePath = false) { $baseTmpPath = $this->getBaseTmpPath(); $basePath = $this->getBasePath(); @@ -226,7 +226,7 @@ public function moveFileFromTmp($imageName) ); } - return $imageName; + return $returnRelativePath ? $baseImagePath : $imageName; } /** diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product.php index af1cda41d8c46..c18404bda1fc8 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product.php @@ -137,10 +137,11 @@ protected function executeAction($ids) /** @var Product\Action\Rows $action */ $action = $this->rowsActionFactory->create(); - if ($indexer->isWorking()) { + if ($indexer->isScheduled()) { $action->execute($ids, true); + } else { + $action->execute($ids); } - $action->execute($ids); return $this; } diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Rows.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Rows.php index 3bd4910767587..5d81c1405efe0 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Rows.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Rows.php @@ -5,6 +5,25 @@ */ namespace Magento\Catalog\Model\Indexer\Category\Product\Action; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Indexer\CacheContext; +use Magento\Framework\Event\ManagerInterface as EventManagerInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\ResourceConnection; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Framework\DB\Query\Generator as QueryGenerator; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Catalog\Model\Config; +use Magento\Catalog\Model\Category; +use Magento\Framework\Indexer\IndexerRegistry; +use Magento\Catalog\Model\Indexer\Product\Category as ProductCategoryIndexer; + +/** + * Reindex multiple rows action. + * + * @package Magento\Catalog\Model\Indexer\Category\Product\Action + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class Rows extends \Magento\Catalog\Model\Indexer\Category\Product\AbstractAction { /** @@ -14,25 +33,121 @@ class Rows extends \Magento\Catalog\Model\Indexer\Category\Product\AbstractActio */ protected $limitationByCategories; + /** + * @var CacheContext + */ + private $cacheContext; + + /** + * @var EventManagerInterface|null + */ + private $eventManager; + + /** + * @var IndexerRegistry + */ + private $indexerRegistry; + + /** + * @param ResourceConnection $resource + * @param StoreManagerInterface $storeManager + * @param Config $config + * @param QueryGenerator|null $queryGenerator + * @param MetadataPool|null $metadataPool + * @param CacheContext|null $cacheContext + * @param EventManagerInterface|null $eventManager + * @param IndexerRegistry|null $indexerRegistry + */ + public function __construct( + ResourceConnection $resource, + StoreManagerInterface $storeManager, + Config $config, + QueryGenerator $queryGenerator = null, + MetadataPool $metadataPool = null, + CacheContext $cacheContext = null, + EventManagerInterface $eventManager = null, + IndexerRegistry $indexerRegistry = null + ) { + parent::__construct($resource, $storeManager, $config, $queryGenerator, $metadataPool); + $this->cacheContext = $cacheContext ?: ObjectManager::getInstance()->get(CacheContext::class); + $this->eventManager = $eventManager ?: ObjectManager::getInstance()->get(EventManagerInterface::class); + $this->indexerRegistry = $indexerRegistry ?: ObjectManager::getInstance()->get(IndexerRegistry::class); + } + /** * Refresh entities index * * @param int[] $entityIds * @param bool $useTempTable * @return $this + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function execute(array $entityIds = [], $useTempTable = false) { - $this->limitationByCategories = $entityIds; + foreach ($entityIds as $entityId) { + $this->limitationByCategories[] = (int)$entityId; + $path = $this->getPathFromCategoryId($entityId); + if (!empty($path)) { + $pathIds = explode('/', $path); + foreach ($pathIds as $pathId) { + $this->limitationByCategories[] = (int)$pathId; + } + } + } + $this->limitationByCategories = array_unique($this->limitationByCategories); $this->useTempTable = $useTempTable; + $indexer = $this->indexerRegistry->get(ProductCategoryIndexer::INDEXER_ID); + $workingState = $indexer->isWorking(); - $this->removeEntries(); + if ($useTempTable && !$workingState && $indexer->isScheduled()) { + foreach ($this->storeManager->getStores() as $store) { + $this->connection->truncateTable($this->getIndexTable($store->getId())); + } + } else { + $this->removeEntries(); + } $this->reindex(); + if ($useTempTable && !$workingState && $indexer->isScheduled()) { + foreach ($this->storeManager->getStores() as $store) { + $removalCategoryIds = array_diff($this->limitationByCategories, [$this->getRootCategoryId($store)]); + $this->connection->delete( + $this->tableMaintainer->getMainTable($store->getId()), + ['category_id IN (?)' => $removalCategoryIds] + ); + $select = $this->connection->select() + ->from($this->tableMaintainer->getMainReplicaTable($store->getId())); + $this->connection->query( + $this->connection->insertFromSelect( + $select, + $this->tableMaintainer->getMainTable($store->getId()), + [], + AdapterInterface::INSERT_ON_DUPLICATE + ) + ); + } + } + + $this->registerCategories($entityIds); + $this->eventManager->dispatch('clean_cache_by_tags', ['object' => $this->cacheContext]); + return $this; } + /** + * Register categories assigned to products + * + * @param array $categoryIds + * @return void + */ + private function registerCategories(array $categoryIds) + { + if ($categoryIds) { + $this->cacheContext->registerEntities(Category::CACHE_TAG, $categoryIds); + } + } + /** * Return array of all category root IDs + tree root ID * diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Category/Action/Rows.php b/app/code/Magento/Catalog/Model/Indexer/Product/Category/Action/Rows.php index 15ba6c8f3758b..ec3d0d57330ec 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Category/Action/Rows.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Category/Action/Rows.php @@ -15,6 +15,9 @@ use Magento\Framework\Event\ManagerInterface as EventManagerInterface; use Magento\Framework\Indexer\CacheContext; use Magento\Store\Model\StoreManagerInterface; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Indexer\IndexerRegistry; +use Magento\Catalog\Model\Indexer\Category\Product as CategoryProductIndexer; /** * Category rows indexer. @@ -40,6 +43,11 @@ class Rows extends \Magento\Catalog\Model\Indexer\Category\Product\AbstractActio */ private $eventManager; + /** + * @var IndexerRegistry + */ + private $indexerRegistry; + /** * @param ResourceConnection $resource * @param StoreManagerInterface $storeManager @@ -48,6 +56,7 @@ class Rows extends \Magento\Catalog\Model\Indexer\Category\Product\AbstractActio * @param MetadataPool|null $metadataPool * @param CacheContext|null $cacheContext * @param EventManagerInterface|null $eventManager + * @param IndexerRegistry|null $indexerRegistry */ public function __construct( ResourceConnection $resource, @@ -56,11 +65,13 @@ public function __construct( QueryGenerator $queryGenerator = null, MetadataPool $metadataPool = null, CacheContext $cacheContext = null, - EventManagerInterface $eventManager = null + EventManagerInterface $eventManager = null, + IndexerRegistry $indexerRegistry = null ) { parent::__construct($resource, $storeManager, $config, $queryGenerator, $metadataPool); $this->cacheContext = $cacheContext ?: ObjectManager::getInstance()->get(CacheContext::class); $this->eventManager = $eventManager ?: ObjectManager::getInstance()->get(EventManagerInterface::class); + $this->indexerRegistry = $indexerRegistry ?: ObjectManager::getInstance()->get(IndexerRegistry::class); } /** @@ -78,12 +89,37 @@ public function execute(array $entityIds = [], $useTempTable = false) $this->limitationByProducts = $idsToBeReIndexed; $this->useTempTable = $useTempTable; + $indexer = $this->indexerRegistry->get(CategoryProductIndexer::INDEXER_ID); + $workingState = $indexer->isWorking(); $affectedCategories = $this->getCategoryIdsFromIndex($idsToBeReIndexed); - $this->removeEntries(); - + if ($useTempTable && !$workingState && $indexer->isScheduled()) { + foreach ($this->storeManager->getStores() as $store) { + $this->connection->truncateTable($this->getIndexTable($store->getId())); + } + } else { + $this->removeEntries(); + } $this->reindex(); + if ($useTempTable && !$workingState && $indexer->isScheduled()) { + foreach ($this->storeManager->getStores() as $store) { + $this->connection->delete( + $this->tableMaintainer->getMainTable($store->getId()), + ['product_id IN (?)' => $this->limitationByProducts] + ); + $select = $this->connection->select() + ->from($this->tableMaintainer->getMainReplicaTable($store->getId())); + $this->connection->query( + $this->connection->insertFromSelect( + $select, + $this->tableMaintainer->getMainTable($store->getId()), + [], + AdapterInterface::INSERT_ON_DUPLICATE + ) + ); + } + } $affectedCategories = array_merge($affectedCategories, $this->getCategoryIdsFromIndex($idsToBeReIndexed)); diff --git a/app/code/Magento/Catalog/Model/Layer/Filter/DataProvider/Price.php b/app/code/Magento/Catalog/Model/Layer/Filter/DataProvider/Price.php index d1aee8c4c5ba6..229844fbe84b5 100644 --- a/app/code/Magento/Catalog/Model/Layer/Filter/DataProvider/Price.php +++ b/app/code/Magento/Catalog/Model/Layer/Filter/DataProvider/Price.php @@ -10,6 +10,9 @@ use Magento\Framework\Registry; use Magento\Store\Model\ScopeInterface; +/** + * Data provider for price filter in layered navigation + */ class Price { /** @@ -103,6 +106,8 @@ public function __construct( } /** + * Getter for interval + * * @return array */ public function getInterval() @@ -111,6 +116,8 @@ public function getInterval() } /** + * Setter for interval + * * @param array $interval * @return void */ @@ -120,6 +127,10 @@ public function setInterval($interval) } /** + * Retrieves price layered navigation modes + * + * @see RANGE_CALCULATION_AUTO + * * @return mixed */ public function getRangeCalculationValue() @@ -131,6 +142,8 @@ public function getRangeCalculationValue() } /** + * Retrieves range step + * * @return mixed */ public function getRangeStepValue() @@ -142,6 +155,8 @@ public function getRangeStepValue() } /** + * Retrieves one price interval + * * @return mixed */ public function getOnePriceIntervalValue() @@ -179,6 +194,8 @@ public function getRangeMaxIntervalsValue() } /** + * Retrieves Catalog Layer object + * * @return Layer */ public function getLayer() @@ -276,6 +293,8 @@ public function getMaxPrice() } /** + * Retrieve list of prior filters + * * @param string $filterParams * @return array */ @@ -310,7 +329,7 @@ public function validateFilter($filter) return false; } foreach ($filter as $v) { - if ($v !== '' && $v !== '0' && (double)$v <= 0 || is_infinite((double)$v)) { + if ($v !== '' && $v !== '0' && (!is_numeric($v) || (double)$v <= 0 || is_infinite((double)$v))) { return false; } } @@ -339,6 +358,8 @@ public function getResetValue() } /** + * Getter for prior intervals + * * @return array */ public function getPriorIntervals() @@ -347,6 +368,8 @@ public function getPriorIntervals() } /** + * Setter for prior intervals + * * @param array $priorInterval * @return void */ @@ -356,6 +379,8 @@ public function setPriorIntervals($priorInterval) } /** + * Get Resource model for price filter + * * @return \Magento\Catalog\Model\ResourceModel\Layer\Filter\Price */ public function getResource() @@ -364,6 +389,8 @@ public function getResource() } /** + * Retrieves additional request data + * * @return string */ public function getAdditionalRequestData() diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index 8092ff3eb9d4a..7015fa0295cfb 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -5,7 +5,6 @@ */ namespace Magento\Catalog\Model; -use Magento\Authorization\Model\UserContextInterface; use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterface; use Magento\Catalog\Api\Data\ProductInterface; @@ -15,7 +14,6 @@ use Magento\Framework\Api\AttributeValueFactory; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\ObjectManager; -use Magento\Framework\AuthorizationInterface; use Magento\Framework\DataObject\IdentityInterface; use Magento\Framework\Pricing\SaleableInterface; @@ -355,16 +353,6 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements */ private $filterCustomAttribute; - /** - * @var UserContextInterface - */ - private $userContext; - - /** - * @var AuthorizationInterface - */ - private $authorization; - /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -837,11 +825,12 @@ public function getStoreIds() } foreach ($websiteIds as $websiteId) { $websiteStores = $this->_storeManager->getWebsite($websiteId)->getStoreIds(); - foreach ($websiteStores as $websiteStore) { - $storeIds []= $websiteStore; - } + $storeIds[] = $websiteStores; } } + if ($storeIds) { + $storeIds = array_merge(...$storeIds); + } $this->setStoreIds($storeIds); } return $this->getData('store_ids'); @@ -874,34 +863,6 @@ public function getAttributes($groupId = null, $skipSuper = false) return $attributes; } - /** - * Get user context. - * - * @return UserContextInterface - */ - private function getUserContext(): UserContextInterface - { - if (!$this->userContext) { - $this->userContext = ObjectManager::getInstance()->get(UserContextInterface::class); - } - - return $this->userContext; - } - - /** - * Get authorization service. - * - * @return AuthorizationInterface - */ - private function getAuthorization(): AuthorizationInterface - { - if (!$this->authorization) { - $this->authorization = ObjectManager::getInstance()->get(AuthorizationInterface::class); - } - - return $this->authorization; - } - /** * Check product options and type options and save them, too * @@ -919,22 +880,6 @@ public function beforeSave() $this->getTypeInstance()->beforeSave($this); - //Validate changing of design. - $userType = $this->getUserContext()->getUserType(); - if (( - $userType === UserContextInterface::USER_TYPE_ADMIN - || $userType === UserContextInterface::USER_TYPE_INTEGRATION - ) - && !$this->getAuthorization()->isAllowed('Magento_Catalog::edit_product_design') - ) { - $this->setData('custom_design', $this->getOrigData('custom_design')); - $this->setData('page_layout', $this->getOrigData('page_layout')); - $this->setData('options_container', $this->getOrigData('options_container')); - $this->setData('custom_layout_update', $this->getOrigData('custom_layout_update')); - $this->setData('custom_design_from', $this->getOrigData('custom_design_from')); - $this->setData('custom_design_to', $this->getOrigData('custom_design_to')); - } - $hasOptions = false; $hasRequiredOptions = false; @@ -1326,12 +1271,11 @@ public function getSpecialToDate() public function getRelatedProducts() { if (!$this->hasRelatedProducts()) { - $products = []; - $collection = $this->getRelatedProductCollection(); - foreach ($collection as $product) { - $products[] = $product; + //Loading all linked products. + $this->getProductLinks(); + if (!$this->hasRelatedProducts()) { + $this->setRelatedProducts([]); } - $this->setRelatedProducts($products); } return $this->getData('related_products'); } @@ -1388,12 +1332,13 @@ public function getRelatedLinkCollection() public function getUpSellProducts() { if (!$this->hasUpSellProducts()) { - $products = []; - foreach ($this->getUpSellProductCollection() as $product) { - $products[] = $product; + //Loading all linked products. + $this->getProductLinks(); + if (!$this->hasUpSellProducts()) { + $this->setUpSellProducts([]); } - $this->setUpSellProducts($products); } + return $this->getData('up_sell_products'); } @@ -1449,12 +1394,13 @@ public function getUpSellLinkCollection() public function getCrossSellProducts() { if (!$this->hasCrossSellProducts()) { - $products = []; - foreach ($this->getCrossSellProductCollection() as $product) { - $products[] = $product; + //Loading all linked products. + $this->getProductLinks(); + if (!$this->hasCrossSellProducts()) { + $this->setCrossSellProducts([]); } - $this->setCrossSellProducts($products); } + return $this->getData('cross_sell_products'); } @@ -1510,7 +1456,11 @@ public function getCrossSellLinkCollection() public function getProductLinks() { if ($this->_links === null) { - $this->_links = $this->getLinkRepository()->getList($this); + if ($this->getSku() && $this->getId()) { + $this->_links = $this->getLinkRepository()->getList($this); + } else { + $this->_links = []; + } } return $this->_links; } diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php index e26717e47274c..68aeabfc70d34 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php @@ -97,17 +97,16 @@ protected function _getWebsiteCurrencyRates() ); foreach ($this->_storeManager->getWebsites() as $website) { /* @var $website \Magento\Store\Model\Website */ - if ($website->getBaseCurrencyCode() != $baseCurrency) { + $websiteBaseCurrency = $website->getBaseCurrencyCode(); + if ($websiteBaseCurrency !== $baseCurrency) { $rate = $this->_currencyFactory->create()->load( $baseCurrency - )->getRate( - $website->getBaseCurrencyCode() - ); + )->getRate($websiteBaseCurrency); if (!$rate) { $rate = 1; } $this->_rates[$website->getId()] = [ - 'code' => $website->getBaseCurrencyCode(), + 'code' => $websiteBaseCurrency, 'rate' => $rate, ]; } else { @@ -187,6 +186,7 @@ public function validate($object) } $compare = implode( '-', + // phpcs:ignore Magento2.Performance.ForeachArrayMerge array_merge( [$priceRow['website_id'], $priceRow['cust_group']], $this->_getAdditionalUniqueFields($priceRow) @@ -210,6 +210,7 @@ public function validate($object) if ($price['website_id'] == 0) { $compare = implode( '-', + // phpcs:ignore Magento2.Performance.ForeachArrayMerge array_merge( [$price['website_id'], $price['cust_group']], $this->_getAdditionalUniqueFields($price) @@ -234,6 +235,7 @@ public function validate($object) $globalCompare = implode( '-', + // phpcs:ignore Magento2.Performance.ForeachArrayMerge array_merge([0, $priceRow['cust_group']], $this->_getAdditionalUniqueFields($priceRow)) ); $websiteCurrency = $rates[$priceRow['website_id']]['code']; @@ -279,6 +281,7 @@ public function preparePriceData(array $priceData, $productTypeId, $websiteId) if (!array_filter($v)) { continue; } + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $key = implode('-', array_merge([$v['cust_group']], $this->_getAdditionalUniqueFields($v))); if ($v['website_id'] == $websiteId) { $data[$key] = $v; diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php new file mode 100644 index 0000000000000..fa5a218824eea --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/LayoutUpdate.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Attribute\Backend; + +use Magento\Catalog\Model\AbstractModel; +use Magento\Catalog\Model\Attribute\Backend\AbstractLayoutUpdate; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\LayoutUpdateManager; + +/** + * Allows to select a layout file to merge when rendering the product's page. + */ +class LayoutUpdate extends AbstractLayoutUpdate +{ + + /** + * @var LayoutUpdateManager + */ + private $manager; + + /** + * @param LayoutUpdateManager $manager + */ + public function __construct(LayoutUpdateManager $manager) + { + $this->manager = $manager; + } + + /** + * @inheritDoc + * + * @param AbstractModel|Product $forModel + */ + protected function listAvailableValues(AbstractModel $forModel): array + { + return $this->manager->fetchAvailableFiles($forModel); + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php index f1943bc108878..0daa1dfb5c8eb 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php @@ -96,12 +96,7 @@ public function execute($entity, $arguments = []) $productId = (int)$entity->getData($identifierField); // prepare original data to compare - $origPrices = []; - $originalId = $entity->getOrigData($identifierField); - if (empty($originalId) || $entity->getData($identifierField) == $originalId) { - $origPrices = $entity->getOrigData($attribute->getName()); - } - + $origPrices = $entity->getOrigData($attribute->getName()); $old = $this->prepareOldTierPriceToCompare($origPrices); // prepare data for save $new = $this->prepareNewDataForSave($priceRows, $isGlobal); diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php b/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php new file mode 100644 index 0000000000000..92ae989500076 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Attribute/LayoutUpdateManager.php @@ -0,0 +1,166 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Attribute; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Product; +use Magento\Framework\App\Area; +use Magento\Framework\DataObject; +use Magento\Framework\View\Design\Theme\FlyweightFactory; +use Magento\Framework\View\DesignInterface; +use Magento\Framework\View\Model\Layout\Merge as LayoutProcessor; +use Magento\Framework\View\Model\Layout\MergeFactory as LayoutProcessorFactory; + +/** + * Manage available layout updates for products. + */ +class LayoutUpdateManager +{ + + /** + * @var FlyweightFactory + */ + private $themeFactory; + + /** + * @var DesignInterface + */ + private $design; + + /** + * @var LayoutProcessorFactory + */ + private $layoutProcessorFactory; + + /** + * @var LayoutProcessor|null + */ + private $layoutProcessor; + + /** + * @param FlyweightFactory $themeFactory + * @param DesignInterface $design + * @param LayoutProcessorFactory $layoutProcessorFactory + */ + public function __construct( + FlyweightFactory $themeFactory, + DesignInterface $design, + LayoutProcessorFactory $layoutProcessorFactory + ) { + $this->themeFactory = $themeFactory; + $this->design = $design; + $this->layoutProcessorFactory = $layoutProcessorFactory; + } + + /** + * Adopt product's SKU to be used as layout handle. + * + * @param ProductInterface $product + * @return string + */ + private function sanitizeSku(ProductInterface $product): string + { + return rawurlencode($product->getSku()); + } + + /** + * Get the processor instance. + * + * @return LayoutProcessor + */ + private function getLayoutProcessor(): LayoutProcessor + { + if (!$this->layoutProcessor) { + $this->layoutProcessor = $this->layoutProcessorFactory->create( + [ + 'theme' => $this->themeFactory->create( + $this->design->getConfigurationDesignTheme(Area::AREA_FRONTEND) + ) + ] + ); + $this->themeFactory = null; + $this->design = null; + } + + return $this->layoutProcessor; + } + + /** + * Fetch list of available files/handles for the product. + * + * @param ProductInterface $product + * @return string[] + */ + public function fetchAvailableFiles(ProductInterface $product): array + { + if (!$product->getSku()) { + return []; + } + + $identifier = $this->sanitizeSku($product); + $handles = $this->getLayoutProcessor()->getAvailableHandles(); + + return array_filter( + array_map( + function (string $handle) use ($identifier) : ?string { + preg_match( + '/^catalog\_product\_view\_selectable\_' .preg_quote($identifier) .'\_([a-z0-9]+)/i', + $handle, + $selectable + ); + if (!empty($selectable[1])) { + return $selectable[1]; + } + + return null; + }, + $handles + ) + ); + } + + /** + * Extract custom layout attribute value. + * + * @param ProductInterface $product + * @return mixed + */ + private function extractAttributeValue(ProductInterface $product) + { + if ($product instanceof Product && !$product->hasData(ProductInterface::CUSTOM_ATTRIBUTES)) { + return $product->getData('custom_layout_update_file'); + } + if ($attr = $product->getCustomAttribute('custom_layout_update_file')) { + return $attr->getValue(); + } + + return null; + } + + /** + * Extract selected custom layout settings. + * + * If no update is selected none will apply. + * + * @param ProductInterface $product + * @param DataObject $intoSettings + * @return void + */ + public function extractCustomSettings(ProductInterface $product, DataObject $intoSettings): void + { + if ($product->getSku() && $value = $this->extractAttributeValue($product)) { + $handles = $intoSettings->getPageLayoutHandles() ?? []; + $handles = array_merge_recursive( + $handles, + ['selectable' => $this->sanitizeSku($product) . '_' . $value] + ); + $intoSettings->setPageLayoutHandles($handles); + } + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php b/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php new file mode 100644 index 0000000000000..0ddb528e768cc --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Source/LayoutUpdate.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Attribute\Source; + +use Magento\Catalog\Model\Attribute\Source\AbstractLayoutUpdate; +use Magento\Catalog\Model\Product\Attribute\LayoutUpdateManager; +use Magento\Framework\Api\CustomAttributesDataInterface; + +/** + * List of layout updates available for a product. + */ +class LayoutUpdate extends AbstractLayoutUpdate +{ + /** + * @var LayoutUpdateManager + */ + private $manager; + + /** + * @param LayoutUpdateManager $manager + */ + public function __construct(LayoutUpdateManager $manager) + { + $this->manager = $manager; + } + + /** + * @inheritDoc + */ + protected function listAvailableOptions(CustomAttributesDataInterface $entity): array + { + return $this->manager->fetchAvailableFiles($entity); + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Authorization.php b/app/code/Magento/Catalog/Model/Product/Authorization.php new file mode 100644 index 0000000000000..b8aa8f70ba70f --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Authorization.php @@ -0,0 +1,170 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Product as ProductModel; +use Magento\Catalog\Model\ProductFactory; +use Magento\Eav\Api\Data\AttributeInterface; +use Magento\Framework\AuthorizationInterface; +use Magento\Framework\Exception\AuthorizationException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Catalog\Model\Product\Attribute\Backend\LayoutUpdate; + +/** + * Additional authorization for product operations. + */ +class Authorization +{ + /** + * @var AuthorizationInterface + */ + private $authorization; + + /** + * @var ProductFactory + */ + private $productFactory; + + /** + * @param AuthorizationInterface $authorization + * @param ProductFactory $factory + */ + public function __construct(AuthorizationInterface $authorization, ProductFactory $factory) + { + $this->authorization = $authorization; + $this->productFactory = $factory; + } + + /** + * Extract attribute value from the model. + * + * @param ProductModel $product + * @param AttributeInterface $attr + * @return mixed + * @throws \RuntimeException When no new value is present. + */ + private function extractAttributeValue(ProductModel $product, AttributeInterface $attr) + { + if ($product->hasData($attr->getAttributeCode())) { + $newValue = $product->getData($attr->getAttributeCode()); + } elseif ($product->hasData(ProductModel::CUSTOM_ATTRIBUTES) + && $attrValue = $product->getCustomAttribute($attr->getAttributeCode()) + ) { + $newValue = $attrValue->getValue(); + } else { + throw new \RuntimeException('No new value is present'); + } + + if (empty($newValue) + || ($attr->getBackend() instanceof LayoutUpdate + && ($newValue === LayoutUpdate::VALUE_USE_UPDATE_XML || $newValue === LayoutUpdate::VALUE_NO_UPDATE) + ) + ) { + $newValue = null; + } + + return $newValue; + } + + /** + * Prepare old values to compare to. + * + * @param AttributeInterface $attribute + * @param array|null $oldProduct + * @return array + */ + private function fetchOldValues(AttributeInterface $attribute, ?array $oldProduct): array + { + $attrCode = $attribute->getAttributeCode(); + if ($oldProduct) { + //New value may only be the saved value + $oldValues = [!empty($oldProduct[$attrCode]) ? $oldProduct[$attrCode] : null]; + if (empty($oldValues[0])) { + $oldValues[0] = null; + } + } else { + //New value can be empty or default + $oldValues[] = $attribute->getDefaultValue(); + } + + return $oldValues; + } + + /** + * Check whether the product has changed. + * + * @param ProductModel $product + * @param array|null $oldProduct + * @return bool + */ + private function hasProductChanged(ProductModel $product, ?array $oldProduct = null): bool + { + $designAttributes = [ + 'custom_design', + 'page_layout', + 'options_container', + 'custom_layout_update', + 'custom_design_from', + 'custom_design_to', + 'custom_layout_update_file' + ]; + $attributes = $product->getAttributes(); + + foreach ($designAttributes as $designAttribute) { + if (!array_key_exists($designAttribute, $attributes)) { + continue; + } + $attribute = $attributes[$designAttribute]; + $oldValues = $this->fetchOldValues($attribute, $oldProduct); + try { + $newValue = $this->extractAttributeValue($product, $attribute); + } catch (\RuntimeException $exception) { + //No new value + continue; + } + if (!in_array($newValue, $oldValues, true)) { + return true; + } + } + + return false; + } + + /** + * Authorize saving of a product. + * + * @throws AuthorizationException + * @throws NoSuchEntityException When product with invalid ID given. + * @param ProductInterface|ProductModel $product + * @return void + */ + public function authorizeSavingOf(ProductInterface $product): void + { + if (!$this->authorization->isAllowed('Magento_Catalog::edit_product_design')) { + $oldData = null; + if ($product->getId()) { + if ($product->getOrigData()) { + $oldData = $product->getOrigData(); + } else { + /** @var ProductModel $savedProduct */ + $savedProduct = $this->productFactory->create(); + $savedProduct->load($product->getId()); + if (!$savedProduct->getSku()) { + throw NoSuchEntityException::singleField('id', $product->getId()); + } + $oldData = $product->getOrigData(); + } + } + if ($this->hasProductChanged($product, $oldData)) { + throw new AuthorizationException(__('Not allowed to edit the product\'s design attributes')); + } + } + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Copier.php b/app/code/Magento/Catalog/Model/Product/Copier.php index 44ebdf0f1f283..a7f7bad1a5167 100644 --- a/app/code/Magento/Catalog/Model/Product/Copier.php +++ b/app/code/Magento/Catalog/Model/Product/Copier.php @@ -6,7 +6,9 @@ namespace Magento\Catalog\Model\Product; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Attribute\ScopeOverriddenValue; use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\ProductFactory; /** * Catalog product copier. @@ -28,7 +30,7 @@ class Copier protected $copyConstructor; /** - * @var \Magento\Catalog\Model\ProductFactory + * @var ProductFactory */ protected $productFactory; @@ -36,17 +38,24 @@ class Copier * @var \Magento\Framework\EntityManager\MetadataPool */ protected $metadataPool; + /** + * @var ScopeOverriddenValue + */ + private $scopeOverriddenValue; /** * @param CopyConstructorInterface $copyConstructor - * @param \Magento\Catalog\Model\ProductFactory $productFactory + * @param ProductFactory $productFactory + * @param ScopeOverriddenValue $scopeOverriddenValue */ public function __construct( CopyConstructorInterface $copyConstructor, - \Magento\Catalog\Model\ProductFactory $productFactory + ProductFactory $productFactory, + ScopeOverriddenValue $scopeOverriddenValue ) { $this->productFactory = $productFactory; $this->copyConstructor = $copyConstructor; + $this->scopeOverriddenValue = $scopeOverriddenValue; } /** @@ -121,19 +130,20 @@ private function setStoresUrl(Product $product, Product $duplicate) : void $storeIds = $duplicate->getStoreIds(); $productId = $product->getId(); $productResource = $product->getResource(); - $defaultUrlKey = $productResource->getAttributeRawValue( - $productId, - 'url_key', - \Magento\Store\Model\Store::DEFAULT_STORE_ID - ); $duplicate->setData('save_rewrites_history', false); foreach ($storeIds as $storeId) { + $useDefault = !$this->scopeOverriddenValue->containsValue( + ProductInterface::class, + $product, + 'url_key', + $storeId + ); + if ($useDefault) { + continue; + } $isDuplicateSaved = false; $duplicate->setStoreId($storeId); $urlKey = $productResource->getAttributeRawValue($productId, 'url_key', $storeId); - if ($urlKey === $defaultUrlKey) { - continue; - } do { $urlKey = $this->modifyUrl($urlKey); $duplicate->setUrlKey($urlKey); diff --git a/app/code/Magento/Catalog/Model/Product/Filter/DateTime.php b/app/code/Magento/Catalog/Model/Product/Filter/DateTime.php new file mode 100644 index 0000000000000..93b7d55458a9f --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Filter/DateTime.php @@ -0,0 +1,67 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Filter; + +use Magento\Framework\Stdlib\DateTime as StdlibDateTime; +use Magento\Framework\Stdlib\DateTime\Filter\DateTime as StdlibDateTimeFilter; + +/** + * Product datetime fields values filter + */ +class DateTime implements \Zend_Filter_Interface +{ + /** + * @var StdlibDateTimeFilter + */ + private $stdlibDateTimeFilter; + + /** + * Initializes dependencies. + * + * @param StdlibDateTimeFilter $stdlibDateTimeFilter + */ + public function __construct(StdlibDateTimeFilter $stdlibDateTimeFilter) + { + $this->stdlibDateTimeFilter = $stdlibDateTimeFilter; + } + + /** + * Convert datetime from locale format to internal format; + * + * Make an additional check for MySql date format which is wrongly parsed by IntlDateFormatter + * + * @param mixed $value + * @return mixed|string + * @throws \Exception + */ + public function filter($value) + { + if (is_string($value)) { + $value = $this->createDateFromMySqlFormat($value) ?? $value; + } + return $this->stdlibDateTimeFilter->filter($value); + } + + /** + * Parse a string in MySql date format into a new DateTime object + * + * @param string $value + * @return \DateTime|null + */ + private function createDateFromMySqlFormat(string $value): ?\DateTime + { + $datetime = date_create_from_format(StdlibDateTime::DATETIME_PHP_FORMAT, $value); + if ($datetime === false) { + $datetime = date_create_from_format(StdlibDateTime::DATE_PHP_FORMAT, $value); + if ($datetime !== false) { + $datetime->setTime(0, 0, 0, 0); + } + } + return $datetime instanceof \DateTime ? $datetime : null; + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php index b374b754d7de1..225a3a4c44a9b 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php @@ -162,7 +162,7 @@ public function execute($product, $arguments = []) if (!empty($image['removed'])) { $clearImages[] = $image['file']; - } elseif (empty($image['value_id'])) { + } elseif (empty($image['value_id']) || !empty($image['recreate'])) { $newFile = $this->moveImageFromTmp($image['file']); $image['new_file'] = $newFile; $newImages[$image['file']] = $image; diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php b/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php index 9e5cf084c25a1..a9afb7cec45e2 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/GalleryManagement.php @@ -71,10 +71,12 @@ public function create($sku, ProductAttributeMediaGalleryEntryInterface $entry) $product->setMediaGalleryEntries($existingMediaGalleryEntries); try { $product = $this->productRepository->save($product); - } catch (InputException $inputException) { - throw $inputException; } catch (\Exception $e) { - throw new StateException(__("The product can't be saved.")); + if ($e instanceof InputException) { + throw $e; + } else { + throw new StateException(__("The product can't be saved.")); + } } foreach ($product->getMediaGalleryEntries() as $entry) { @@ -98,19 +100,13 @@ public function update($sku, ProductAttributeMediaGalleryEntryInterface $entry) ); } $found = false; + $entryTypes = (array)$entry->getTypes(); foreach ($existingMediaGalleryEntries as $key => $existingEntry) { - $entryTypes = (array)$entry->getTypes(); - $existingEntryTypes = (array)$existingMediaGalleryEntries[$key]->getTypes(); - $existingMediaGalleryEntries[$key]->setTypes(array_diff($existingEntryTypes, $entryTypes)); + $existingEntryTypes = (array)$existingEntry->getTypes(); + $existingEntry->setTypes(array_diff($existingEntryTypes, $entryTypes)); if ($existingEntry->getId() == $entry->getId()) { $found = true; - - $file = $entry->getContent(); - - if ($file && $file->getBase64EncodedData() || $entry->getFile()) { - $entry->setId(null); - } $existingMediaGalleryEntries[$key] = $entry; } } diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/UpdateHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/UpdateHandler.php index 189135776b68b..049846ef36490 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/UpdateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/UpdateHandler.php @@ -5,6 +5,7 @@ */ namespace Magento\Catalog\Model\Product\Gallery; +use Magento\Catalog\Model\ResourceModel\Product\Gallery; use Magento\Framework\EntityManager\Operation\ExtensionInterface; /** @@ -75,6 +76,16 @@ protected function processNewImage($product, array &$image) $image['value_id'], $product->getData($this->metadata->getLinkField()) ); + } elseif (!empty($image['recreate'])) { + $data['value_id'] = $image['value_id']; + $data['value'] = $image['file']; + $data['attribute_id'] = $this->getAttribute()->getAttributeId(); + + if (!empty($image['media_type'])) { + $data['media_type'] = $image['media_type']; + } + + $this->resourceModel->saveDataRow(Gallery::GALLERY_TABLE, $data); } return $data; diff --git a/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php b/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php index 4b7a623b15c19..c0f4e83ef3de4 100644 --- a/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php +++ b/app/code/Magento/Catalog/Model/Product/Image/ParamsBuilder.php @@ -130,10 +130,12 @@ private function getWatermark(string $type, int $scopeId = null): array ); if ($file) { - $size = $this->scopeConfig->getValue( - "design/watermark/{$type}_size", - ScopeInterface::SCOPE_STORE, - $scopeId + $size = explode( + 'x', + $this->scopeConfig->getValue( + "design/watermark/{$type}_size", + ScopeInterface::SCOPE_STORE + ) ); $opacity = $this->scopeConfig->getValue( "design/watermark/{$type}_imageOpacity", @@ -145,8 +147,8 @@ private function getWatermark(string $type, int $scopeId = null): array ScopeInterface::SCOPE_STORE, $scopeId ); - $width = !empty($size['width']) ? $size['width'] : null; - $height = !empty($size['height']) ? $size['height'] : null; + $width = !empty($size['0']) ? $size['0'] : null; + $height = !empty($size['1']) ? $size['1'] : null; return [ 'watermark_file' => $file, diff --git a/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php b/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php index a7468bcb1e77f..4a8e6431d6ce8 100644 --- a/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php @@ -4,14 +4,17 @@ * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model\Product\Link; use Magento\Catalog\Api\ProductLinkRepositoryInterface; use Magento\Catalog\Model\ResourceModel\Product\Link; use Magento\Framework\EntityManager\MetadataPool; +use Magento\Catalog\Api\Data\ProductLinkInterface; /** - * Class SaveProductLinks + * Save product links. */ class SaveHandler { @@ -47,8 +50,10 @@ public function __construct( } /** - * @param string $entityType - * @param object $entity + * Save product links for the product. + * + * @param string $entityType Product type. + * @param \Magento\Catalog\Api\Data\ProductInterface $entity * @return \Magento\Catalog\Api\Data\ProductInterface * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ @@ -56,13 +61,13 @@ public function execute($entityType, $entity) { $link = $entity->getData($this->metadataPool->getMetadata($entityType)->getLinkField()); if ($this->linkResource->hasProductLinks($link)) { - /** @var \Magento\Catalog\Api\Data\ProductInterface $entity */ foreach ($this->productLinkRepository->getList($entity) as $link) { $this->productLinkRepository->delete($link); } } // Build links per type + /** @var ProductLinkInterface[][] $linksByType */ $linksByType = []; foreach ($entity->getProductLinks() as $link) { $linksByType[$link->getLinkType()][] = $link; @@ -71,13 +76,17 @@ public function execute($entityType, $entity) // Set array position as a fallback position if necessary foreach ($linksByType as $linkType => $links) { if (!$this->hasPosition($links)) { - array_walk($linksByType[$linkType], function ($productLink, $position) { - $productLink->setPosition(++$position); - }); + array_walk( + $linksByType[$linkType], + function (ProductLinkInterface $productLink, $position) { + $productLink->setPosition(++$position); + } + ); } } // Flatten multi-dimensional linksByType in ProductLinks + /** @var ProductLinkInterface[] $productLinks */ $productLinks = array_reduce($linksByType, 'array_merge', []); if (count($productLinks) > 0) { @@ -90,13 +99,14 @@ public function execute($entityType, $entity) /** * Check if at least one link without position - * @param array $links + * + * @param ProductLinkInterface[] $links * @return bool */ - private function hasPosition(array $links) + private function hasPosition(array $links): bool { foreach ($links as $link) { - if (!array_key_exists('position', $link->getData())) { + if ($link->getPosition() === null) { return false; } } diff --git a/app/code/Magento/Catalog/Model/Product/Option.php b/app/code/Magento/Catalog/Model/Product/Option.php index 4f730834412e4..128f420e033c2 100644 --- a/app/code/Magento/Catalog/Model/Product/Option.php +++ b/app/code/Magento/Catalog/Model/Product/Option.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\Model\Product; @@ -11,8 +12,15 @@ use Magento\Catalog\Api\Data\ProductCustomOptionValuesInterfaceFactory; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Option\Type\Date; +use Magento\Catalog\Model\Product\Option\Type\DefaultType; +use Magento\Catalog\Model\Product\Option\Type\File; +use Magento\Catalog\Model\Product\Option\Type\Select; +use Magento\Catalog\Model\Product\Option\Type\Text; +use Magento\Catalog\Model\Product\Option\Value; use Magento\Catalog\Model\ResourceModel\Product\Option\Value\Collection; -use Magento\Catalog\Pricing\Price\BasePrice; +use Magento\Catalog\Pricing\Price\CalculateCustomOptionCatalogRule; +use Magento\Framework\App\ObjectManager; use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Model\AbstractExtensibleModel; @@ -98,6 +106,16 @@ class Option extends AbstractExtensibleModel implements ProductCustomOptionInter */ protected $validatorPool; + /** + * @var string[] + */ + private $optionGroups; + + /** + * @var string[] + */ + private $optionTypesToGroups; + /** * @var MetadataPool */ @@ -108,6 +126,11 @@ class Option extends AbstractExtensibleModel implements ProductCustomOptionInter */ private $customOptionValuesFactory; + /** + * @var CalculateCustomOptionCatalogRule + */ + private $calculateCustomOptionCatalogRule; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -121,6 +144,9 @@ class Option extends AbstractExtensibleModel implements ProductCustomOptionInter * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data * @param ProductCustomOptionValuesInterfaceFactory|null $customOptionValuesFactory + * @param array $optionGroups + * @param array $optionTypesToGroups + * @param CalculateCustomOptionCatalogRule $calculateCustomOptionCatalogRule * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -135,14 +161,37 @@ public function __construct( \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [], - ProductCustomOptionValuesInterfaceFactory $customOptionValuesFactory = null + ProductCustomOptionValuesInterfaceFactory $customOptionValuesFactory = null, + array $optionGroups = [], + array $optionTypesToGroups = [], + CalculateCustomOptionCatalogRule $calculateCustomOptionCatalogRule = null ) { $this->productOptionValue = $productOptionValue; $this->optionTypeFactory = $optionFactory; - $this->validatorPool = $validatorPool; $this->string = $string; + $this->validatorPool = $validatorPool; $this->customOptionValuesFactory = $customOptionValuesFactory ?: - \Magento\Framework\App\ObjectManager::getInstance()->get(ProductCustomOptionValuesInterfaceFactory::class); + ObjectManager::getInstance()->get(ProductCustomOptionValuesInterfaceFactory::class); + $this->calculateCustomOptionCatalogRule = $calculateCustomOptionCatalogRule ?? + ObjectManager::getInstance()->get(CalculateCustomOptionCatalogRule::class); + $this->optionGroups = $optionGroups ?: [ + self::OPTION_GROUP_DATE => Date::class, + self::OPTION_GROUP_FILE => File::class, + self::OPTION_GROUP_SELECT => Select::class, + self::OPTION_GROUP_TEXT => Text::class, + ]; + $this->optionTypesToGroups = $optionTypesToGroups ?: [ + self::OPTION_TYPE_FIELD => self::OPTION_GROUP_TEXT, + self::OPTION_TYPE_AREA => self::OPTION_GROUP_TEXT, + self::OPTION_TYPE_FILE => self::OPTION_GROUP_FILE, + self::OPTION_TYPE_DROP_DOWN => self::OPTION_GROUP_SELECT, + self::OPTION_TYPE_RADIO => self::OPTION_GROUP_SELECT, + self::OPTION_TYPE_CHECKBOX => self::OPTION_GROUP_SELECT, + self::OPTION_TYPE_MULTIPLE => self::OPTION_GROUP_SELECT, + self::OPTION_TYPE_DATE => self::OPTION_GROUP_DATE, + self::OPTION_TYPE_DATE_TIME => self::OPTION_GROUP_DATE, + self::OPTION_TYPE_TIME => self::OPTION_GROUP_DATE, + ]; parent::__construct( $context, @@ -314,36 +363,22 @@ public function getGroupByType($type = null) if ($type === null) { $type = $this->getType(); } - $optionGroupsToTypes = [ - self::OPTION_TYPE_FIELD => self::OPTION_GROUP_TEXT, - self::OPTION_TYPE_AREA => self::OPTION_GROUP_TEXT, - self::OPTION_TYPE_FILE => self::OPTION_GROUP_FILE, - self::OPTION_TYPE_DROP_DOWN => self::OPTION_GROUP_SELECT, - self::OPTION_TYPE_RADIO => self::OPTION_GROUP_SELECT, - self::OPTION_TYPE_CHECKBOX => self::OPTION_GROUP_SELECT, - self::OPTION_TYPE_MULTIPLE => self::OPTION_GROUP_SELECT, - self::OPTION_TYPE_DATE => self::OPTION_GROUP_DATE, - self::OPTION_TYPE_DATE_TIME => self::OPTION_GROUP_DATE, - self::OPTION_TYPE_TIME => self::OPTION_GROUP_DATE, - ]; - return $optionGroupsToTypes[$type] ?? ''; + return $this->optionTypesToGroups[$type] ?? ''; } /** * Group model factory * * @param string $type Option type - * @return \Magento\Catalog\Model\Product\Option\Type\DefaultType + * @return DefaultType * @throws LocalizedException */ public function groupFactory($type) { $group = $this->getGroupByType($type); - if (!empty($group)) { - return $this->optionTypeFactory->create( - 'Magento\Catalog\Model\Product\Option\Type\\' . $this->string->upperCaseWords($group) - ); + if (!empty($group) && isset($this->optionGroups[$group])) { + return $this->optionTypeFactory->create($this->optionGroups[$group]); } throw new LocalizedException(__('The option type to get group instance is incorrect.')); } @@ -439,10 +474,12 @@ public function afterSave() */ public function getPrice($flag = false) { - if ($flag && $this->getPriceType() == self::$typePercent) { - $basePrice = $this->getProduct()->getPriceInfo()->getPrice(BasePrice::PRICE_CODE)->getValue(); - $price = $basePrice * ($this->_getData(self::KEY_PRICE) / 100); - return $price; + if ($flag) { + return $this->calculateCustomOptionCatalogRule->execute( + $this->getProduct(), + (float)$this->getData(self::KEY_PRICE), + $this->getPriceType() === Value::TYPE_PERCENT + ); } return $this->_getData(self::KEY_PRICE); } @@ -929,7 +966,7 @@ public function setExtensionAttributes( private function getOptionRepository() { if (null === $this->optionRepository) { - $this->optionRepository = \Magento\Framework\App\ObjectManager::getInstance() + $this->optionRepository = ObjectManager::getInstance() ->get(\Magento\Catalog\Model\Product\Option\Repository::class); } return $this->optionRepository; @@ -943,7 +980,7 @@ private function getOptionRepository() private function getMetadataPool() { if (null === $this->metadataPool) { - $this->metadataPool = \Magento\Framework\App\ObjectManager::getInstance() + $this->metadataPool = ObjectManager::getInstance() ->get(\Magento\Framework\EntityManager\MetadataPool::class); } return $this->metadataPool; diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/DefaultType.php b/app/code/Magento/Catalog/Model/Product/Option/Type/DefaultType.php index c388be8b6f394..be7f1921afccf 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/DefaultType.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/DefaultType.php @@ -3,17 +3,26 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\Model\Product\Option\Type; -use Magento\Framework\Exception\LocalizedException; use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Configuration\Item\ItemInterface; +use Magento\Catalog\Model\Product\Configuration\Item\Option\OptionInterface; +use Magento\Catalog\Model\Product\Option; +use Magento\Catalog\Model\Product\Option\Value; +use Magento\Catalog\Pricing\Price\CalculateCustomOptionCatalogRule; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\LocalizedException; /** * Catalog product option default type * * @api * @author Magento Core Team <core@magentocommerce.com> + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 */ @@ -22,14 +31,14 @@ class DefaultType extends \Magento\Framework\DataObject /** * Option Instance * - * @var \Magento\Catalog\Model\Product\Option + * @var Option */ protected $_option; /** * Product Instance * - * @var \Magento\Catalog\Model\Product + * @var Product */ protected $_product; @@ -54,27 +63,36 @@ class DefaultType extends \Magento\Framework\DataObject */ protected $_checkoutSession; + /** + * @var CalculateCustomOptionCatalogRule + */ + private $calculateCustomOptionCatalogRule; + /** * Construct * * @param \Magento\Checkout\Model\Session $checkoutSession * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param array $data + * @param CalculateCustomOptionCatalogRule $calculateCustomOptionCatalogRule */ public function __construct( \Magento\Checkout\Model\Session $checkoutSession, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - array $data = [] + array $data = [], + CalculateCustomOptionCatalogRule $calculateCustomOptionCatalogRule = null ) { $this->_checkoutSession = $checkoutSession; parent::__construct($data); $this->_scopeConfig = $scopeConfig; + $this->calculateCustomOptionCatalogRule = $calculateCustomOptionCatalogRule ?? ObjectManager::getInstance() + ->get(CalculateCustomOptionCatalogRule::class); } /** * Option Instance setter * - * @param \Magento\Catalog\Model\Product\Option $option + * @param Option $option * @return $this */ public function setOption($option) @@ -86,12 +104,12 @@ public function setOption($option) /** * Option Instance getter * + * @return Option * @throws \Magento\Framework\Exception\LocalizedException - * @return \Magento\Catalog\Model\Product\Option */ public function getOption() { - if ($this->_option instanceof \Magento\Catalog\Model\Product\Option) { + if ($this->_option instanceof Option) { return $this->_option; } throw new LocalizedException(__('The option instance type in options group is incorrect.')); @@ -100,7 +118,7 @@ public function getOption() /** * Product Instance setter * - * @param \Magento\Catalog\Model\Product $product + * @param Product $product * @return $this */ public function setProduct($product) @@ -112,12 +130,12 @@ public function setProduct($product) /** * Product Instance getter * + * @return Product * @throws \Magento\Framework\Exception\LocalizedException - * @return \Magento\Catalog\Model\Product */ public function getProduct() { - if ($this->_product instanceof \Magento\Catalog\Model\Product) { + if ($this->_product instanceof Product) { return $this->_product; } throw new LocalizedException(__('The product instance type in options group is incorrect.')); @@ -126,15 +144,12 @@ public function getProduct() /** * Getter for Configuration Item Option * - * @return \Magento\Catalog\Model\Product\Configuration\Item\Option\OptionInterface + * @return OptionInterface * @throws LocalizedException */ public function getConfigurationItemOption() { - if ($this->_getData( - 'configuration_item_option' - ) instanceof \Magento\Catalog\Model\Product\Configuration\Item\Option\OptionInterface - ) { + if ($this->_getData('configuration_item_option') instanceof OptionInterface) { return $this->_getData('configuration_item_option'); } @@ -149,14 +164,12 @@ public function getConfigurationItemOption() /** * Getter for Configuration Item * - * @return \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface + * @return ItemInterface * @throws \Magento\Framework\Exception\LocalizedException */ public function getConfigurationItem() { - if ($this->_getData( - 'configuration_item' - ) instanceof \Magento\Catalog\Model\Product\Configuration\Item\ItemInterface + if ($this->_getData('configuration_item') instanceof ItemInterface ) { return $this->_getData('configuration_item'); } @@ -341,7 +354,11 @@ public function getOptionPrice($optionValue, $basePrice) { $option = $this->getOption(); - return $this->_getChargeableOptionPrice($option->getPrice(), $option->getPriceType() == 'percent', $basePrice); + return $this->calculateCustomOptionCatalogRule->execute( + $option->getProduct(), + (float)$option->getPrice(), + $option->getPriceType() === Value::TYPE_PERCENT + ); } /** @@ -368,14 +385,14 @@ public function getProductOptions() $options = $this->getProduct()->getOptions(); if ($options != null) { foreach ($options as $_option) { - /* @var $option \Magento\Catalog\Model\Product\Option */ + /* @var $option Option */ $this->_productOptions[$this->getProduct()->getId()][$_option->getTitle()] = [ 'option_id' => $_option->getId(), ]; if ($_option->getGroupByType() == ProductCustomOptionInterface::OPTION_GROUP_SELECT) { $optionValues = []; foreach ($_option->getValues() as $_value) { - /* @var $value \Magento\Catalog\Model\Product\Option\Value */ + /* @var $value Value */ $optionValues[$_value->getTitle()] = $_value->getId(); } $this->_productOptions[$this @@ -395,12 +412,14 @@ public function getProductOptions() } /** + * Return final chargeable price for option + * * @param float $price Price of option * @param boolean $isPercent Price type - percent or fixed * @param float $basePrice For percent price type * @return float * @deprecated 102.0.4 typo in method name - * @see _getChargeableOptionPrice + * @see CalculateCustomOptionCatalogRule::execute */ protected function _getChargableOptionPrice($price, $isPercent, $basePrice) { @@ -414,6 +433,8 @@ protected function _getChargableOptionPrice($price, $isPercent, $basePrice) * @param boolean $isPercent Price type - percent or fixed * @param float $basePrice For percent price type * @return float + * @deprecated + * @see CalculateCustomOptionCatalogRule::execute */ protected function _getChargeableOptionPrice($price, $isPercent, $basePrice) { diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php b/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php index d2766b1bbb054..8eebd3e91c2ee 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php @@ -3,9 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\Model\Product\Option\Type; +use Magento\Catalog\Model\Product\Option\Value; +use Magento\Catalog\Pricing\Price\CalculateCustomOptionCatalogRule; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\LocalizedException; /** @@ -37,6 +41,11 @@ class Select extends \Magento\Catalog\Model\Product\Option\Type\DefaultType */ private $singleSelectionTypes; + /** + * @var CalculateCustomOptionCatalogRule + */ + private $calculateCustomOptionCatalogRule; + /** * @param \Magento\Checkout\Model\Session $checkoutSession * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig @@ -44,6 +53,7 @@ class Select extends \Magento\Catalog\Model\Product\Option\Type\DefaultType * @param \Magento\Framework\Escaper $escaper * @param array $data * @param array $singleSelectionTypes + * @param CalculateCustomOptionCatalogRule $calculateCustomOptionCatalogRule */ public function __construct( \Magento\Checkout\Model\Session $checkoutSession, @@ -51,7 +61,8 @@ public function __construct( \Magento\Framework\Stdlib\StringUtils $string, \Magento\Framework\Escaper $escaper, array $data = [], - array $singleSelectionTypes = [] + array $singleSelectionTypes = [], + CalculateCustomOptionCatalogRule $calculateCustomOptionCatalogRule = null ) { $this->string = $string; $this->_escaper = $escaper; @@ -61,6 +72,8 @@ public function __construct( 'drop_down' => \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN, 'radio' => \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_RADIO, ]; + $this->calculateCustomOptionCatalogRule = $calculateCustomOptionCatalogRule ?? ObjectManager::getInstance() + ->get(CalculateCustomOptionCatalogRule::class); } /** @@ -248,10 +261,10 @@ public function getOptionPrice($optionValue, $basePrice) foreach (explode(',', $optionValue) as $value) { $_result = $option->getValueById($value); if ($_result) { - $result += $this->_getChargeableOptionPrice( - $_result->getPrice(), - $_result->getPriceType() == 'percent', - $basePrice + $result += $this->calculateCustomOptionCatalogRule->execute( + $option->getProduct(), + (float)$_result->getPrice(), + $_result->getPriceType() === Value::TYPE_PERCENT ); } else { if ($this->getListener()) { @@ -263,10 +276,10 @@ public function getOptionPrice($optionValue, $basePrice) } elseif ($this->_isSingleSelection()) { $_result = $option->getValueById($optionValue); if ($_result) { - $result = $this->_getChargeableOptionPrice( - $_result->getPrice(), - $_result->getPriceType() == 'percent', - $basePrice + $result = $this->calculateCustomOptionCatalogRule->execute( + $option->getProduct(), + (float)$_result->getPrice(), + $_result->getPriceType() === Value::TYPE_PERCENT ); } else { if ($this->getListener()) { diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/Text.php b/app/code/Magento/Catalog/Model/Product/Option/Type/Text.php index 9ffe75e513bce..71a6556dc8858 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/Text.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/Text.php @@ -10,6 +10,8 @@ /** * Catalog product option text type + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class Text extends \Magento\Catalog\Model\Product\Option\Type\DefaultType { @@ -68,6 +70,7 @@ public function validateUserValue($values) // Check maximal length limit $maxCharacters = $option->getMaxCharacters(); + $value = $this->normalizeNewLineSymbols($value); if ($maxCharacters > 0 && $this->string->strlen($value) > $maxCharacters) { $this->setIsValid(false); throw new LocalizedException(__('The text is too long. Shorten the text and try again.')); @@ -101,4 +104,15 @@ public function getFormattedOptionValue($value) { return $this->_escaper->escapeHtml($value); } + + /** + * Normalize newline symbols + * + * @param string $value + * @return string + */ + private function normalizeNewLineSymbols(string $value) + { + return str_replace(["\r\n", "\n\r", "\r"], "\n", $value); + } } diff --git a/app/code/Magento/Catalog/Model/Product/Option/Value.php b/app/code/Magento/Catalog/Model/Product/Option/Value.php index ebbc060c99edf..783bda4699792 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Value.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Value.php @@ -3,13 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\Model\Product\Option; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Option; -use Magento\Framework\Model\AbstractModel; use Magento\Catalog\Pricing\Price\BasePrice; +use Magento\Catalog\Pricing\Price\CalculateCustomOptionCatalogRule; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Model\AbstractModel; use Magento\Catalog\Pricing\Price\CustomOptionPriceCalculator; use Magento\Catalog\Pricing\Price\RegularPrice; @@ -69,6 +72,11 @@ class Value extends AbstractModel implements \Magento\Catalog\Api\Data\ProductCu */ private $customOptionPriceCalculator; + /** + * @var CalculateCustomOptionCatalogRule + */ + private $calculateCustomOptionCatalogRule; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -77,6 +85,7 @@ class Value extends AbstractModel implements \Magento\Catalog\Api\Data\ProductCu * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data * @param CustomOptionPriceCalculator|null $customOptionPriceCalculator + * @param CalculateCustomOptionCatalogRule|null $CalculateCustomOptionCatalogRule */ public function __construct( \Magento\Framework\Model\Context $context, @@ -85,11 +94,14 @@ public function __construct( \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [], - CustomOptionPriceCalculator $customOptionPriceCalculator = null + CustomOptionPriceCalculator $customOptionPriceCalculator = null, + CalculateCustomOptionCatalogRule $CalculateCustomOptionCatalogRule = null ) { $this->_valueCollectionFactory = $valueCollectionFactory; $this->customOptionPriceCalculator = $customOptionPriceCalculator - ?? \Magento\Framework\App\ObjectManager::getInstance()->get(CustomOptionPriceCalculator::class); + ?? ObjectManager::getInstance()->get(CustomOptionPriceCalculator::class); + $this->calculateCustomOptionCatalogRule = $CalculateCustomOptionCatalogRule + ?? ObjectManager::getInstance()->get(CalculateCustomOptionCatalogRule::class); parent::__construct( $context, @@ -101,6 +113,8 @@ public function __construct( } /** + * Override parent _construct method + * * @return void */ protected function _construct() @@ -109,6 +123,8 @@ protected function _construct() } /** + * Add value. + * * @codeCoverageIgnoreStart * @param mixed $value * @return $this @@ -120,6 +136,8 @@ public function addValue($value) } /** + * Get values. + * * @return array */ public function getValues() @@ -128,6 +146,8 @@ public function getValues() } /** + * Set values. + * * @param array $values * @return $this */ @@ -138,6 +158,8 @@ public function setValues($values) } /** + * Unset values. + * * @return $this */ public function unsetValues() @@ -147,6 +169,8 @@ public function unsetValues() } /** + * Set option. + * * @param Option $option * @return $this */ @@ -157,6 +181,8 @@ public function setOption(Option $option) } /** + * Unset option. + * * @return $this */ public function unsetOption() @@ -166,7 +192,7 @@ public function unsetOption() } /** - * Enter description here... + * Get option. * * @return Option */ @@ -176,6 +202,8 @@ public function getOption() } /** + * Set product. + * * @param Product $product * @return $this */ @@ -188,6 +216,8 @@ public function setProduct($product) //@codeCoverageIgnoreEnd /** + * Get product. + * * @return Product */ public function getProduct() @@ -199,7 +229,10 @@ public function getProduct() } /** + * Save values. + * * @return $this + * @throws \Exception */ public function saveValues() { @@ -225,8 +258,9 @@ public function saveValues() } /** - * Return price. If $flag is true and price is percent - * return converted percent to price + * Return price. + * + * If $flag is true and price is percent return converted percent to price * * @param bool $flag * @return float|int @@ -234,7 +268,11 @@ public function saveValues() public function getPrice($flag = false) { if ($flag) { - return $this->customOptionPriceCalculator->getOptionPriceByPriceCode($this, BasePrice::PRICE_CODE); + return $this->calculateCustomOptionCatalogRule->execute( + $this->getProduct(), + (float)$this->getData(self::KEY_PRICE), + $this->getPriceType() === self::TYPE_PERCENT + ); } return $this->_getData(self::KEY_PRICE); } @@ -250,7 +288,7 @@ public function getRegularPrice() } /** - * Enter description here... + * Get values collection. * * @param Option $option * @return \Magento\Catalog\Model\ResourceModel\Product\Option\Value\Collection @@ -268,6 +306,8 @@ public function getValuesCollection(Option $option) } /** + * Get values by option. + * * @param array $optionIds * @param int $option_id * @param int $store_id @@ -287,6 +327,8 @@ public function getValuesByOption($optionIds, $option_id, $store_id) } /** + * Delete value. + * * @param int $option_id * @return $this */ @@ -297,6 +339,8 @@ public function deleteValue($option_id) } /** + * Delete values. + * * @param int $option_type_id * @return $this */ diff --git a/app/code/Magento/Catalog/Model/ProductLink/CollectionProvider.php b/app/code/Magento/Catalog/Model/ProductLink/CollectionProvider.php index b96aff148e750..7b25533ff72b8 100644 --- a/app/code/Magento/Catalog/Model/ProductLink/CollectionProvider.php +++ b/app/code/Magento/Catalog/Model/ProductLink/CollectionProvider.php @@ -4,8 +4,11 @@ * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model\ProductLink; +use Magento\Catalog\Model\Product; use Magento\Catalog\Model\ProductLink\Converter\ConverterPool; use Magento\Framework\Exception\NoSuchEntityException; @@ -19,6 +22,11 @@ class CollectionProvider */ protected $providers; + /** + * @var MapProviderInterface[] + */ + private $mapProviders; + /** * @var ConverterPool */ @@ -27,43 +35,169 @@ class CollectionProvider /** * @param ConverterPool $converterPool * @param CollectionProviderInterface[] $providers + * @param MapProviderInterface[] $mapProviders */ - public function __construct(ConverterPool $converterPool, array $providers = []) + public function __construct(ConverterPool $converterPool, array $providers = [], array $mapProviders = []) { $this->converterPool = $converterPool; $this->providers = $providers; + $this->mapProviders = $mapProviders; + } + + /** + * Extract link data from linked products. + * + * @param Product[] $linkedProducts + * @param string $type + * @return array + */ + private function prepareList(array $linkedProducts, string $type): array + { + $converter = $this->converterPool->getConverter($type); + $links = []; + foreach ($linkedProducts as $item) { + $itemId = $item->getId(); + $links[$itemId] = $converter->convert($item); + $links[$itemId]['position'] = $links[$itemId]['position'] ?? 0; + $links[$itemId]['link_type'] = $type; + } + + return $links; } /** * Get product collection by link type * - * @param \Magento\Catalog\Model\Product $product + * @param Product $product * @param string $type * @return array * @throws NoSuchEntityException */ - public function getCollection(\Magento\Catalog\Model\Product $product, $type) + public function getCollection(Product $product, $type) { if (!isset($this->providers[$type])) { throw new NoSuchEntityException(__("The collection provider isn't registered.")); } $products = $this->providers[$type]->getLinkedProducts($product); - $converter = $this->converterPool->getConverter($type); - $sorterItems = []; - foreach ($products as $item) { - $itemId = $item->getId(); - $sorterItems[$itemId] = $converter->convert($item); - $sorterItems[$itemId]['position'] = $sorterItems[$itemId]['position'] ?? 0; + + $linkData = $this->prepareList($products, $type); + usort( + $linkData, + function (array $itemA, array $itemB): int { + $posA = (int)$itemA['position']; + $posB = (int)$itemB['position']; + + return $posA <=> $posB; + } + ); + + return $linkData; + } + + /** + * Load maps from map providers. + * + * @param array $map + * @param array $typeProcessors + * @param Product[] $products + * @return void + */ + private function retrieveMaps(array &$map, array $typeProcessors, array $products): void + { + /** + * @var MapProviderInterface $processor + * @var string[] $types + */ + foreach ($typeProcessors as $processorIndex => $types) { + $typeMap = $this->mapProviders[$processorIndex]->fetchMap($products, $types); + /** + * @var string $sku + * @var Product[][] $links + */ + foreach ($typeMap as $sku => $links) { + $linkData = []; + foreach ($links as $linkType => $linkedProducts) { + $linkData[] = $this->prepareList($linkedProducts, $linkType); + } + if ($linkData) { + $existing = []; + if (array_key_exists($sku, $map)) { + $existing = $map[$sku]; + } + // phpcs:ignore Magento2.Performance.ForeachArrayMerge + $map[$sku] = array_merge($existing, ...$linkData); + } + } + } + } + + /** + * Load links for each product separately. + * + * @param \SplObjectStorage $map + * @param string[] $types + * @param Product[] $products + * @return void + * @throws NoSuchEntityException + */ + private function retrieveSingles(array &$map, array $types, array $products): void + { + foreach ($products as $product) { + $linkData = []; + foreach ($types as $type) { + $linkData[] = $this->getCollection($product, $type); + } + $linkData = array_filter($linkData); + if ($linkData) { + $existing = []; + if (array_key_exists($product->getSku(), $map)) { + $existing = $map[$product->getSku()]; + } + // phpcs:ignore Magento2.Performance.ForeachArrayMerge + $map[$product->getSku()] = array_merge($existing, ...$linkData); + } } + } - usort($sorterItems, function ($itemA, $itemB) { - $posA = (int)$itemA['position']; - $posB = (int)$itemB['position']; + /** + * Load map of linked product data. + * + * Link data consists of link_type, type, sku, position, extension attributes? and custom_attributes?. + * + * @param Product[] $products + * @param array $types Keys - string names, values - codes. + * @return array Keys - SKUs, values containing link data. + * @throws NoSuchEntityException + * @throws \InvalidArgumentException + */ + public function getMap(array $products, array $types): array + { + if (!$types) { + throw new \InvalidArgumentException('Types are required'); + } + $map = []; + $typeProcessors = []; + /** @var string[] $singleProcessors */ + $singleProcessors = []; + //Finding map processors + foreach ($types as $type => $typeCode) { + foreach ($this->mapProviders as $i => $mapProvider) { + if ($mapProvider->canProcessLinkType($type)) { + if (!array_key_exists($i, $typeProcessors)) { + $typeProcessors[$i] = []; + } + $typeProcessors[$i][$type] = $typeCode; + continue 2; + } + } + //No map processor found, will process 1 by 1 + $singleProcessors[] = $type; + } - return $posA <=> $posB; - }); + $this->retrieveMaps($map, $typeProcessors, $products); + $this->retrieveSingles($map, $singleProcessors, $products); - return $sorterItems; + return $map; } } diff --git a/app/code/Magento/Catalog/Model/ProductLink/CollectionProvider/LinkedMapProvider.php b/app/code/Magento/Catalog/Model/ProductLink/CollectionProvider/LinkedMapProvider.php new file mode 100644 index 0000000000000..6be2d2e52cf23 --- /dev/null +++ b/app/code/Magento/Catalog/Model/ProductLink/CollectionProvider/LinkedMapProvider.php @@ -0,0 +1,232 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\ProductLink\CollectionProvider; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\ProductLink\MapProviderInterface; +use Magento\Catalog\Model\Product\Link; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Catalog\Model\ResourceModel\Product\Link\Product\Collection as LinkedProductCollection; +use Magento\Catalog\Model\ResourceModel\Product\Link\Product\CollectionFactory as LinkedProductCollectionFactory; + +/** + * Provides linked products. + */ +class LinkedMapProvider implements MapProviderInterface +{ + /** + * Link types supported. + */ + private const TYPES = ['crosssell', 'related', 'upsell']; + + /** + * Type name => Product model cache key. + */ + private const PRODUCT_CACHE_KEY_MAP = [ + 'crosssell' => 'cross_sell_products', + 'upsell' => 'up_sell_products', + 'related' => 'related_products' + ]; + + /** + * @var Link + */ + private $linkModel; + + /** + * @var MetadataPool + */ + private $metadata; + + /** + * @var LinkedProductCollectionFactory + */ + private $productCollectionFactory; + + /** + * LinkedMapProvider constructor. + * @param Link $linkModel + * @param MetadataPool $metadataPool + * @param LinkedProductCollectionFactory $productCollectionFactory + */ + public function __construct( + Link $linkModel, + MetadataPool $metadataPool, + LinkedProductCollectionFactory $productCollectionFactory + ) { + $this->linkModel = $linkModel; + $this->metadata = $metadataPool; + $this->productCollectionFactory = $productCollectionFactory; + } + + /** + * @inheritDoc + */ + public function canProcessLinkType(string $linkType): bool + { + return in_array($linkType, self::TYPES, true); + } + + /** + * Add linked products to the map. + * + * @param Product[][] $map + * @param string $sku + * @param string $type + * @param Product[] $linked + * @return void + */ + private function addLinkedToMap(array &$map, string $sku, string $type, array $linked): void + { + if (!array_key_exists($sku, $map)) { + $map[$sku] = []; + } + if (!array_key_exists($type, $map[$sku])) { + $map[$sku][$type] = []; + } + $map[$sku][$type] = array_merge($map[$sku][$type], $linked); + } + + /** + * Extract cached linked products from entities and find root products that do need a query. + * + * @param Product[] $products Products mapped by link field value. + * @param int[] $types Type requested. + * @param Product[][] $map Map of linked products. + * @return string[][] {Type name => Product link field values} map. + */ + private function processCached(array $products, array $types, array &$map): array + { + /** @var string[][] $query */ + $query = []; + + foreach ($products as $productId => $product) { + $sku = $product->getSku(); + foreach (array_keys($types) as $type) { + if (array_key_exists($type, self::PRODUCT_CACHE_KEY_MAP) + && $product->hasData(self::PRODUCT_CACHE_KEY_MAP[$type]) + ) { + $this->addLinkedToMap($map, $sku, $type, $product->getData(self::PRODUCT_CACHE_KEY_MAP[$type])); + //Cached found, no need to load. + continue; + } + + if (!array_key_exists($type, $query)) { + $query[$type] = []; + } + $query[$type][] = $productId; + } + } + + return $query; + } + + /** + * Load products linked to given products. + * + * @param string[][] $productIds {Type name => Product IDs (link field values)} map. + * @param int[] $types Type name => type ID map. + * @return Product[][] Type name => Product list map. + */ + private function queryLinkedProducts(array $productIds, array $types): array + { + $found = []; + foreach ($types as $type => $typeId) { + if (!array_key_exists($type, $productIds)) { + continue; + } + + /** @var LinkedProductCollection $collection */ + $collection = $this->productCollectionFactory->create(['productIds' => $productIds[$type]]); + $this->linkModel->setLinkTypeId($typeId); + $collection->setLinkModel($this->linkModel); + $collection->setIsStrongMode(); + $found[$type] = $collection->getItems(); + } + + return $found; + } + + /** + * Cache found linked products for existing root product instances. + * + * @param Product[] $forProducts + * @param Product[][] $map + * @param int[] $linkTypesRequested Link types that were queried. + * @return void + */ + private function cacheLinked(array $forProducts, array $map, array $linkTypesRequested): void + { + foreach ($forProducts as $product) { + $sku = $product->getSku(); + if (!array_key_exists($sku, $map)) { + $found = []; + } else { + $found = $map[$sku]; + } + foreach (array_keys($linkTypesRequested) as $linkName) { + if (!array_key_exists($linkName, $found)) { + $found[$linkName] = []; + } + } + + foreach (self::PRODUCT_CACHE_KEY_MAP as $typeName => $cacheKey) { + if (!array_key_exists($typeName, $linkTypesRequested)) { + //If products were not queried for current type then moving on + continue; + } + + $product->setData($cacheKey, $found[$typeName]); + } + } + } + + /** + * @inheritDoc + */ + public function fetchMap(array $products, array $linkTypes): array + { + if (!$products || !$linkTypes) { + throw new \InvalidArgumentException('Products and link types are required.'); + } + + //Gathering products information + $productActualIdField = $this->metadata->getMetadata(ProductInterface::class)->getLinkField(); + /** @var Product[] $rootProducts */ + $rootProducts = []; + /** @var Product $product */ + foreach ($products as $product) { + if ($id = $product->getData($productActualIdField)) { + $rootProducts[$id] = $product; + } + } + unset($product); + //Cannot load without persisted products + if (!$rootProducts) { + return []; + } + + //Finding linked. + $map = []; + $query = $this->processCached($rootProducts, $linkTypes, $map); + $foundLinked = $this->queryLinkedProducts($query, $linkTypes); + + //Filling map with what we've found. + foreach ($foundLinked as $linkType => $linkedProducts) { + foreach ($linkedProducts as $linkedProduct) { + $product = $rootProducts[$linkedProduct->getData('_linked_to_product_id')]; + $this->addLinkedToMap($map, $product->getSku(), $linkType, [$linkedProduct]); + } + } + + $this->cacheLinked($rootProducts, $map, $linkTypes); + + return $map; + } +} diff --git a/app/code/Magento/Catalog/Model/ProductLink/Data/ListCriteria.php b/app/code/Magento/Catalog/Model/ProductLink/Data/ListCriteria.php new file mode 100644 index 0000000000000..4ba59e1fd08e2 --- /dev/null +++ b/app/code/Magento/Catalog/Model/ProductLink/Data/ListCriteria.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\ProductLink\Data; + +use Magento\Catalog\Model\Product; + +/** + * @inheritDoc + */ +class ListCriteria implements ListCriteriaInterface +{ + /** + * @var string + */ + private $productSku; + + /** + * @var Product|null + */ + private $product; + + /** + * @var string[]|null + */ + private $linkTypes; + + /** + * ListCriteria constructor. + * @param string $belongsToProductSku + * @param string[]|null $linkTypes + * @param Product|null $belongsToProduct + */ + public function __construct( + string $belongsToProductSku, + ?array $linkTypes = null, + ?Product $belongsToProduct = null + ) { + $this->productSku = $belongsToProductSku; + $this->linkTypes = $linkTypes; + if ($belongsToProduct) { + $this->productSku = $belongsToProduct->getSku(); + $this->product = $belongsToProduct; + } + } + + /** + * @inheritDoc + */ + public function getBelongsToProductSku(): string + { + return $this->productSku; + } + + /** + * @inheritDoc + */ + public function getLinkTypes(): ?array + { + return $this->linkTypes; + } + + /** + * Product model. + * + * @see getBelongsToProductSku() + * @return Product|null + */ + public function getBelongsToProduct(): ?Product + { + return $this->product; + } +} diff --git a/app/code/Magento/Catalog/Model/ProductLink/Data/ListCriteriaInterface.php b/app/code/Magento/Catalog/Model/ProductLink/Data/ListCriteriaInterface.php new file mode 100644 index 0000000000000..0291be5b9e783 --- /dev/null +++ b/app/code/Magento/Catalog/Model/ProductLink/Data/ListCriteriaInterface.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\ProductLink\Data; + +/** + * Criteria for finding lists. + */ +interface ListCriteriaInterface +{ + /** + * Links belong to this product. + * + * @return string + */ + public function getBelongsToProductSku(): string; + + /** + * Limit links by type (in). + * + * @return string[]|null + */ + public function getLinkTypes(): ?array; +} diff --git a/app/code/Magento/Catalog/Model/ProductLink/Data/ListResult.php b/app/code/Magento/Catalog/Model/ProductLink/Data/ListResult.php new file mode 100644 index 0000000000000..4828837d790fb --- /dev/null +++ b/app/code/Magento/Catalog/Model/ProductLink/Data/ListResult.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\ProductLink\Data; + +use Magento\Catalog\Api\Data\ProductLinkInterface; + +/** + * @inheritDoc + */ +class ListResult implements ListResultInterface +{ + /** + * @var ProductLinkInterface[]|null + */ + private $result; + + /** + * @var \Throwable|null + */ + private $error; + + /** + * ListResult constructor. + * @param ProductLinkInterface[]|null $result + * @param \Throwable|null $error + */ + public function __construct(?array $result, ?\Throwable $error) + { + $this->result = $result; + $this->error = $error; + if ($this->result === null && $this->error === null) { + throw new \InvalidArgumentException('Result must either contain values or an error.'); + } + } + + /** + * @inheritDoc + */ + public function getResult(): ?array + { + return $this->result; + } + + /** + * @inheritDoc + */ + public function getError(): ?\Throwable + { + return $this->error; + } +} diff --git a/app/code/Magento/Catalog/Model/ProductLink/Data/ListResultInterface.php b/app/code/Magento/Catalog/Model/ProductLink/Data/ListResultInterface.php new file mode 100644 index 0000000000000..f5c0454e7a542 --- /dev/null +++ b/app/code/Magento/Catalog/Model/ProductLink/Data/ListResultInterface.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\ProductLink\Data; + +use Magento\Catalog\Api\Data\ProductLinkInterface; + +/** + * Result of finding a list of links. + */ +interface ListResultInterface +{ + /** + * Found links, null if error occurred. + * + * @return ProductLinkInterface[]|null + */ + public function getResult(): ?array; + + /** + * Error that occurred during retrieval of the list. + * + * @return \Throwable|null + */ + public function getError(): ?\Throwable; +} diff --git a/app/code/Magento/Catalog/Model/ProductLink/MapProviderInterface.php b/app/code/Magento/Catalog/Model/ProductLink/MapProviderInterface.php new file mode 100644 index 0000000000000..31951ab10f5b4 --- /dev/null +++ b/app/code/Magento/Catalog/Model/ProductLink/MapProviderInterface.php @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Model\ProductLink; + +use Magento\Catalog\Model\Product; + +/** + * Provide link data for products. + */ +interface MapProviderInterface +{ + /** + * Whether a provider can provide data for given link type. + * + * @param string $linkType + * @return bool + */ + public function canProcessLinkType(string $linkType): bool; + + /** + * Load linked products. + * + * Must return map with keys as product objects, values as maps of link types and products linked. + * + * @param Product[] $products With SKUs as keys. + * @param string[] $linkTypes List of supported link types to process, keys - names, values - codes. + * @return Product[][] + */ + public function fetchMap(array $products, array $linkTypes): array; +} diff --git a/app/code/Magento/Catalog/Model/ProductLink/ProductLinkQuery.php b/app/code/Magento/Catalog/Model/ProductLink/ProductLinkQuery.php new file mode 100644 index 0000000000000..4bc400605a429 --- /dev/null +++ b/app/code/Magento/Catalog/Model/ProductLink/ProductLinkQuery.php @@ -0,0 +1,245 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Model\ProductLink; + +use Magento\Catalog\Model\Product\LinkTypeProvider; +use Magento\Catalog\Model\ProductLink\Data\ListCriteria; +use Magento\Catalog\Model\ProductLink\Data\ListResult; +use Magento\Catalog\Model\ProductRepository; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Api\SimpleDataObjectConverter; +use Magento\Catalog\Api\Data\ProductLinkInterfaceFactory; +use Magento\Catalog\Api\Data\ProductLinkExtensionFactory; +use Magento\Framework\Exception\InputException; + +/** + * Search for product links by criteria. + * + * Batch contract for getting product links. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ProductLinkQuery +{ + /** + * @var LinkTypeProvider + */ + private $linkTypeProvider; + + /** + * @var ProductRepository + */ + private $productRepository; + + /** + * @var SearchCriteriaBuilder + */ + private $criteriaBuilder; + + /** + * @var CollectionProvider + */ + private $collectionProvider; + + /** + * @var ProductLinkInterfaceFactory + */ + private $productLinkFactory; + + /** + * @var ProductLinkExtensionFactory + */ + private $productLinkExtensionFactory; + + /** + * @param LinkTypeProvider $linkTypeProvider + * @param ProductRepository $productRepository + * @param SearchCriteriaBuilder $criteriaBuilder + * @param CollectionProvider $collectionProvider + * @param ProductLinkInterfaceFactory $productLinkFactory + * @param ProductLinkExtensionFactory $productLinkExtensionFactory + */ + public function __construct( + LinkTypeProvider $linkTypeProvider, + ProductRepository $productRepository, + SearchCriteriaBuilder $criteriaBuilder, + CollectionProvider $collectionProvider, + ProductLinkInterfaceFactory $productLinkFactory, + ProductLinkExtensionFactory $productLinkExtensionFactory + ) { + $this->linkTypeProvider = $linkTypeProvider; + $this->productRepository = $productRepository; + $this->criteriaBuilder = $criteriaBuilder; + $this->collectionProvider = $collectionProvider; + $this->productLinkFactory = $productLinkFactory; + $this->productLinkExtensionFactory = $productLinkExtensionFactory; + } + + /** + * Extract all link types requested. + * + * @param \Magento\Catalog\Model\ProductLink\Data\ListCriteriaInterface[] $criteria + * @return string[] + */ + private function extractRequestedLinkTypes(array $criteria): array + { + $linkTypes = $this->linkTypeProvider->getLinkTypes(); + $linkTypesToLoad = []; + foreach ($criteria as $listCriteria) { + if ($listCriteria->getLinkTypes() === null) { + //All link types are to be returned. + $linkTypesToLoad = null; + break; + } + $linkTypesToLoad[] = $listCriteria->getLinkTypes(); + } + if ($linkTypesToLoad !== null) { + if (count($linkTypesToLoad) === 1) { + $linkTypesToLoad = $linkTypesToLoad[0]; + } else { + $linkTypesToLoad = array_merge(...$linkTypesToLoad); + } + $linkTypesToLoad = array_flip($linkTypesToLoad); + $linkTypes = array_filter( + $linkTypes, + function (string $code) use ($linkTypesToLoad) { + return array_key_exists($code, $linkTypesToLoad); + }, + ARRAY_FILTER_USE_KEY + ); + } + + return $linkTypes; + } + + /** + * Load products links were requested for. + * + * @param \Magento\Catalog\Model\ProductLink\Data\ListCriteriaInterface[] $criteria + * @return \Magento\Catalog\Model\Product[] Keys are SKUs. + */ + private function loadProductsByCriteria(array $criteria): array + { + $products = []; + $skusToLoad = []; + foreach ($criteria as $listCriteria) { + if ($listCriteria instanceof ListCriteria + && $listCriteria->getBelongsToProduct() + ) { + $products[$listCriteria->getBelongsToProduct()->getSku()] = $listCriteria->getBelongsToProduct(); + } else { + $skusToLoad[] = $listCriteria->getBelongsToProductSku(); + } + } + + $skusToLoad = array_filter( + $skusToLoad, + function ($sku) use ($products) { + return !array_key_exists($sku, $products); + } + ); + if ($skusToLoad) { + $loaded = $this->productRepository->getList( + $this->criteriaBuilder->addFilter('sku', $skusToLoad, 'in')->create() + ); + foreach ($loaded->getItems() as $product) { + $products[$product->getSku()] = $product; + } + } + + return $products; + } + + /** + * Convert links data to DTOs. + * + * @param string $productSku SKU of the root product. + * @param array[] $linksData Links data returned from collection. + * @param string[]|null $acceptedTypes Link types that are accepted. + * @return \Magento\Catalog\Api\Data\ProductLinkInterface[] + */ + private function convertLinksData(string $productSku, array $linksData, ?array $acceptedTypes): array + { + $list = []; + foreach ($linksData as $linkData) { + if ($acceptedTypes && !in_array($linkData['link_type'], $acceptedTypes, true)) { + continue; + } + /** @var \Magento\Catalog\Api\Data\ProductLinkInterface $productLink */ + $productLink = $this->productLinkFactory->create(); + $productLink->setSku($productSku) + ->setLinkType($linkData['link_type']) + ->setLinkedProductSku($linkData['sku']) + ->setLinkedProductType($linkData['type']) + ->setPosition($linkData['position']); + if (isset($linkData['custom_attributes'])) { + $productLinkExtension = $productLink->getExtensionAttributes(); + if ($productLinkExtension === null) { + /** @var \Magento\Catalog\Api\Data\ProductLinkExtensionInterface $productLinkExtension */ + $productLinkExtension = $this->productLinkExtensionFactory->create(); + } + foreach ($linkData['custom_attributes'] as $option) { + $name = $option['attribute_code']; + $value = $option['value']; + $setterName = 'set' . SimpleDataObjectConverter::snakeCaseToUpperCamelCase($name); + // Check if setter exists + if (method_exists($productLinkExtension, $setterName)) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction + call_user_func([$productLinkExtension, $setterName], $value); + } + } + $productLink->setExtensionAttributes($productLinkExtension); + } + $list[] = $productLink; + } + + return $list; + } + + /** + * Get list of product links found by criteria. + * + * Results are returned in the same order as criteria items. + * + * @param \Magento\Catalog\Model\ProductLink\Data\ListCriteriaInterface[] $criteria + * @return \Magento\Catalog\Model\ProductLink\Data\ListResultInterface[] + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function search(array $criteria): array + { + if (!$criteria) { + throw InputException::requiredField('criteria'); + } + + //Requested link types. + $linkTypes = $this->extractRequestedLinkTypes($criteria); + //Requested products. + $products = $this->loadProductsByCriteria($criteria); + //Map of products and their linked products' data. + $map = $this->collectionProvider->getMap($products, $linkTypes); + + //Batch contract results. + $results = []; + foreach ($criteria as $listCriteria) { + $productSku = $listCriteria->getBelongsToProductSku(); + if (!array_key_exists($productSku, $map)) { + $results[] = new ListResult([], null); + continue; + } + try { + $list = $this->convertLinksData($productSku, $map[$productSku], $listCriteria->getLinkTypes()); + $results[] = new ListResult($list, null); + } catch (\Throwable $error) { + $results[] = new ListResult(null, $error); + } + } + + return $results; + } +} diff --git a/app/code/Magento/Catalog/Model/ProductLink/Repository.php b/app/code/Magento/Catalog/Model/ProductLink/Repository.php index 98977de7effaf..960044efbc2ec 100644 --- a/app/code/Magento/Catalog/Model/ProductLink/Repository.php +++ b/app/code/Magento/Catalog/Model/ProductLink/Repository.php @@ -3,6 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Catalog\Model\ProductLink; use Magento\Catalog\Api\Data\ProductInterface; @@ -10,6 +13,7 @@ use Magento\Catalog\Api\Data\ProductLinkExtensionFactory; use Magento\Catalog\Model\Product\Initialization\Helper\ProductLinks as LinksInitializer; use Magento\Catalog\Model\Product\LinkTypeProvider; +use Magento\Catalog\Model\ProductLink\Data\ListCriteria; use Magento\Framework\Api\SimpleDataObjectConverter; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\NoSuchEntityException; @@ -17,6 +21,8 @@ use Magento\Framework\App\ObjectManager; /** + * Product link entity repository. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Repository implements \Magento\Catalog\Api\ProductLinkRepositoryInterface @@ -48,11 +54,14 @@ class Repository implements \Magento\Catalog\Api\ProductLinkRepositoryInterface /** * @var CollectionProvider + * @deprecated Not used anymore. + * @see query */ protected $entityCollectionProvider; /** * @var LinksInitializer + * @deprecated Not used. */ protected $linkInitializer; @@ -68,14 +77,23 @@ class Repository implements \Magento\Catalog\Api\ProductLinkRepositoryInterface /** * @var ProductLinkInterfaceFactory + * @deprecated Not used anymore, search delegated. + * @see getList() */ protected $productLinkFactory; /** * @var ProductLinkExtensionFactory + * @deprecated Not used anymore, search delegated. + * @see getList() */ protected $productLinkExtensionFactory; + /** + * @var ProductLinkQuery + */ + private $query; + /** * Constructor * @@ -86,6 +104,7 @@ class Repository implements \Magento\Catalog\Api\ProductLinkRepositoryInterface * @param \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor * @param \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory|null $productLinkFactory * @param \Magento\Catalog\Api\Data\ProductLinkExtensionFactory|null $productLinkExtensionFactory + * @param ProductLinkQuery|null $query * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -95,7 +114,8 @@ public function __construct( \Magento\Catalog\Model\ProductLink\Management $linkManagement, \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor, \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory $productLinkFactory = null, - \Magento\Catalog\Api\Data\ProductLinkExtensionFactory $productLinkExtensionFactory = null + \Magento\Catalog\Api\Data\ProductLinkExtensionFactory $productLinkExtensionFactory = null, + ?ProductLinkQuery $query = null ) { $this->productRepository = $productRepository; $this->entityCollectionProvider = $entityCollectionProvider; @@ -106,10 +126,11 @@ public function __construct( ->get(\Magento\Catalog\Api\Data\ProductLinkInterfaceFactory::class); $this->productLinkExtensionFactory = $productLinkExtensionFactory ?: ObjectManager::getInstance() ->get(\Magento\Catalog\Api\Data\ProductLinkExtensionFactory::class); + $this->query = $query ?? ObjectManager::getInstance()->get(ProductLinkQuery::class); } /** - * {@inheritdoc} + * @inheritDoc */ public function save(\Magento\Catalog\Api\Data\ProductLinkInterface $entity) { @@ -146,47 +167,25 @@ public function save(\Magento\Catalog\Api\Data\ProductLinkInterface $entity) /** * Get product links list * - * @param \Magento\Catalog\Api\Data\ProductInterface $product + * @param \Magento\Catalog\Api\Data\ProductInterface|\Magento\Catalog\Model\Product $product * @return \Magento\Catalog\Api\Data\ProductLinkInterface[] */ public function getList(\Magento\Catalog\Api\Data\ProductInterface $product) { - $output = []; - $linkTypes = $this->getLinkTypeProvider()->getLinkTypes(); - foreach (array_keys($linkTypes) as $linkTypeName) { - $collection = $this->entityCollectionProvider->getCollection($product, $linkTypeName); - foreach ($collection as $item) { - /** @var \Magento\Catalog\Api\Data\ProductLinkInterface $productLink */ - $productLink = $this->productLinkFactory->create(); - $productLink->setSku($product->getSku()) - ->setLinkType($linkTypeName) - ->setLinkedProductSku($item['sku']) - ->setLinkedProductType($item['type']) - ->setPosition($item['position']); - if (isset($item['custom_attributes'])) { - $productLinkExtension = $productLink->getExtensionAttributes(); - if ($productLinkExtension === null) { - $productLinkExtension = $this->productLinkExtensionFactory()->create(); - } - foreach ($item['custom_attributes'] as $option) { - $name = $option['attribute_code']; - $value = $option['value']; - $setterName = 'set' . SimpleDataObjectConverter::snakeCaseToUpperCamelCase($name); - // Check if setter exists - if (method_exists($productLinkExtension, $setterName)) { - call_user_func([$productLinkExtension, $setterName], $value); - } - } - $productLink->setExtensionAttributes($productLinkExtension); - } - $output[] = $productLink; - } + if (!$product->getSku() || !$product->getId()) { + return $product->getProductLinks(); } - return $output; + $criteria = new ListCriteria($product->getSku(), null, $product); + $result = $this->query->search([$criteria])[0]; + + if ($result->getError()) { + throw $result->getError(); + } + return $result->getResult(); } /** - * {@inheritdoc} + * @inheritDoc */ public function delete(\Magento\Catalog\Api\Data\ProductLinkInterface $entity) { @@ -219,7 +218,7 @@ public function delete(\Magento\Catalog\Api\Data\ProductLinkInterface $entity) } /** - * {@inheritdoc} + * @inheritDoc */ public function deleteById($sku, $type, $linkedProductSku) { @@ -243,6 +242,8 @@ public function deleteById($sku, $type, $linkedProductSku) } /** + * Get Link resource instance. + * * @return \Magento\Catalog\Model\ResourceModel\Product\Link */ private function getLinkResource() @@ -255,6 +256,8 @@ private function getLinkResource() } /** + * Get LinkTypeProvider instance. + * * @return LinkTypeProvider */ private function getLinkTypeProvider() @@ -267,6 +270,8 @@ private function getLinkTypeProvider() } /** + * Get MetadataPool instance. + * * @return \Magento\Framework\EntityManager\MetadataPool */ private function getMetadataPool() diff --git a/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php b/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php index fdcf2956dbdef..2aa92b8f0316e 100644 --- a/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php +++ b/app/code/Magento/Catalog/Model/ProductRepository/MediaGalleryProcessor.php @@ -92,7 +92,17 @@ public function processMediaGallery(ProductInterface $product, array $mediaGalle if ($updatedEntry['file'] === null) { unset($updatedEntry['file']); } - $existingMediaGallery[$key] = array_merge($existingEntry, $updatedEntry); + if (isset($updatedEntry['content'])) { + //need to recreate image and reset object + $existingEntry['recreate'] = true; + // phpcs:ignore Magento2.Performance.ForeachArrayMerge + $newEntry = array_merge($existingEntry, $updatedEntry); + $newEntries[] = $newEntry; + unset($existingMediaGallery[$key]); + } else { + // phpcs:ignore Magento2.Performance.ForeachArrayMerge + $existingMediaGallery[$key] = array_merge($existingEntry, $updatedEntry); + } } else { //set the removed flag $existingEntry['removed'] = true; diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category.php b/app/code/Magento/Catalog/Model/ResourceModel/Category.php index 9e0d174a4cccb..c4980c917d069 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Category.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Category.php @@ -771,6 +771,8 @@ public function getParentDesignCategory($category) 'custom_layout_update' )->addAttributeToSelect( 'custom_apply_to_products' + )->addAttributeToSelect( + 'custom_layout_update_file' )->addFieldToFilter( 'entity_id', ['in' => $pathIds] diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php index 355561c5e384d..f3984b4a35f1a 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php @@ -38,6 +38,18 @@ class Attribute extends \Magento\Eav\Model\Entity\Attribute implements const KEY_IS_GLOBAL = 'is_global'; + private const ALLOWED_INPUT_TYPES = [ + 'boolean' => true, + 'date' => true, + 'datetime' => true, + 'multiselect' => true, + 'price' => true, + 'select' => true, + 'text' => true, + 'textarea' => true, + 'weight' => true, + ]; + /** * @var LockValidatorInterface */ @@ -236,7 +248,7 @@ public function afterSave() ) { $this->_indexerEavProcessor->markIndexerAsInvalid(); } - + $this->_source = null; return parent::afterSave(); @@ -403,18 +415,7 @@ public function getSourceModel() */ public function isAllowedForRuleCondition() { - $allowedInputTypes = [ - 'boolean', - 'date', - 'datetime', - 'multiselect', - 'price', - 'select', - 'text', - 'textarea', - 'weight', - ]; - return $this->getIsVisible() && in_array($this->getFrontendInput(), $allowedInputTypes); + return $this->getIsVisible() && isset(self::ALLOWED_INPUT_TYPES[$this->getFrontendInput()]); } /** @@ -845,9 +846,6 @@ public function afterDelete() /** * @inheritdoc * @since 100.0.9 - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __sleep() { @@ -861,9 +859,6 @@ public function __sleep() /** * @inheritdoc * @since 100.0.9 - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __wakeup() { diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product.php b/app/code/Magento/Catalog/Model/ResourceModel/Product.php index b0b15cfd69d13..c5587d3b25665 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product.php @@ -6,10 +6,12 @@ namespace Magento\Catalog\Model\ResourceModel; use Magento\Catalog\Model\ResourceModel\Product\Website\Link as ProductWebsiteLink; +use Magento\Eav\Api\AttributeManagementInterface; use Magento\Framework\App\ObjectManager; use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; use Magento\Catalog\Model\Product as ProductEntity; use Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface; +use Magento\Framework\DataObject; use Magento\Framework\EntityManager\EntityManager; use Magento\Framework\Model\AbstractModel; @@ -93,6 +95,11 @@ class Product extends AbstractResource */ private $tableMaintainer; + /** + * @var AttributeManagementInterface + */ + private $eavAttributeManagement; + /** * @param \Magento\Eav\Model\Entity\Context $context * @param \Magento\Store\Model\StoreManagerInterface $storeManager @@ -106,7 +113,7 @@ class Product extends AbstractResource * @param array $data * @param TableMaintainer|null $tableMaintainer * @param UniqueValidationInterface|null $uniqueValidator - * + * @param AttributeManagementInterface|null $eavAttributeManagement * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -121,7 +128,8 @@ public function __construct( \Magento\Catalog\Model\Product\Attribute\DefaultAttributes $defaultAttributes, $data = [], TableMaintainer $tableMaintainer = null, - UniqueValidationInterface $uniqueValidator = null + UniqueValidationInterface $uniqueValidator = null, + AttributeManagementInterface $eavAttributeManagement = null ) { $this->_categoryCollectionFactory = $categoryCollectionFactory; $this->_catalogCategory = $catalogCategory; @@ -138,6 +146,8 @@ public function __construct( ); $this->connectionName = 'catalog'; $this->tableMaintainer = $tableMaintainer ?: ObjectManager::getInstance()->get(TableMaintainer::class); + $this->eavAttributeManagement = $eavAttributeManagement + ?? ObjectManager::getInstance()->get(AttributeManagementInterface::class); } /** @@ -268,10 +278,10 @@ public function getIdBySku($sku) /** * Process product data before save * - * @param \Magento\Framework\DataObject $object + * @param DataObject $object * @return $this */ - protected function _beforeSave(\Magento\Framework\DataObject $object) + protected function _beforeSave(DataObject $object) { $self = parent::_beforeSave($object); /** @@ -286,15 +296,73 @@ protected function _beforeSave(\Magento\Framework\DataObject $object) /** * Save data related with product * - * @param \Magento\Framework\DataObject $product + * @param DataObject $product * @return $this */ - protected function _afterSave(\Magento\Framework\DataObject $product) + protected function _afterSave(DataObject $product) { + $this->removeNotInSetAttributeValues($product); $this->_saveWebsiteIds($product)->_saveCategories($product); return parent::_afterSave($product); } + /** + * Remove attribute values that absent in product attribute set + * + * @param DataObject $product + * @return DataObject + */ + private function removeNotInSetAttributeValues(DataObject $product): DataObject + { + $oldAttributeSetId = $product->getOrigData(ProductEntity::ATTRIBUTE_SET_ID); + if ($oldAttributeSetId && $product->dataHasChangedFor(ProductEntity::ATTRIBUTE_SET_ID)) { + $newAttributes = $product->getAttributes(); + $newAttributesCodes = array_keys($newAttributes); + $oldAttributes = $this->eavAttributeManagement->getAttributes( + ProductEntity::ENTITY, + $oldAttributeSetId + ); + $oldAttributesCodes = []; + foreach ($oldAttributes as $oldAttribute) { + $oldAttributesCodes[] = $oldAttribute->getAttributecode(); + } + $notInSetAttributeCodes = array_diff($oldAttributesCodes, $newAttributesCodes); + if (!empty($notInSetAttributeCodes)) { + $this->deleteSelectedEntityAttributeRows($product, $notInSetAttributeCodes); + } + } + + return $product; + } + + /** + * Clear selected entity attribute rows + * + * @param DataObject $product + * @param array $attributeCodes + * @return void + */ + private function deleteSelectedEntityAttributeRows(DataObject $product, array $attributeCodes): void + { + $backendTables = []; + foreach ($attributeCodes as $attributeCode) { + $attribute = $this->getAttribute($attributeCode); + $backendTable = $attribute->getBackendTable(); + if (!$attribute->isStatic() && $backendTable) { + $backendTables[$backendTable][] = $attribute->getId(); + } + } + + $entityIdField = $this->getLinkField(); + $entityId = $product->getData($entityIdField); + foreach ($backendTables as $backendTable => $attributes) { + $connection = $this->getConnection(); + $where = $connection->quoteInto('attribute_id IN (?)', $attributes); + $where .= $connection->quoteInto(" AND {$entityIdField} = ?", $entityId); + $connection->delete($backendTable, $where); + } + } + /** * @inheritdoc */ @@ -337,12 +405,12 @@ protected function _saveWebsiteIds($product) /** * Save product category relations * - * @param \Magento\Framework\DataObject $object + * @param DataObject $object * @return $this * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @deprecated 101.1.0 */ - protected function _saveCategories(\Magento\Framework\DataObject $object) + protected function _saveCategories(DataObject $object) { return $this; } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index 442499f2da7e3..e31180d4ff6cf 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -2520,10 +2520,10 @@ public function getPricesCount() /** * Add is_saleable attribute to filter * - * @param array|null $condition + * @param mixed $condition * @return $this */ - private function addIsSaleableAttributeToFilter(?array $condition): self + private function addIsSaleableAttributeToFilter($condition): self { $columns = $this->getSelect()->getPart(Select::COLUMNS); foreach ($columns as $columnEntry) { @@ -2551,10 +2551,10 @@ private function addIsSaleableAttributeToFilter(?array $condition): self * Add tier price attribute to filter * * @param string $attribute - * @param array|null $condition + * @param mixed $condition * @return $this */ - private function addTierPriceAttributeToFilter(string $attribute, ?array $condition): self + private function addTierPriceAttributeToFilter(string $attribute, $condition): self { $attrCode = $attribute; $connection = $this->getConnection(); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Link/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Link/Product/Collection.php index 5724496d7ebdc..f1d4552cf37f0 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Link/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Link/Product/Collection.php @@ -5,6 +5,14 @@ */ namespace Magento\Catalog\Model\ResourceModel\Product\Link\Product; +use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; +use Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver; +use Magento\Catalog\Model\ResourceModel\Category; +use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; +use Magento\Customer\Api\GroupManagementInterface; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\Indexer\DimensionFactory; + /** * Catalog product linked products collection * @@ -50,6 +58,111 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection */ protected $_hasLinkFilter = false; + /** + * @var string[]|null Root product link fields values. + */ + private $productIds; + + /** + * @var string|null + */ + private $linkField; + + /** + * Collection constructor. + * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory + * @param \Psr\Log\LoggerInterface $logger + * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy + * @param \Magento\Framework\Event\ManagerInterface $eventManager + * @param \Magento\Eav\Model\Config $eavConfig + * @param \Magento\Framework\App\ResourceConnection $resource + * @param \Magento\Eav\Model\EntityFactory $eavEntityFactory + * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper + * @param \Magento\Framework\Validator\UniversalFactory $universalFactory + * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param \Magento\Framework\Module\Manager $moduleManager + * @param \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState + * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory + * @param \Magento\Catalog\Model\ResourceModel\Url $catalogUrl + * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate + * @param \Magento\Customer\Model\Session $customerSession + * @param \Magento\Framework\Stdlib\DateTime $dateTime + * @param GroupManagementInterface $groupManagement + * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection + * @param ProductLimitationFactory|null $productLimitationFactory + * @param MetadataPool|null $metadataPool + * @param TableMaintainer|null $tableMaintainer + * @param PriceTableResolver|null $priceTableResolver + * @param DimensionFactory|null $dimensionFactory + * @param Category|null $categoryResourceModel + * @param string[]|null $productIds Root product IDs (linkFields, not entity_ids). + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function __construct( + \Magento\Framework\Data\Collection\EntityFactory $entityFactory, + \Psr\Log\LoggerInterface $logger, + \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy, + \Magento\Framework\Event\ManagerInterface $eventManager, + \Magento\Eav\Model\Config $eavConfig, + \Magento\Framework\App\ResourceConnection $resource, + \Magento\Eav\Model\EntityFactory $eavEntityFactory, + \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper, + \Magento\Framework\Validator\UniversalFactory $universalFactory, + \Magento\Store\Model\StoreManagerInterface $storeManager, + \Magento\Framework\Module\Manager $moduleManager, + \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState, + \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, + \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory, + \Magento\Catalog\Model\ResourceModel\Url $catalogUrl, + \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, + \Magento\Customer\Model\Session $customerSession, + \Magento\Framework\Stdlib\DateTime $dateTime, + GroupManagementInterface $groupManagement, + \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, + ProductLimitationFactory $productLimitationFactory = null, + MetadataPool $metadataPool = null, + TableMaintainer $tableMaintainer = null, + PriceTableResolver $priceTableResolver = null, + DimensionFactory $dimensionFactory = null, + Category $categoryResourceModel = null, + ?array $productIds = null + ) { + parent::__construct( + $entityFactory, + $logger, + $fetchStrategy, + $eventManager, + $eavConfig, + $resource, + $eavEntityFactory, + $resourceHelper, + $universalFactory, + $storeManager, + $moduleManager, + $catalogProductFlatState, + $scopeConfig, + $productOptionFactory, + $catalogUrl, + $localeDate, + $customerSession, + $dateTime, + $groupManagement, + $connection, + $productLimitationFactory, + $metadataPool, + $tableMaintainer, + $priceTableResolver, + $dimensionFactory, + $categoryResourceModel + ); + + if ($productIds) { + $this->productIds = $productIds; + $this->_hasLinkFilter = true; + } + } + /** * Declare link model and initialize type attributes join * @@ -98,6 +211,7 @@ public function setProduct(\Magento\Catalog\Model\Product $product) if ($product && $product->getId()) { $this->_hasLinkFilter = true; $this->setStore($product->getStore()); + $this->productIds = [$product->getData($this->getLinkField())]; } return $this; } @@ -142,7 +256,7 @@ public function addProductFilter($products) if (!is_array($products)) { $products = [$products]; } - $identifierField = $this->getProductEntityMetadata()->getIdentifierField(); + $identifierField = $this->getLinkField(); $this->getSelect()->where("product_entity_table.$identifierField IN (?)", $products); $this->_hasLinkFilter = true; } @@ -202,21 +316,20 @@ protected function _joinLinks() $connection->quoteInto('links.link_type_id = ?', $this->_linkTypeId), ]; $joinType = 'join'; - $linkField = $this->getProductEntityMetadata()->getLinkField(); - if ($this->getProduct() && $this->getProduct()->getId()) { - $linkFieldId = $this->getProduct()->getData( - $linkField - ); + $linkField = $this->getLinkField(); + if ($this->productIds) { if ($this->_isStrongMode) { - $this->getSelect()->where('links.product_id = ?', (int)$linkFieldId); + $this->getSelect()->where('links.product_id in (?)', $this->productIds); } else { $joinType = 'joinLeft'; - $joinCondition[] = $connection->quoteInto('links.product_id = ?', $linkFieldId); + $joinCondition[] = $connection->quoteInto('links.product_id in (?)', $this->productIds); + } + if (count($this->productIds) === 1) { + $this->addFieldToFilter( + $linkField, + ['neq' => array_values($this->productIds)[0]] + ); } - $this->addFieldToFilter( - $linkField, - ['neq' => $linkFieldId] - ); } elseif ($this->_isStrongMode) { $this->addFieldToFilter( $linkField, @@ -227,7 +340,7 @@ protected function _joinLinks() $select->{$joinType}( ['links' => $this->getTable('catalog_product_link')], implode(' AND ', $joinCondition), - ['link_id'] + ['link_id' => 'link_id', '_linked_to_product_id' => 'product_id'] ); $this->joinAttributes(); } @@ -347,13 +460,14 @@ public function addLinkAttributeToFilter($code, $condition) /** * Join Product To Links + * * @return void */ private function joinProductsToLinks() { if ($this->_hasLinkFilter) { $metaDataPool = $this->getProductEntityMetadata(); - $linkField = $metaDataPool->getLinkField(); + $linkField = $this->getLinkField(); $entityTable = $metaDataPool->getEntityTable(); $this->getSelect() ->join( @@ -363,4 +477,18 @@ private function joinProductsToLinks() ); } } + + /** + * Get product entity's identifier field. + * + * @return string + */ + private function getLinkField(): string + { + if (!$this->linkField) { + $this->linkField = $this->getProductEntityMetadata()->getLinkField(); + } + + return $this->linkField; + } } diff --git a/app/code/Magento/Catalog/Observer/CategoryDesignAuthorization.php b/app/code/Magento/Catalog/Observer/CategoryDesignAuthorization.php new file mode 100644 index 0000000000000..94977485b95b3 --- /dev/null +++ b/app/code/Magento/Catalog/Observer/CategoryDesignAuthorization.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Observer; + +use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Catalog\Model\Category\Authorization; +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Event\Observer; +use Magento\Framework\Exception\AuthorizationException; + +/** + * Employ additional authorization logic when a category is saved. + */ +class CategoryDesignAuthorization implements ObserverInterface +{ + /** + * @var Authorization + */ + private $authorization; + + /** + * @param Authorization $authorization + */ + public function __construct(Authorization $authorization) + { + $this->authorization = $authorization; + } + + /** + * @inheritDoc + * + * @throws AuthorizationException + */ + public function execute(Observer $observer) + { + /** @var CategoryInterface $category */ + $category = $observer->getEvent()->getData('category'); + $this->authorization->authorizeSavingOf($category); + } +} diff --git a/app/code/Magento/Catalog/Plugin/CategoryAuthorization.php b/app/code/Magento/Catalog/Plugin/CategoryAuthorization.php new file mode 100644 index 0000000000000..af2dccb96f937 --- /dev/null +++ b/app/code/Magento/Catalog/Plugin/CategoryAuthorization.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Plugin; + +use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Model\Category\Authorization; +use Magento\Framework\Exception\LocalizedException; + +/** + * Perform additional authorization for category operations. + */ +class CategoryAuthorization +{ + /** + * @var Authorization + */ + private $authorization; + + /** + * @param Authorization $authorization + */ + public function __construct(Authorization $authorization) + { + $this->authorization = $authorization; + } + + /** + * Authorize saving of a category. + * + * @param CategoryRepositoryInterface $subject + * @param CategoryInterface $category + * @throws LocalizedException + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeSave(CategoryRepositoryInterface $subject, CategoryInterface $category): array + { + $this->authorization->authorizeSavingOf($category); + + return [$category]; + } +} diff --git a/app/code/Magento/Catalog/Plugin/ProductAuthorization.php b/app/code/Magento/Catalog/Plugin/ProductAuthorization.php new file mode 100644 index 0000000000000..ce2fe19cf1aee --- /dev/null +++ b/app/code/Magento/Catalog/Plugin/ProductAuthorization.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Plugin; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Authorization; +use Magento\Framework\Exception\LocalizedException; + +/** + * Perform additional authorization for product operations. + */ +class ProductAuthorization +{ + /** + * @var Authorization + */ + private $authorization; + + /** + * @param Authorization $authorization + */ + public function __construct(Authorization $authorization) + { + $this->authorization = $authorization; + } + + /** + * Authorize saving of a product. + * + * @param ProductRepositoryInterface $subject + * @param ProductInterface $product + * @param bool $saveOptions + * @throws LocalizedException + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeSave( + ProductRepositoryInterface $subject, + ProductInterface $product, + $saveOptions = false + ): array { + $this->authorization->authorizeSavingOf($product); + + return [$product, $saveOptions]; + } +} diff --git a/app/code/Magento/Catalog/Pricing/Price/CalculateCustomOptionCatalogRule.php b/app/code/Magento/Catalog/Pricing/Price/CalculateCustomOptionCatalogRule.php new file mode 100644 index 0000000000000..b3f3ac7bf68ef --- /dev/null +++ b/app/code/Magento/Catalog/Pricing/Price/CalculateCustomOptionCatalogRule.php @@ -0,0 +1,119 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Pricing\Price; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\PriceModifierInterface; +use Magento\CatalogRule\Pricing\Price\CatalogRulePrice; +use Magento\Framework\Pricing\Price\BasePriceProviderInterface; +use Magento\Framework\Pricing\PriceCurrencyInterface; + +/** + * Calculates prices of custom options of the product with catalog rules applied. + */ +class CalculateCustomOptionCatalogRule +{ + /** + * @var PriceCurrencyInterface + */ + private $priceCurrency; + + /** + * @var PriceModifierInterface + */ + private $priceModifier; + + /** + * @param PriceCurrencyInterface $priceCurrency + * @param PriceModifierInterface $priceModifier + */ + public function __construct( + PriceCurrencyInterface $priceCurrency, + PriceModifierInterface $priceModifier + ) { + $this->priceModifier = $priceModifier; + $this->priceCurrency = $priceCurrency; + } + + /** + * Calculate prices of custom options of the product with catalog rules applied. + * + * @param Product $product + * @param float $optionPriceValue + * @param bool $isPercent + * @return float + */ + public function execute( + Product $product, + float $optionPriceValue, + bool $isPercent + ): float { + $regularPrice = (float)$product->getPriceInfo() + ->getPrice(RegularPrice::PRICE_CODE) + ->getValue(); + $catalogRulePrice = $this->priceModifier->modifyPrice( + $regularPrice, + $product + ); + $basePriceWithOutCatalogRules = (float)$this->getGetBasePriceWithOutCatalogRules($product); + // Apply catalog price rules to product options only if catalog price rules are applied to product. + if ($catalogRulePrice < $basePriceWithOutCatalogRules) { + $optionPrice = $this->getOptionPriceWithoutPriceRule($optionPriceValue, $isPercent, $regularPrice); + $totalCatalogRulePrice = $this->priceModifier->modifyPrice( + $regularPrice + $optionPrice, + $product + ); + $finalOptionPrice = $totalCatalogRulePrice - $catalogRulePrice; + } else { + $finalOptionPrice = $this->getOptionPriceWithoutPriceRule( + $optionPriceValue, + $isPercent, + $this->getGetBasePriceWithOutCatalogRules($product) + ); + } + + return $this->priceCurrency->convertAndRound($finalOptionPrice); + } + + /** + * Get product base price without catalog rules applied. + * + * @param Product $product + * @return float + */ + private function getGetBasePriceWithOutCatalogRules(Product $product): float + { + $basePrice = null; + foreach ($product->getPriceInfo()->getPrices() as $price) { + if ($price instanceof BasePriceProviderInterface + && $price->getPriceCode() !== CatalogRulePrice::PRICE_CODE + && $price->getValue() !== false + ) { + $basePrice = min( + $price->getValue(), + $basePrice ?? $price->getValue() + ); + } + } + + return $basePrice ?? $product->getPrice(); + } + + /** + * Calculate option price without catalog price rule discount. + * + * @param float $optionPriceValue + * @param bool $isPercent + * @param float $basePrice + * @return float + */ + private function getOptionPriceWithoutPriceRule(float $optionPriceValue, bool $isPercent, float $basePrice): float + { + return $isPercent ? $basePrice * $optionPriceValue / 100 : $optionPriceValue; + } +} diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/UpdateCustomLayoutAttributes.php b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateCustomLayoutAttributes.php new file mode 100644 index 0000000000000..81fd35ccd3178 --- /dev/null +++ b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateCustomLayoutAttributes.php @@ -0,0 +1,121 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Setup\Patch\Data; + +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Catalog\Setup\CategorySetup; +use Magento\Catalog\Setup\CategorySetupFactory; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Category; + +/** + * Add new custom layout related attributes. + */ +class UpdateCustomLayoutAttributes implements DataPatchInterface +{ + /** + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** + * @var CategorySetupFactory + */ + private $categorySetupFactory; + + /** + * PatchInitial constructor. + * @param ModuleDataSetupInterface $moduleDataSetup + * @param CategorySetupFactory $categorySetupFactory + */ + public function __construct( + ModuleDataSetupInterface $moduleDataSetup, + CategorySetupFactory $categorySetupFactory + ) { + $this->moduleDataSetup = $moduleDataSetup; + $this->categorySetupFactory = $categorySetupFactory; + } + + /** + * @inheritDoc + */ + public static function getDependencies() + { + return []; + } + + /** + * @inheritDoc + */ + public function getAliases() + { + return []; + } + + /** + * @inheritDoc + */ + public function apply() + { + /** @var CategorySetup $eavSetup */ + $eavSetup = $this->categorySetupFactory->create(['setup' => $this->moduleDataSetup]); + $eavSetup->addAttribute( + Product::ENTITY, + 'custom_layout_update_file', + [ + 'type' => 'varchar', + 'label' => 'Custom Layout Update', + 'input' => 'select', + 'source' => \Magento\Catalog\Model\Product\Attribute\Source\LayoutUpdate::class, + 'required' => false, + 'sort_order' => 51, + 'backend' => \Magento\Catalog\Model\Product\Attribute\Backend\LayoutUpdate::class, + 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'group' => 'Design', + 'is_used_in_grid' => false, + 'is_visible_in_grid' => false, + 'is_filterable_in_grid' => false + ] + ); + + $eavSetup->addAttribute( + Category::ENTITY, + 'custom_layout_update_file', + [ + 'type' => 'varchar', + 'label' => 'Custom Layout Update', + 'input' => 'select', + 'source' => \Magento\Catalog\Model\Category\Attribute\Source\LayoutUpdate::class, + 'required' => false, + 'sort_order' => 51, + 'backend' => \Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate::class, + 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'group' => 'Custom Design', + 'is_used_in_grid' => false, + 'is_visible_in_grid' => false, + 'is_filterable_in_grid' => false + ] + ); + + $eavSetup->updateAttribute( + Product::ENTITY, + 'custom_layout_update', + 'is_visible', + false + ); + + $eavSetup->updateAttribute( + Category::ENTITY, + 'custom_layout_update', + 'is_visible', + false + ); + } +} diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddCategoryImageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddCategoryImageActionGroup.xml new file mode 100644 index 0000000000000..5837891667cc9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddCategoryImageActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddCategoryImageActionGroup"> + <annotations> + <description>Requires navigation to the Category creation/edit page. Adds the provided image to a Category. Validates that the Image exists.</description> + </annotations> + <arguments> + <argument name="image" defaultValue="ProductImage"/> + </arguments> + + <conditionalClick selector="{{AdminCategoryContentSection.sectionHeader}}" dependentSelector="{{AdminCategoryContentSection.uploadButton}}" visible="false" stepKey="openContentSection"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad"/> + <waitForElementVisible selector="{{AdminCategoryContentSection.uploadButton}}" stepKey="seeImageSectionIsReady"/> + <attachFile selector="{{AdminCategoryContentSection.uploadImageFile}}" userInput="{{image.file}}" stepKey="uploadFile"/> + <waitForAjaxLoad time="30" stepKey="waitForAjaxUpload"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> + <grabTextFrom selector="{{AdminCategoryContentSection.imageFileName}}" stepKey="grabCategoryFileName"/> + <assertRegExp stepKey="assertEquals" message="pass"> + <expectedResult type="string">/magento-logo(_[0-9]+)*?\.png$/</expectedResult> + <actualResult type="variable">grabCategoryFileName</actualResult> + </assertRegExp> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddCrossSellProductBySkuActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddCrossSellProductBySkuActionGroup.xml new file mode 100644 index 0000000000000..bf238a0b88417 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddCrossSellProductBySkuActionGroup.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="AddCrossSellProductBySkuActionGroup"> + <annotations> + <description>Adds the provided Product SKU as a Cross Sell Product on the Admin Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="sku"/> + </arguments> + + <!--Scroll up to avoid error--> + <scrollTo selector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDropdown}}" x="0" y="-100" stepKey="scrollTo"/> + <conditionalClick selector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDropdown}}" dependentSelector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDependent}}" visible="false" stepKey="openDropDownIfClosedRelatedUpSellCrossSell"/> + <click selector="{{AdminProductFormRelatedUpSellCrossSellSection.AddCrossSellProductsButton}}" stepKey="clickAddCrossSellButton"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{sku}}" stepKey="fillProductSkuFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <click selector="{{AdminProductModalSlideGridSection.productGridXRowYColumnButton('1', '1')}}" stepKey="selectProduct"/> + <click selector="{{AdminProductCrossSellModalSection.addSelectedProducts}}" stepKey="addRelatedProductSelected"/> + <waitForPageLoad stepKey="waitForModalDisappear"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductAttributeInProductModalActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductAttributeInProductModalActionGroup.xml new file mode 100644 index 0000000000000..0b8721c589706 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductAttributeInProductModalActionGroup.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="AddProductAttributeInProductModalActionGroup"> + <annotations> + <description>Adds the provided Attribute Code to the Product on the Admin Product creation/edit page.</description> + </annotations> + <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> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductCustomOptionFieldActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductCustomOptionFieldActionGroup.xml new file mode 100644 index 0000000000000..784a30de3d3c0 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductCustomOptionFieldActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddProductCustomOptionFieldActionGroup"> + <annotations> + <description>Add a custom Field Product Option to a Product based on the provided Option.</description> + </annotations> + <arguments> + <argument name="option" defaultValue="ProductOptionField"/> + </arguments> + + <scrollTo selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" stepKey="scrollToAddButtonOption"/> + <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" visible="false" stepKey="openCustomOptionSection"/> + <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOption"/> + <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.lastOptionTitle}}" stepKey="waitForOption"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.lastOptionTitle}}" userInput="{{option.title}}" stepKey="fillTitle"/> + <click selector="{{AdminProductCustomizableOptionsSection.lastOptionTypeParent}}" stepKey="openTypeSelect"/> + <click selector="{{AdminProductCustomizableOptionsSection.optionType('Field')}}" stepKey="selectTypeFile"/> + <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.optionPriceByTitle(option.title)}}" stepKey="waitForElements"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.optionPriceByTitle(option.title)}}" userInput="{{option.price}}" stepKey="fillPrice"/> + <selectOption selector="{{AdminProductCustomizableOptionsSection.optionPriceTypeByTitle(option.title)}}" userInput="{{option.price_type}}" stepKey="selectPriceType"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.optionSkuByTitle(option.title)}}" userInput="{{option.title}}" stepKey="fillSku"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductCustomOptionFileActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductCustomOptionFileActionGroup.xml new file mode 100644 index 0000000000000..961493d06f494 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductCustomOptionFileActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddProductCustomOptionFileActionGroup"> + <annotations> + <description>Add a custom File Product Option to a Product based on the provided File.</description> + </annotations> + <arguments> + <argument name="option" defaultValue="ProductOptionFile"/> + </arguments> + + <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" visible="false" stepKey="openCustomOptionSection"/> + <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOption"/> + <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.lastOptionTitle}}" stepKey="waitForOption"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.lastOptionTitle}}" userInput="{{option.title}}" stepKey="fillTitle"/> + <click selector="{{AdminProductCustomizableOptionsSection.lastOptionTypeParent}}" stepKey="openTypeSelect"/> + <click selector="{{AdminProductCustomizableOptionsSection.optionType('File')}}" stepKey="selectTypeFile"/> + <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.optionPriceByTitle(option.title)}}" stepKey="waitForElements"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.optionPriceByTitle(option.title)}}" userInput="{{option.price}}" stepKey="fillPrice"/> + <selectOption selector="{{AdminProductCustomizableOptionsSection.optionPriceTypeByTitle(option.title)}}" userInput="{{option.price_type}}" stepKey="selectPriceType"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.optionFileExtensionByTitle(option.title)}}" userInput="{{option.file_extension}}" stepKey="fillCompatibleExtensions"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductImageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductImageActionGroup.xml new file mode 100644 index 0000000000000..2efeca44003b3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductImageActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddProductImageActionGroup"> + <annotations> + <description>Adds the provided Product Image on the Admin Products creation/edit page.</description> + </annotations> + <arguments> + <argument name="image" defaultValue="ProductImage"/> + </arguments> + + <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductImagesSection"/> + <waitForPageLoad time="30" stepKey="waitForPageRefresh"/> + <waitForElementVisible selector="{{AdminProductImagesSection.imageUploadButton}}" stepKey="seeImageSectionIsReady"/> + <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="{{image.file}}" stepKey="uploadFile"/> + <waitForElementNotVisible selector="{{AdminProductImagesSection.uploadProgressBar}}" stepKey="waitForUpload"/> + <waitForElementVisible selector="{{AdminProductImagesSection.imageFile(image.fileName)}}" stepKey="waitForThumbnail"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml deleted file mode 100644 index bc7288d33bcb3..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AddSimpleProductToCart"> - <annotations> - <description>Navigates to the Storefront Product page. Then adds the Product to the Cart. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <argument name="product" defaultValue="product"/> - </arguments> - - <amOnPage url="{{StorefrontProductPage.url(product.custom_attributes[url_key])}}" stepKey="goToProductPage"/> - <waitForPageLoad stepKey="waitForProductPage"/> - <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addToCart"/> - <waitForElementNotVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAdding}}" stepKey="waitForElementNotVisibleAddToCartButtonTitleIsAdding"/> - <waitForElementNotVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAdded}}" stepKey="waitForElementNotVisibleAddToCartButtonTitleIsAdded"/> - <waitForElementVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAddToCart}}" stepKey="waitForElementVisibleAddToCartButtonTitleIsAddToCart"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" time="30" stepKey="waitForProductAddedMessage"/> - <see selector="{{StorefrontMessagesSection.success}}" userInput="You added {{product.name}} to your shopping cart." stepKey="seeAddToCartSuccessMessage"/> - </actionGroup> - <actionGroup name="StorefrontAddSimpleProductWithQtyActionGroup" extends="AddSimpleProductToCart"> - <arguments> - <argument name="quantity" type="string" defaultValue="1"/> - </arguments> - <fillField userInput="{{quantity}}" selector="{{StorefrontProductPageSection.qtyInput}}" stepKey="fillProductQty" after="goToProductPage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductWithQtyToCartFromStorefrontProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductWithQtyToCartFromStorefrontProductPageActionGroup.xml new file mode 100644 index 0000000000000..237db68dcf953 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductWithQtyToCartFromStorefrontProductPageActionGroup.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="AddProductWithQtyToCartFromStorefrontProductPageActionGroup" extends="AddToCartFromStorefrontProductPageActionGroup"> + <annotations> + <description>EXTENDS: addToCartFromStorefrontProductPage. Fills in the provided Product Quantity for the provided Product Name.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + <argument name="productQty" type="string"/> + </arguments> + + <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="{{productQty}}" stepKey="fillProductQuantity" before="addToCart"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddRelatedProductBySkuActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddRelatedProductBySkuActionGroup.xml new file mode 100644 index 0000000000000..ce62acf6dd32a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddRelatedProductBySkuActionGroup.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="AddRelatedProductBySkuActionGroup"> + <annotations> + <description>Adds the provided Product SKU as a Related Product on the Admin Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="sku"/> + </arguments> + + <!--Scroll up to avoid error--> + <scrollTo selector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDropdown}}" x="0" y="-100" stepKey="scrollTo"/> + <conditionalClick selector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDropdown}}" dependentSelector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDependent}}" visible="false" stepKey="openDropDownIfClosedRelatedUpSellCrossSell"/> + <click selector="{{AdminProductFormRelatedUpSellCrossSellSection.AddRelatedProductsButton}}" stepKey="clickAddRelatedProductButton"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{sku}}" stepKey="fillProductSkuFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <click selector="{{AdminProductModalSlideGridSection.productGridXRowYColumnButton('1', '1')}}" stepKey="selectProduct"/> + <click selector="{{AdminAddRelatedProductsModalSection.AddSelectedProductsButton}}" stepKey="addRelatedProductSelected"/> + <waitForElementNotVisible selector="{{AdminAddRelatedProductsModalSection.AddSelectedProductsButton}}" stepKey="waitForElementNotVisible"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSimpleProductToCartActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSimpleProductToCartActionGroup.xml new file mode 100644 index 0000000000000..81e3b8c99d9d2 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSimpleProductToCartActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddSimpleProductToCartActionGroup"> + <annotations> + <description>Navigates to the Storefront Product page. Then adds the Product to the Cart. Validates that the Success Message is present and correct.</description> + </annotations> + <arguments> + <argument name="product" defaultValue="product"/> + </arguments> + + <amOnPage url="{{StorefrontProductPage.url(product.custom_attributes[url_key])}}" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPage"/> + <click selector="{{StorefrontProductPageSection.addToCartBtn}}" stepKey="addToCart"/> + <waitForElementNotVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAdding}}" stepKey="waitForElementNotVisibleAddToCartButtonTitleIsAdding"/> + <waitForElementNotVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAdded}}" stepKey="waitForElementNotVisibleAddToCartButtonTitleIsAdded"/> + <waitForElementVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAddToCart}}" stepKey="waitForElementVisibleAddToCartButtonTitleIsAddToCart"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" time="30" stepKey="waitForProductAddedMessage"/> + <see selector="{{StorefrontMessagesSection.success}}" userInput="You added {{product.name}} to your shopping cart." stepKey="seeAddToCartSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSpecialPriceToProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSpecialPriceToProductActionGroup.xml new file mode 100644 index 0000000000000..dde25104b64dd --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddSpecialPriceToProductActionGroup.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="AddSpecialPriceToProductActionGroup"> + <annotations> + <description>Sets the provided Special Price on the Admin Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="price" type="string" defaultValue="8"/> + </arguments> + + <waitForPageLoad stepKey="waitForPageLoad"/> + <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink"/> + <waitForPageLoad stepKey="waitForAdvancedPricingModal"/> + <waitForElementVisible selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="waitSpecialPrice"/> + <fillField userInput="{{price}}" selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="fillSpecialPrice"/> + <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDone"/> + <waitForPageLoad stepKey="waitForAdvancedPricingModalGone"/> + <waitForElementNotVisible selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="waitForCloseModalWindow"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddToCartFromStorefrontProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddToCartFromStorefrontProductPageActionGroup.xml new file mode 100644 index 0000000000000..b27bda2b7c8f1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddToCartFromStorefrontProductPageActionGroup.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"> + <!--Click Add to Cart button in storefront product page--> + <actionGroup name="AddToCartFromStorefrontProductPageActionGroup"> + <annotations> + <description>Click on the Add to Cart button. Validates that the Success Message is present.</description> + </annotations> + <arguments> + <argument name="productName"/> + </arguments> + + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart"/> + <waitForPageLoad stepKey="waitForAddToCart"/> + <waitForElementNotVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAdding}}" stepKey="waitForElementNotVisibleAddToCartButtonTitleIsAdding"/> + <waitForElementNotVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAdded}}" stepKey="waitForElementNotVisibleAddToCartButtonTitleIsAdded"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <see selector="{{StorefrontMessagesSection.success}}" userInput="You added {{productName}} to your shopping cart." stepKey="seeAddToCartSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddUpSellProductBySkuActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddUpSellProductBySkuActionGroup.xml new file mode 100644 index 0000000000000..0ba6b479885e3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddUpSellProductBySkuActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddUpSellProductBySkuActionGroup" extends="AddRelatedProductBySkuActionGroup"> + <annotations> + <description>EXTENDS: AddRelatedProductBySkuActionGroup. Add the provided Product as an Up Sell Product.</description> + </annotations> + + <click selector="{{AdminProductFormRelatedUpSellCrossSellSection.AddUpSellProductsButton}}" stepKey="clickAddRelatedProductButton"/> + <conditionalClick selector="{{AdminAddUpSellProductsModalSection.Modal}} {{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminAddUpSellProductsModalSection.Modal}} {{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminAddUpSellProductsModalSection.Modal}} {{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminAddUpSellProductsModalSection.Modal}} {{AdminProductGridFilterSection.skuFilter}}" userInput="{{sku}}" stepKey="fillProductSkuFilter"/> + <click selector="{{AdminAddUpSellProductsModalSection.Modal}} {{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <click selector="{{AdminAddUpSellProductsModalSection.Modal}}{{AdminProductModalSlideGridSection.productGridXRowYColumnButton('1', '1')}}" stepKey="selectProduct"/> + <click selector="{{AdminAddUpSellProductsModalSection.AddSelectedProductsButton}}" stepKey="addRelatedProductSelected"/> + <waitForPageLoad stepKey="waitForPageToLoad1"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAddAdvancedPricingToTheProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAddAdvancedPricingToTheProductActionGroup.xml index f5e5957507eeb..958549d5768a8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAddAdvancedPricingToTheProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAddAdvancedPricingToTheProductActionGroup.xml @@ -28,16 +28,4 @@ <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceFixedPriceInput(index)}}" userInput="{{groupPrice.price}}" stepKey="selectProductTierPriceFixedPrice"/> <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton"/> </actionGroup> - - <!-- Customer group is selected in different way for B2B --> - <actionGroup name="AdminAddAdvancedPricingToTheProductExtendedActionGroup" extends="AdminAddAdvancedPricingToTheProductActionGroup"> - <annotations> - <description>EXTENDS: AdminAddAdvancedPricingToTheProductActionGroup. Removes 'selectProductTierPriceCustomerGroupInput'. Selects the provided Group Price at the provided Index for B2B.</description> - </annotations> - - <remove keyForRemoval="selectProductTierPriceCustomerGroupInput"/> - <click selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect(index)}}" stepKey="clickProductTierPriceCustGroupSelect" after="selectProductTierPriceWebsiteInput"/> - <waitForElement selector="{{AdminProductFormAdvancedPricingSection.productTierPriceGroupOrCatalogOption(groupPrice.customer_group)}}" time="30" stepKey="waitProductTierPriceGroupOrCatalogOption" after="clickProductTierPriceCustGroupSelect"/> - <click selector="{{AdminProductFormAdvancedPricingSection.productTierPriceGroupOrCatalogOption(groupPrice.customer_group)}}" stepKey="clickAllGroupsOption" after="waitProductTierPriceGroupOrCatalogOption"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAddAdvancedPricingToTheProductExtendedActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAddAdvancedPricingToTheProductExtendedActionGroup.xml new file mode 100644 index 0000000000000..cf8a3879886f1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAddAdvancedPricingToTheProductExtendedActionGroup.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="AdminAddAdvancedPricingToTheProductExtendedActionGroup" extends="AdminAddAdvancedPricingToTheProductActionGroup"> + <annotations> + <description>EXTENDS: AdminAddAdvancedPricingToTheProductActionGroup. Removes 'selectProductTierPriceCustomerGroupInput'. Selects the provided Group Price at the provided Index for B2B.</description> + </annotations> + + <remove keyForRemoval="selectProductTierPriceCustomerGroupInput"/> + <click selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect(index)}}" stepKey="clickProductTierPriceCustGroupSelect" after="selectProductTierPriceWebsiteInput"/> + <waitForElement selector="{{AdminProductFormAdvancedPricingSection.productTierPriceGroupOrCatalogOption(groupPrice.customer_group)}}" time="30" stepKey="waitProductTierPriceGroupOrCatalogOption" after="clickProductTierPriceCustGroupSelect"/> + <click selector="{{AdminProductFormAdvancedPricingSection.productTierPriceGroupOrCatalogOption(groupPrice.customer_group)}}" stepKey="clickAllGroupsOption" after="waitProductTierPriceGroupOrCatalogOption"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAddUnassignedAttributeToGroupActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAddUnassignedAttributeToGroupActionGroup.xml new file mode 100644 index 0000000000000..2815bb7a00abf --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAddUnassignedAttributeToGroupActionGroup.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="AdminAddUnassignedAttributeToGroupActionGroup" extends="CreateDefaultAttributeSetActionGroup"> + <arguments> + <argument name="firstOption" type="string" defaultValue="color"/> + <argument name="secondOption" type="string" defaultValue="material"/> + <argument name="group" type="string" defaultValue="Product Details"/> + </arguments> + <dragAndDrop selector1="{{AdminProductAttributeSetSection.attribute(firstOption)}}" selector2="{{AdminProductAttributeSetSection.attribute(group)}}" stepKey="unassign1"/> + <dragAndDrop selector1="{{AdminProductAttributeSetSection.attribute(secondOption)}}" selector2="{{AdminProductAttributeSetSection.attribute(group)}}" stepKey="unassign2"/> + <click selector="{{AdminProductAttributeSetSection.saveBtn}}" stepKey="clickSaveButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssertProductCustomOptionVisibleActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssertProductCustomOptionVisibleActionGroup.xml new file mode 100644 index 0000000000000..c00d214d53984 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssertProductCustomOptionVisibleActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertProductCustomOptionVisibleActionGroup"> + <arguments> + <argument name="option" defaultValue="ProductOptionField"/> + </arguments> + <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="expandContentTab"/> + <waitForPageLoad time="10" stepKey="waitCustomizableOptionsTabOpened"/> + <seeElement selector="{{AdminProductCustomizableOptionsSection.fillOptionTitle(option.title)}}" stepKey="assertCustomOptionVisible"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssertProductHasNoCustomOptionActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssertProductHasNoCustomOptionActionGroup.xml new file mode 100644 index 0000000000000..d4d10efb697a1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssertProductHasNoCustomOptionActionGroup.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="AdminAssertProductHasNoCustomOptionActionGroup" extends="AdminAssertProductCustomOptionVisibleActionGroup"> + <remove keyForRemoval="assertCustomOptionVisible"/> + <dontSeeElement selector="{{AdminProductCustomizableOptionsSection.fillOptionTitle(option.title)}}" after="waitCustomizableOptionsTabOpened" stepKey="assertNoCustomOption"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssertProductHasNoCustomOptionsActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssertProductHasNoCustomOptionsActionGroup.xml new file mode 100644 index 0000000000000..f1a41b4922c5a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssertProductHasNoCustomOptionsActionGroup.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertProductHasNoCustomOptionsActionGroup"> + <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="expandContentTab"/> + <waitForPageLoad time="10" stepKey="waitCustomizableOptionsTabOpened"/> + <dontSeeElement selector="{{AdminProductCustomizableOptionsSection.customOption}}" stepKey="assertNoCustomOptions"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssignProductToCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssignProductToCategoryActionGroup.xml new file mode 100644 index 0000000000000..b88129f382263 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminAssignProductToCategoryActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssignProductToCategoryActionGroup"> + <annotations> + <description>Navigates to existing product page. Changes the category and saves the product.</description> + </annotations> + <arguments> + <argument name="productId" type="string"/> + <argument name="categoryName" type="string"/> + </arguments> + + <amOnPage url="{{AdminProductEditPage.url(productId)}}" stepKey="amOnPage"/> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{categoryName}}]" stepKey="selectCategory"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the product." stepKey="seeSaveProductMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml deleted file mode 100644 index afc332cc28378..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml +++ /dev/null @@ -1,459 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Create a new category--> - <actionGroup name="CreateCategory"> - <annotations> - <description>Requires navigation to the Category creation page. Adds a new Subcategory. Validates that the Category was created.</description> - </annotations> - <arguments> - <argument name="categoryEntity" defaultValue="_defaultCategory"/> - </arguments> - - <seeInCurrentUrl url="{{AdminCategoryPage.url}}" stepKey="seeOnCategoryPage"/> - <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Category" stepKey="seeCategoryPageTitle"/> - <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{categoryEntity.name}}" stepKey="enterCategoryName"/> - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSEO"/> - <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="{{categoryEntity.name_lwr}}" stepKey="enterURLKey"/> - <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> - <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccess"/> - <seeInTitle userInput="{{categoryEntity.name}}" stepKey="seeNewCategoryPageTitle"/> - <seeElement selector="{{AdminCategorySidebarTreeSection.categoryInTree(categoryEntity.name)}}" stepKey="seeCategoryInTree"/> - </actionGroup> - - <!-- Go to create new root or sub category page --> - <actionGroup name="goToCreateCategoryPage"> - <annotations> - <description>Goes to the Category grid page. Clicks the Add Subcategory button.</description> - </annotations> - <arguments> - <argument name="selector" defaultValue="AdminCategorySidebarActionSection.AddSubcategoryButton"/> - </arguments> - - <amOnPage url="{{AdminCategoryPage.url}}" stepKey="amOnAdminCategoryPage"/> - <scrollToTopOfPage stepKey="scrollToTopOfPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <click selector="{{selector}}" stepKey="clickOnAddCategory"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Category" stepKey="seeCategoryPageTitle"/> - </actionGroup> - - <!-- Go to admin category page by id --> - <actionGroup name="goToAdminCategoryPageById"> - <annotations> - <description>Goes to the Category edit page for a specified Category ID.</description> - </annotations> - <arguments> - <argument name="id" type="string"/> - </arguments> - - <amOnPage url="{{AdminCategoryEditPage.url(id)}}" stepKey="amOnAdminCategoryPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="{{id}}" stepKey="seeCategoryPageTitle"/> - </actionGroup> - - <!-- Fill category fields --> - <actionGroup name="fillCategoryForm"> - <annotations> - <description>Requires navigation to the Subcategory creation/edit page. Fills the Subcategory Name. Fills the Search Engine Optimization.</description> - </annotations> - <arguments> - <argument name="categoryEntity" defaultValue="_defaultCategory"/> - </arguments> - - <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{categoryEntity.name}}" stepKey="enterCategoryName"/> - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSEO"/> - <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="{{categoryEntity.name_lwr}}" stepKey="enterURLKey"/> - </actionGroup> - - <!-- Save category form --> - <actionGroup name="saveCategoryForm"> - <annotations> - <description>Requires navigation to the Category creation/edit page. Checks that the url contains the AdminCategoryPage url. Saves the Category.</description> - </annotations> - - <seeInCurrentUrl url="{{AdminCategoryPage.url}}" stepKey="seeOnCategoryPage"/> - <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> - <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccess"/> - </actionGroup> - - <!--Upload image for category --> - <actionGroup name="addCategoryImage"> - <annotations> - <description>Requires navigation to the Category creation/edit page. Adds the provided image to a Category. Validates that the Image exists.</description> - </annotations> - <arguments> - <argument name="image" defaultValue="ProductImage"/> - </arguments> - - <conditionalClick selector="{{AdminCategoryContentSection.sectionHeader}}" dependentSelector="{{AdminCategoryContentSection.uploadButton}}" visible="false" stepKey="openContentSection"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad"/> - <waitForElementVisible selector="{{AdminCategoryContentSection.uploadButton}}" stepKey="seeImageSectionIsReady"/> - <attachFile selector="{{AdminCategoryContentSection.uploadImageFile}}" userInput="{{image.file}}" stepKey="uploadFile"/> - <waitForAjaxLoad time="30" stepKey="waitForAjaxUpload"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> - <grabTextFrom selector="{{AdminCategoryContentSection.imageFileName}}" stepKey="grabCategoryFileName"/> - <assertRegExp stepKey="assertEquals" message="pass"> - <expectedResult type="string">/magento-logo(_[0-9]+)*?\.png$/</expectedResult> - <actualResult type="variable">grabCategoryFileName</actualResult> - </assertRegExp> - </actionGroup> - - <!-- Remove image from category --> - <actionGroup name="removeCategoryImage"> - <annotations> - <description>Requires navigation to the Category creation/edit page. Removes the current Category image. Validates that the Image does not exist.</description> - </annotations> - - <conditionalClick selector="{{AdminCategoryContentSection.sectionHeader}}" dependentSelector="{{AdminCategoryContentSection.uploadButton}}" visible="false" stepKey="openContentSection"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <waitForElementVisible selector="{{AdminCategoryContentSection.uploadButton}}" stepKey="seeImageSectionIsReady"/> - <click selector="{{AdminCategoryContentSection.removeImageButton}}" stepKey="clickRemoveImage"/> - <waitForAjaxLoad time="30" stepKey="waitForAjaxUpload"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> - <dontSee selector="{{AdminCategoryContentSection.imageFileName}}" stepKey="dontSeeImage"/> - </actionGroup> - - <actionGroup name="checkCategoryImageInAdmin"> - <annotations> - <description>Requires navigation to the Category creation/edit page. Click on the Upload button. Validates that the Image exists.</description> - </annotations> - <arguments> - <argument name="image" defaultValue="ProductImage"/> - </arguments> - - <conditionalClick selector="{{AdminCategoryContentSection.sectionHeader}}" dependentSelector="{{AdminCategoryContentSection.uploadButton}}" visible="false" stepKey="openContentSection"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <waitForElementVisible selector="{{AdminCategoryContentSection.uploadButton}}" stepKey="seeImageSectionIsReady"/> - <grabTextFrom selector="{{AdminCategoryContentSection.imageFileName}}" stepKey="grabCategoryFileName"/> - <assertRegExp stepKey="assertEquals" message="pass"> - <expectedResult type="string">/magento-logo(_[0-9]+)*?\.png$/</expectedResult> - <actualResult type="variable">grabCategoryFileName</actualResult> - </assertRegExp> - </actionGroup> - - <!-- Action to navigate to Media Gallery. Used in tests to cleanup uploaded images --> - <actionGroup name="navigateToMediaGallery"> - <annotations> - <description>Navigates to the category page and Opens the Media Gallery.</description> - </annotations> - - <amOnPage url="{{AdminCategoryPage.url}}" stepKey="amOnAdminCategoryPage"/> - <waitForElementVisible selector="{{AdminCategoryContentSection.sectionHeader}}" stepKey="waitForContentSection"/> - <conditionalClick selector="{{AdminCategoryContentSection.sectionHeader}}" dependentSelector="{{AdminCategoryContentSection.uploadButton}}" visible="false" stepKey="openContentSection"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <waitForElementVisible selector="{{AdminCategoryContentSection.selectFromGalleryButton}}" stepKey="waitForSelectFromGalleryButton"/> - <click selector="{{AdminCategoryContentSection.selectFromGalleryButton}}" stepKey="clickSelectFromGalleryButton"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> - </actionGroup> - - <!--Actions to check if a category exists on StoreFront--> - <actionGroup name="CheckCategoryOnStorefront"> - <annotations> - <description>Navigates to the category page on the storefront and asserts that the title is correct for page and browser.</description> - </annotations> - <arguments> - <argument name="categoryEntity" defaultValue="_defaultCategory"/> - </arguments> - - <amOnPage url="/{{categoryEntity.name_lwr}}.html" stepKey="goToCategoryFrontPage"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <see selector="{{StorefrontCategoryMainSection.CategoryTitle}}" userInput="{{categoryEntity.name_lwr}}" stepKey="assertCategoryOnStorefront"/> - <seeInTitle userInput="{{categoryEntity.name}}" stepKey="seeCategoryNameInTitle"/> - </actionGroup> - - <!--Actions to delete category--> - <actionGroup name="DeleteCategory"> - <annotations> - <description>Navigates to the category page and deletes the specified category.</description> - </annotations> - <arguments> - <argument name="categoryEntity" defaultValue="_defaultCategory"/> - </arguments> - - <amOnPage url="{{AdminCategoryPage.url}}" stepKey="goToCategoryPage"/> - <waitForPageLoad time="60" stepKey="waitForCategoryPageLoad"/> - <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(categoryEntity.name)}}" stepKey="clickCategoryLink"/> - <click selector="{{AdminCategoryMainActionsSection.DeleteButton}}" stepKey="clickDelete"/> - <waitForElementVisible selector="{{AdminCategoryModalSection.message}}" stepKey="waitForConfirmationModal"/> - <see selector="{{AdminCategoryModalSection.message}}" userInput="Are you sure you want to delete this category?" stepKey="seeDeleteConfirmationMessage"/> - <click selector="{{AdminCategoryModalSection.ok}}" stepKey="confirmDelete"/> - <waitForPageLoad time="60" stepKey="waitForDeleteToFinish"/> - <see selector="You deleted the category." stepKey="seeDeleteSuccess"/> - <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="expandToSeeAllCategories"/> - <dontSee selector="{{AdminCategorySidebarTreeSection.categoryInTree(categoryEntity.name)}}" stepKey="dontSeeCategoryInTree"/> - </actionGroup> - <actionGroup name="AdminDeleteCategoryByName" extends="DeleteCategory"> - <arguments> - <argument name="categoryName" type="string" defaultValue="category1"/> - </arguments> - <remove keyForRemoval="clickCategoryLink"/> - <remove keyForRemoval="dontSeeCategoryInTree"/> - <remove keyForRemoval="expandToSeeAllCategories"/> - <conditionalClick selector="{{AdminCategorySidebarTreeSection.expandAll}}" dependentSelector="{{AdminCategorySidebarTreeSection.categoryByName(categoryName)}}" visible="false" stepKey="expandCategories" after="waitForCategoryPageLoad"/> - <click selector="{{AdminCategorySidebarTreeSection.categoryByName(categoryName)}}" stepKey="clickCategory" after="expandCategories"/> - <conditionalClick selector="{{AdminCategorySidebarTreeSection.expandAll}}" dependentSelector="{{AdminCategorySidebarTreeSection.categoryByName(categoryName)}}" visible="false" stepKey="expandCategoriesToSeeAll" after="seeDeleteSuccess"/> - <dontSee selector="{{AdminCategorySidebarTreeSection.categoryByName(categoryName)}}" stepKey="dontSeeCategory" after="expandCategoriesToSeeAll"/> - </actionGroup> - - <!-- Actions to fill out a new category from the product page--> - <!-- The action assumes that you are already on an admin product configuration page --> - <actionGroup name="FillNewProductCategory" > - <annotations> - <description>Actions to fill out a new category from the product page with specified category and parent category names.</description> - </annotations> - <arguments> - <argument name="categoryName" defaultValue="Test Category" type="string"/> - <argument name="parentCategoryName" defaultValue="default" type="string"/> - </arguments> - - <!-- Click on new Category --> - <click stepKey="clickNewCategory" selector="{{AdminProductCategoryCreationSection.newCategory}}"/> - <waitForPageLoad stepKey="waitForFieldSet"/> - <fillField stepKey="fillCategoryName" selector="{{AdminProductCategoryCreationSection.nameInput}}" userInput="{{categoryName}}"/> - - <!-- Search and select a parent category for the product --> - <click stepKey="clickParentCategory" selector="{{AdminProductCategoryCreationSection.parentCategory}}"/> - <waitForPageLoad stepKey="waitForDropDownVisible"/> - <fillField stepKey="searchForParent" userInput="{{parentCategoryName}}" selector="{{AdminProductCategoryCreationSection.parentSearch}}"/> - <waitForPageLoad stepKey="waitForFieldResults"/> - <click stepKey="clickParent" selector="{{AdminProductCategoryCreationSection.parentSearchResult}}"/> - <click stepKey="createCategory" selector="{{AdminProductCategoryCreationSection.createCategory}}"/> - <waitForPageLoad stepKey="waitForCategoryCreated"/> - </actionGroup> - - <!-- Actions to delete the category last made --> - <actionGroup name="DeleteMostRecentCategory"> - <annotations> - <description>Actions to delete the category last made (the last category on the list).</description> - </annotations> - - <amOnPage url="/{{AdminCategoryPage.url}}" stepKey="goToCategoryFrontPage"/> - <waitForPageLoad stepKey="waitForCategoryPageLoad"/> - <click stepKey="goToCreateCategory" selector="{{AdminCategorySidebarTreeSection.lastCreatedCategory}}"/> - <waitForPageLoad stepKey="waitForCreatedCategoryPageLoad"/> - <click stepKey="clickDeleteCategory" selector="{{AdminCategoryMainActionsSection.DeleteButton}}"/> - <waitForPageLoad stepKey="waitForModalVisible"/> - <click stepKey="clickOkToDelete" selector="{{AdminCategoryModalSection.ok}}"/> - <waitForPageLoad stepKey="waitForModalNotVisible"/> - </actionGroup> - - <!-- Actions to check if a certain category is present on the page --> - <actionGroup name="CategoryPresent" > - <annotations> - <description>Navigates to category page, asserts category is there. Navigates to storefront category page and asserts category is there. This action group will not work categories where name does NOT equal SEO.</description> - </annotations> - <arguments> - <argument name="categoryName" defaultValue="Test Category" type="string"/> - </arguments> - - <amOnPage url="{{AdminCategoryPage.url}}" stepKey="goToCategoryAdminPage"/> - <waitForPageLoad stepKey="waitForCategoryAdminPageLoad"/> - <see userInput="{{categoryName}}" stepKey="assertCategoryOnAdminPage" selector="{{AdminCategorySidebarTreeSection.treeContainer}}"/> - <amOnPage url="/{{categoryName}}.html" stepKey="goToCustomerFrontPage"/> - <see userInput="{{categoryName}}" stepKey="assertCategoryNameOnStorefront" selector="{{StorefrontCategoryMainSection.CategoryTitle}}"/> - <waitForPageLoad stepKey="waitForCustomerCategoryPageLoad"/> - </actionGroup> - - <!--Check that name field is required--> - <actionGroup name="CheckCategoryNameIsRequiredField"> - <annotations> - <description>Navigates to category page, attempts to add subcategory without name. Expects required field prompt.</description> - </annotations> - - <seeInCurrentUrl url="{{AdminCategoryPage.url}}" stepKey="seeOnCategoryPage"/> - <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> - <clearField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" stepKey="makeNameFieldEmpty"/> - <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> - <seeInCurrentUrl url="{{AdminCategoryPage.url}}add" stepKey="seeBackOnCreateCategoryPage"/> - <see selector="{{AdminCategoryBasicFieldSection.FieldError('uid')}}" userInput="This is a required field." stepKey="seeErrorMessage"/> - </actionGroup> - - <actionGroup name="switchCategoryStoreView"> - <annotations> - <description>Navigates to category page, selects a category and changes store view to specified store.</description> - </annotations> - <arguments> - <argument name="Store"/> - <argument name="CatName"/> - </arguments> - - <amOnPage url="{{AdminCategoryPage.page}}" stepKey="amOnCategoryPage"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(CatName)}}" stepKey="navigateToCreatedCategory"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <waitForLoadingMaskToDisappear stepKey="waitForSpinner"/> - <scrollToTopOfPage stepKey="scrollToToggle"/> - <click selector="{{AdminCategoryMainActionsSection.CategoryStoreViewDropdownToggle}}" stepKey="openStoreViewDropDown"/> - <click selector="{{AdminCategoryMainActionsSection.CategoryStoreViewOption(Store)}}" stepKey="selectStoreView"/> - <waitForPageLoad stepKey="waitForPageLoad3"/> - <waitForLoadingMaskToDisappear stepKey="waitForSpinner2"/> - <click selector="{{AdminCategoryMainActionsSection.CategoryStoreViewModalAccept}}" stepKey="selectStoreViewAccept"/> - <waitForPageLoad stepKey="waitForStoreViewChangeLoad"/> - </actionGroup> - - <actionGroup name="switchCategoryToAllStoreView"> - <annotations> - <description>Navigates to category page, selects a category and changes store view to all stores.</description> - </annotations> - <arguments> - <argument name="CatName"/> - </arguments> - - <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(CatName)}}" stepKey="navigateToCreatedCategory"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <waitForLoadingMaskToDisappear stepKey="waitForSpinner1"/> - <scrollToTopOfPage stepKey="scrollToToggle"/> - <click selector="{{AdminCategoryMainActionsSection.CategoryStoreViewDropdownToggle}}" stepKey="openStoreViewDropDown"/> - <click selector="{{AdminCategoryMainActionsSection.allStoreViews}}" stepKey="clickStoreViewByName"/> - <see selector="{{AdminCategoryMainActionsSection.storeSwitcher}}" userInput="All Store Views" stepKey="seeAllStoreView"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <waitForLoadingMaskToDisappear stepKey="waitForSpinner2"/> - <click selector="{{AdminCategoryMainActionsSection.CategoryStoreViewModalAccept}}" stepKey="selectStoreViewAccept"/> - <waitForPageLoad stepKey="waitForStoreViewChangeLoad"/> - </actionGroup> - - <actionGroup name="navigateToCreatedCategory"> - <annotations> - <description>Navigates to category page, selects a category by specified category.</description> - </annotations> - <arguments> - <argument name="Category"/> - </arguments> - - <amOnPage url="{{AdminCategoryPage.page}}" stepKey="amOnCategoryPage"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="expandAll"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(Category.Name)}}" stepKey="navigateToCreatedCategory"/> - <waitForLoadingMaskToDisappear stepKey="waitForSpinner"/> - </actionGroup> - - <actionGroup name="ChangeSeoUrlKey"> - <annotations> - <description>Requires navigation to category creation/edit. Updates the Search Engine Optimization.</description> - </annotations> - <arguments> - <argument name="value" type="string"/> - </arguments> - - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSeoSection"/> - <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="{{value}}" stepKey="enterURLKey"/> - <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> - <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessage"/> - </actionGroup> - - <actionGroup name="ChangeSeoUrlKeyForSubCategory"> - <annotations> - <description>Requires navigation to subcategory creation/edit. Updates the Search Engine Optimization.</description> - </annotations> - <arguments> - <argument name="value" type="string"/> - </arguments> - - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSeoSection"/> - <uncheckOption selector="{{AdminCategorySEOSection.UrlKeyDefaultValueCheckbox}}" stepKey="uncheckDefaultValue"/> - <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="{{value}}" stepKey="enterURLKey"/> - <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> - <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessage"/> - </actionGroup> - - <actionGroup name="OpenCategoryFromCategoryTree"> - <annotations> - <description>Navigates to category page, selects a category by specified category. Replicates actionGroup:navigateToCreatedCategory.</description> - </annotations> - <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> - - <actionGroup name="AdminAssignProductToCategory"> - <annotations> - <description>Navigates to existing product page. Changes the category and saves the product.</description> - </annotations> - <arguments> - <argument name="productId" type="string"/> - <argument name="categoryName" type="string"/> - </arguments> - - <amOnPage url="{{AdminProductEditPage.url(productId)}}" stepKey="amOnPage"/> - <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{categoryName}}]" stepKey="selectCategory"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You saved the product." stepKey="seeSaveProductMessage"/> - </actionGroup> - - <actionGroup name="FillCategoryNameAndUrlKeyAndSave"> - <annotations> - <description>Requires navigation to subcategory creation/edit. Fills the name, and sets the Search Engine Optimization for the category.</description> - </annotations> - <arguments> - <argument name="categoryName" type="string"/> - <argument name="categoryUrlKey" type="string"/> - </arguments> - - <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{categoryName}}" stepKey="enterCategoryName"/> - <scrollTo selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="scrollToSearchEngineOptimization"/> - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSEO"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> - <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="{{categoryUrlKey}}" stepKey="enterURLKey"/> - <scrollToTopOfPage stepKey="scrollToTheTopOfPage"/> - <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> - </actionGroup> - - <actionGroup name="AdminCategoryAssignProduct"> - <annotations> - <description>Requires navigation to category creation/edit page. Assign products to category - using "Products in Category" tab.</description> - </annotations> - <arguments> - <argument name="productSku" type="string"/> - </arguments> - - <conditionalClick selector="{{AdminCategoryBasicFieldSection.productsInCategory}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="false" stepKey="clickOnProductInCategory"/> - <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clickOnResetFilter"/> - <fillField selector="{{AdminCategoryContentSection.productTableColumnSku}}" userInput="{{productSku}}" stepKey="fillSkuFilter"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton"/> - <click selector="{{AdminCategoryContentSection.productTableRow}}" stepKey="selectProductFromTableRow"/> - </actionGroup> - - <actionGroup name="DeleteDefaultCategoryChildren"> - <annotations> - <description>Deletes all children categories of Default Root Category.</description> - </annotations> - - <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToAdminCategoryPage"/> - <executeInSelenium function="function ($webdriver) use ($I) { - $children = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::xpath('//ul[contains(@class, \'x-tree-node-ct\')]/li[@class=\'x-tree-node\' and contains(., - \'{{DefaultCategory.name}}\')]/ul[contains(@class, \'x-tree-node-ct\')]/li//a')); - while (!empty($children)) { - $I->click('//ul[contains(@class, \'x-tree-node-ct\')]/li[@class=\'x-tree-node\' and contains(., - \'{{DefaultCategory.name}}\')]/ul[contains(@class, \'x-tree-node-ct\')]/li//a'); - $I->waitForPageLoad(30); - $I->click('#delete'); - $I->waitForElementVisible('aside.confirm .modal-footer button.action-accept'); - $I->click('aside.confirm .modal-footer button.action-accept'); - $I->waitForPageLoad(30); - $I->waitForElementVisible('#messages div.message-success', 30); - $I->see('You deleted the category.', '#messages div.message-success'); - $children = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::xpath('//ul[contains(@class, \'x-tree-node-ct\')]/li[@class=\'x-tree-node\' and contains(., - \'{{DefaultCategory.name}}\')]/ul[contains(@class, \'x-tree-node-ct\')]/li//a')); - } - }" stepKey="deleteAllChildCategories"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryAssignProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryAssignProductActionGroup.xml new file mode 100644 index 0000000000000..454c65c8bc5c3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryAssignProductActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCategoryAssignProductActionGroup"> + <annotations> + <description>Requires navigation to category creation/edit page. Assign products to category - using "Products in Category" tab.</description> + </annotations> + <arguments> + <argument name="productSku" type="string"/> + </arguments> + + <conditionalClick selector="{{AdminCategoryBasicFieldSection.productsInCategory}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="false" stepKey="clickOnProductInCategory"/> + <scrollTo stepKey="scrollToProductGrid" selector="{{AdminCategoryBasicFieldSection.productsInCategory}}" x="0" y="-80" /> + <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clickOnResetFilter"/> + <fillField selector="{{AdminCategoryContentSection.productTableColumnSku}}" userInput="{{productSku}}" stepKey="fillSkuFilter"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton"/> + <click selector="{{AdminCategoryContentSection.productTableRow}}" stepKey="selectProductFromTableRow"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductIsMissingInCategoryProductsGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductIsMissingInCategoryProductsGridActionGroup.xml new file mode 100644 index 0000000000000..4b360cde1485a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductIsMissingInCategoryProductsGridActionGroup.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="AdminCheckProductIsMissingInCategoryProductsGridActionGroup"> + <arguments> + <argument name="productName" type="string"/> + </arguments> + <dontSee selector="{{AdminCategoryProductsGridSection.nameColumn}}" userInput="{{productName}}" stepKey="dontSeeProduct"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductPositionInCategoryProductsGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductPositionInCategoryProductsGridActionGroup.xml new file mode 100644 index 0000000000000..2c0832e0ee877 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductPositionInCategoryProductsGridActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCheckProductPositionInCategoryProductsGridActionGroup"> + <arguments> + <argument name="position" type="string"/> + <argument name="productName" type="string"/> + </arguments> + <see selector="{{AdminCategoryProductsGridSection.rowProductName(position)}}" userInput="{{productName}}" stepKey="assertProductPosition"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductsInGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductsInGridActionGroup.xml deleted file mode 100644 index 440739875c0c1..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCheckProductsInGridActionGroup.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminCheckProductIsMissingInCategoryProductsGrid"> - <arguments> - <argument name="productName" type="string"/> - </arguments> - <dontSee selector="{{AdminCategoryProductsGridSection.nameColumn}}" userInput="{{productName}}" stepKey="dontSeeProduct"/> - </actionGroup> - <actionGroup name="AdminCheckProductPositionInCategoryProductsGrid"> - <arguments> - <argument name="position" type="string"/> - <argument name="productName" type="string"/> - </arguments> - <see selector="{{AdminCategoryProductsGridSection.rowProductName(position)}}" userInput="{{productName}}" stepKey="assertProductPosition"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminClickOnAdvancedInventoryButtonActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminClickOnAdvancedInventoryButtonActionGroup.xml new file mode 100644 index 0000000000000..33b99cef6d650 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminClickOnAdvancedInventoryButtonActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminClickOnAdvancedInventoryButtonActionGroup"> + <annotations> + <description>Clicks on the 'Advanced Inventory' link on the Admin Product creation/edit page.</description> + </annotations> + + <click selector="{{AdminProductFormSection.advancedInventoryButton}}" stepKey="clickOnAdvancedInventoryLink"/> + <waitForPageLoad stepKey="waitForAdvancedInventoryPageToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateAttributeFromProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateAttributeFromProductPageActionGroup.xml new file mode 100644 index 0000000000000..ae8e8a84149e1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateAttributeFromProductPageActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateAttributeFromProductPageActionGroup"> + <annotations> + <description>From the Product creation/edit page, under 'Configurations', Clicks on 'Create Configurations'. Clicks on 'Create New Attribute'. Fills Label. Selects Type. Clicks on Save.</description> + </annotations> + <arguments> + <argument name="attributeName" type="string"/> + <argument name="attributeType" type="string" defaultValue="TextField"/> + </arguments> + + <click selector="{{AdminProductFormSection.addAttributeBtn}}" stepKey="clickAddAttributeBtn"/> + <see userInput="Select Attribute" stepKey="checkNewAttributePopUpAppeared"/> + <click selector="{{AdminProductFormAttributeSection.createNewAttribute}}" stepKey="clickCreateNewAttribute"/> + <fillField selector="{{AdminProductFormNewAttributeSection.attributeLabel}}" userInput="{{attributeName}}" stepKey="fillAttributeLabel"/> + <selectOption selector="{{AdminProductFormNewAttributeSection.attributeType}}" userInput="{{attributeType}}" stepKey="selectAttributeType"/> + <click selector="{{AdminProductFormNewAttributeSection.saveAttribute}}" stepKey="saveAttribute"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateAttributeFromProductPageWithScopeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateAttributeFromProductPageWithScopeActionGroup.xml new file mode 100644 index 0000000000000..2ee84fcb24b68 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateAttributeFromProductPageWithScopeActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateAttributeFromProductPageWithScopeActionGroup" extends="AdminCreateAttributeFromProductPageActionGroup" insertAfter="selectAttributeType"> + <arguments> + <argument name="scope" type="string" defaultValue="Store View"/> + </arguments> + <conditionalClick selector="{{AdminProductFormNewAttributeAdvancedSection.sectionHeader}}" dependentSelector="{{AdminProductFormNewAttributeAdvancedSection.scope}}" visible="false" stepKey="openAttributeAdvancedSection"/> + <selectOption selector="{{AdminProductFormNewAttributeAdvancedSection.scope}}" userInput="{{scope}}" stepKey="selectScope"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateAttributeWithSearchWeightActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateAttributeWithSearchWeightActionGroup.xml new file mode 100644 index 0000000000000..2ca4c9b616862 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateAttributeWithSearchWeightActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateAttributeWithSearchWeightActionGroup" extends="AdminProductPageCreateAttributeSetWithAttributeActionGroup" insertAfter="selectAttributeType"> + <annotations> + <description>EXTENDS: AdminProductPageCreateAttributeSetWithAttribute. Sets the provided Search Weight and Default Values.</description> + </annotations> + <arguments> + <argument name="weight" type="string" defaultValue="1"/> + <argument name="defaultValue" type="string" defaultValue="default"/> + </arguments> + + <click selector="{{AdminProductFormNewAttributeAdvancedSection.sectionHeader}}" stepKey="openAdvancedSection"/> + <fillField selector="{{AdminProductFormNewAttributeAdvancedSection.defaultValue}}" userInput="{{defaultValue}}" stepKey="inputDefault"/> + <click selector="{{AdminProductFormNewAttributeStorefrontSection.sectionHeader}}" stepKey="openStorefrontSection"/> + <checkOption selector="{{AdminProductFormNewAttributeStorefrontSection.useInSearch}}" stepKey="checkUseInSearch"/> + <waitForElementVisible selector="{{AdminProductFormNewAttributeStorefrontSection.searchWeight}}" stepKey="waitForSearchWeight"/> + <selectOption selector="{{AdminProductFormNewAttributeStorefrontSection.searchWeight}}" userInput="{{weight}}" stepKey="selectWeight"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateAttributeWithValueWithTwoStoreViesFromProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateAttributeWithValueWithTwoStoreViesFromProductPageActionGroup.xml new file mode 100644 index 0000000000000..ec02df1fbf327 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateAttributeWithValueWithTwoStoreViesFromProductPageActionGroup.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateAttributeWithValueWithTwoStoreViesFromProductPageActionGroup" extends="AdminCreateAttributeFromProductPageActionGroup"> + <remove keyForRemoval="saveAttribute"/> + <annotations> + <description>EXTENDS: AdminCreateAttributeFromProductPage. Adds the 2 provided Store Views to a Product. Clicks on Save.</description> + </annotations> + <arguments> + <argument name="firstStoreViewName" type="string"/> + <argument name="secondStoreViewName" type="string"/> + </arguments> + + <click selector="{{AdminProductFormNewAttributeSection.addValue}}" stepKey="addValue" after="selectAttributeType"/> + <seeElement selector="{{AdminProductFormNewAttributeSection.optionViewName(firstStoreViewName))}}" stepKey="seeFirstStoreView"/> + <seeElement selector="{{AdminProductFormNewAttributeSection.optionViewName(firstStoreViewName))}}" stepKey="seeSecondStoreView"/> + <fillField selector="{{AdminProductFormNewAttributeSection.optionValue('1'))}}" userInput="default" stepKey="fillDefaultStoreView"/> + <fillField selector="{{AdminProductFormNewAttributeSection.optionValue('2'))}}" userInput="admin" stepKey="fillAdminStoreView"/> + <fillField selector="{{AdminProductFormNewAttributeSection.optionValue('3'))}}" userInput="view1" stepKey="fillFirstStoreView"/> + <fillField selector="{{AdminProductFormNewAttributeSection.optionValue('4'))}}" userInput="view2" stepKey="fillSecondStoreView"/> + + <!--Check store view in Manage Titles section--> + <click selector="{{AdminProductFormNewAttributeSection.manageTitlesHeader}}" stepKey="openManageTitlesSection"/> + <seeElement selector="{{AdminProductFormNewAttributeSection.manageTitlesViewName(customStoreEN.name)}}" stepKey="seeFirstStoreViewName"/> + <seeElement selector="{{AdminProductFormNewAttributeSection.manageTitlesViewName(customStoreFR.name)}}" stepKey="seeSecondStoreViewName"/> + <click selector="{{AdminProductFormNewAttributeSection.saveAttribute}}" stepKey="saveAttribute1"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateCatalogProductWidgetActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateCatalogProductWidgetActionGroup.xml new file mode 100644 index 0000000000000..1b9b9d60b36e2 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateCatalogProductWidgetActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateCatalogProductWidgetActionGroup" extends="AdminCreateWidgetActionGroup"> + <annotations> + <description>EXTENDS: AdminCreateWidgetActionGroup. Creates Catalog Category Link Widget.</description> + </annotations> + <arguments> + <argument name="categoryName" type="string" defaultValue="{{DefaultCategory.name}}"/> + </arguments> + + <waitForElementVisible selector="{{AdminNewWidgetSection.selectCategory}}" after="clickWidgetOptions" stepKey="waitForSelectCategoryButtonVisible"/> + <click selector="{{AdminNewWidgetSection.selectCategory}}" stepKey="clickOnSelectCategory"/> + <waitForPageLoad stepKey="waitForCategoryTreeLoaded"/> + <click selector="{{AdminCategorySidebarTreeSection.expandRootCategoryByName(DefaultCategory.name)}}" stepKey="clickToExpandDefaultCategory"/> + <waitForElementVisible selector="{{AdminCategorySidebarTreeSection.categoryInTree(categoryName)}}" stepKey="waitForCategoryVisible"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(categoryName)}}" stepKey="selectCategory"/> + <waitForPageLoad stepKey="waitForWidgetPageLoaded"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveButton"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The widget instance has been saved" stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateRecentlyProductsWidgetActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateRecentlyProductsWidgetActionGroup.xml new file mode 100644 index 0000000000000..e22620790ef70 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateRecentlyProductsWidgetActionGroup.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="AdminCreateRecentlyProductsWidgetActionGroup" extends="AdminCreateWidgetActionGroup"> + <annotations> + <description>EXTENDS: AdminCreateWidgetActionGroup. Adds Product Attributes/Buttons to a Widget. Clicks on the Save button.</description> + </annotations> + + <selectOption selector="{{AdminCatalogProductWidgetSection.productAttributesToShow}}" parameterArray="['Name', 'Image', 'Price']" stepKey="selectAllProductAttributes"/> + <selectOption selector="{{AdminCatalogProductWidgetSection.productButtonsToShow}}" parameterArray="['Add to Cart', 'Add to Compare', 'Add to Wishlist']" stepKey="selectAllProductButtons"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveWidget"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessageAppears"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateSearchableProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateSearchableProductAttributeActionGroup.xml new file mode 100644 index 0000000000000..1e1418b3e0e75 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateSearchableProductAttributeActionGroup.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateSearchableProductAttributeActionGroup" extends="CreateProductAttributeActionGroup" insertAfter="checkRequired"> + <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="goToStorefrontPropertiesTab"/> + <waitForElementVisible selector="{{StorefrontPropertiesSection.PageTitle}}" stepKey="waitTabLoad"/> + <selectOption selector="{{AdvancedAttributePropertiesSection.UseInSearch}}" userInput="Yes" stepKey="setSearchable"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateSimpleProductWithTextOptionCharLimitActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateSimpleProductWithTextOptionCharLimitActionGroup.xml new file mode 100644 index 0000000000000..66620e3d9dd07 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateSimpleProductWithTextOptionCharLimitActionGroup.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateSimpleProductWithTextOptionCharLimitActionGroup"> + <annotations> + <description>Goes to the Admin Product grid page. Clicks on Add. Fills the provided Product details (Name, SKU, Price, Quantity, Category and URL). Adds a Text Product Option with the provided Char Limits. Clicks on Save. Validates that the Product details are present and correct.</description> + </annotations> + <arguments> + <argument name="category"/> + <argument name="simpleProduct"/> + <argument name="charLimit"/> + </arguments> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> + <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> + <fillField userInput="{{simpleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> + <fillField userInput="{{simpleProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> + <fillField userInput="{{simpleProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> + <fillField userInput="{{simpleProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{category.name}}]" stepKey="searchAndSelectCategory"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + + <click selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" stepKey="openCustomOptionsSection"/> + <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOption"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.optionTitleInput('0')}}" userInput="option1" stepKey="fillOptionTitle"/> + <click selector="{{AdminProductCustomizableOptionsSection.optionTypeOpenDropDown}}" stepKey="openTypeDropDown"/> + <click selector="{{AdminProductCustomizableOptionsSection.optionTypeTextField}}" stepKey="selectTypeTextField"/> + <fillField userInput="20" selector="{{AdminProductCustomizableOptionsSection.maxCharactersInput}}" stepKey="fillMaxChars"/> + + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertSaveMessageSuccess"/> + <seeInField userInput="{{simpleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="assertFieldName"/> + <seeInField userInput="{{simpleProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="assertFieldSku"/> + <seeInField userInput="{{simpleProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="assertFieldPrice"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSectionAssert"/> + <seeInField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="assertFieldUrlKey"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml deleted file mode 100644 index 45e2ed6205b20..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminCreateRecentlyProductsWidgetActionGroup" extends="AdminCreateWidgetActionGroup"> - <annotations> - <description>EXTENDS: AdminCreateWidgetActionGroup. Adds Product Attributes/Buttons to a Widget. Clicks on the Save button.</description> - </annotations> - - <selectOption selector="{{AdminCatalogProductWidgetSection.productAttributesToShow}}" parameterArray="['Name', 'Image', 'Price']" stepKey="selectAllProductAttributes"/> - <selectOption selector="{{AdminCatalogProductWidgetSection.productButtonsToShow}}" parameterArray="['Add to Cart', 'Add to Compare', 'Add to Wishlist']" stepKey="selectAllProductButtons"/> - <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveWidget"/> - <waitForElementVisible selector="{{AdminMessagesSection.successMessage}}" stepKey="waitForSuccessMessageAppears"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteAllProductCustomOptionsActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteAllProductCustomOptionsActionGroup.xml new file mode 100644 index 0000000000000..103c25b2c6f50 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteAllProductCustomOptionsActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminDeleteAllProductCustomOptionsActionGroup"> + <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="expandContentTab"/> + <waitForPageLoad time="10" stepKey="waitCustomizableOptionsTabOpened"/> + <executeInSelenium function="function($webdriver) use ($I) { + $buttons = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::cssSelector('[data-index=\'options\'] [data-index=\'delete_button\']')); + while(!empty($buttons)) { + $button = reset($buttons); + $I->executeJS('arguments[0].scrollIntoView(false)', [$button]); + $button->click(); + $webdriver->wait()->until(\Facebook\WebDriver\WebDriverExpectedCondition::stalenessOf($button)); + $buttons = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::cssSelector('[data-index=\'options\'] [data-index=\'delete_button\']')); + } + }" stepKey="deleteCustomOptions"/> + <dontSeeElement selector="{{AdminProductCustomizableOptionsSection.customOptionButtonDelete}}" stepKey="assertNoCustomOptions"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteCategoryByNameActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteCategoryByNameActionGroup.xml new file mode 100644 index 0000000000000..de7f569074ac3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteCategoryByNameActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminDeleteCategoryByNameActionGroup" extends="DeleteCategoryActionGroup"> + <arguments> + <argument name="categoryName" type="string" defaultValue="category1"/> + </arguments> + <remove keyForRemoval="clickCategoryLink"/> + <remove keyForRemoval="dontSeeCategoryInTree"/> + <remove keyForRemoval="expandToSeeAllCategories"/> + <conditionalClick selector="{{AdminCategorySidebarTreeSection.expandAll}}" dependentSelector="{{AdminCategorySidebarTreeSection.categoryByName(categoryName)}}" visible="false" stepKey="expandCategories" after="waitForCategoryPageLoad"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryByName(categoryName)}}" stepKey="clickCategory" after="expandCategories"/> + <conditionalClick selector="{{AdminCategorySidebarTreeSection.expandAll}}" dependentSelector="{{AdminCategorySidebarTreeSection.categoryByName(categoryName)}}" visible="false" stepKey="expandCategoriesToSeeAll" after="seeDeleteSuccess"/> + <dontSee selector="{{AdminCategorySidebarTreeSection.categoryByName(categoryName)}}" stepKey="dontSeeCategory" after="expandCategoriesToSeeAll"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteProductCustomOptionActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteProductCustomOptionActionGroup.xml new file mode 100644 index 0000000000000..22c4ef6a7d568 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteProductCustomOptionActionGroup.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="AdminDeleteProductCustomOptionActionGroup" extends="AdminAssertProductCustomOptionVisibleActionGroup"> + <remove keyForRemoval="assertCustomOptionVisible"/> + <click selector="{{AdminProductCustomizableOptionsSection.deleteCustomOptions(option.title)}}" after="waitCustomizableOptionsTabOpened" stepKey="clickDeleteCustomOption"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminFillProductAttributePropertiesActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminFillProductAttributePropertiesActionGroup.xml index ec73001976dc6..cd850f8a7004d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminFillProductAttributePropertiesActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminFillProductAttributePropertiesActionGroup.xml @@ -15,16 +15,4 @@ <fillField selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{attributeName}}" stepKey="fillDefaultLabel"/> <selectOption selector="{{AttributePropertiesSection.InputType}}" userInput="{{attributeType}}" stepKey="selectInputType"/> </actionGroup> - - <!--You are on AdminProductEditPage--> - <!-- Switch scope for product attribute--> - <!-- !Note! Scope : 0 - Store View; 1 - Global; 2 - Website; --> - <actionGroup name="AdminSwitchScopeForProductAttributeActionGroup"> - <arguments> - <argument name="scope" type="string" defaultValue="1"/> - </arguments> - <click selector="{{AttributePropertiesSection.AdvancedProperties}}" stepKey="expandAdvancedProperties"/> - <waitForElementVisible selector="{{AttributePropertiesSection.Scope}}" stepKey="waitOpenAdvancedProperties"/> - <selectOption selector="{{AttributePropertiesSection.Scope}}" userInput="1" stepKey="selectNecessaryScope"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminFillProductCountryOfManufactureActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminFillProductCountryOfManufactureActionGroup.xml new file mode 100644 index 0000000000000..7642bb5f6635d --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminFillProductCountryOfManufactureActionGroup.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="AdminFillProductCountryOfManufactureActionGroup"> + <arguments> + <argument name="countryId" type="string" defaultValue="US"/> + </arguments> + <selectOption selector="{{AdminProductFormBundleSection.countryOfManufactureDropDown}}" userInput="{{countryId}}" stepKey="countryOfManufactureDropDown"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminNavigateToProductAttributeAdvancedSectionActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminNavigateToProductAttributeAdvancedSectionActionGroup.xml new file mode 100644 index 0000000000000..27aa0474c362d --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminNavigateToProductAttributeAdvancedSectionActionGroup.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="AdminNavigateToProductAttributeAdvancedSectionActionGroup"> + <annotations> + <description>Navigate and open Advanced Attribute Properties section on product attribute page</description> + </annotations> + + <scrollTo selector="{{AdvancedAttributePropertiesSection.AdvancedAttributePropertiesSectionToggle}}" stepKey="scrollToSection"/> + <conditionalClick selector="{{AdvancedAttributePropertiesSection.AdvancedAttributePropertiesSectionToggle}}" dependentSelector="{{AdvancedAttributePropertiesSection.AttributeCode}}" visible="false" stepKey="openSection"/> + <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" stepKey="waitForSlideOutSection"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProcessProductWebsitesActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProcessProductWebsitesActionGroup.xml new file mode 100644 index 0000000000000..fbd49224231f3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProcessProductWebsitesActionGroup.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="AdminProcessProductWebsitesActionGroup" extends="CreatedProductConnectToWebsiteActionGroup"> + <arguments> + <argument name="websiteToUnassign"/> + </arguments> + <uncheckOption selector="{{ProductInWebsitesSection.website(websiteToUnassign.name)}}" after="SelectWebsite" stepKey="uncheckWebsite"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml deleted file mode 100644 index 428b3828901cd..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ /dev/null @@ -1,759 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Navigate to create product page from product grid page--> - <actionGroup name="goToCreateProductPage"> - <annotations> - <description>Clicks on the 'Add Product' toggle on the Admin Products grid page. Clicks on the provided Product (Type).</description> - </annotations> - <arguments> - <argument name="product" defaultValue="_defaultProduct"/> - </arguments> - - <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductToggle"/> - <waitForElementVisible selector="{{AdminProductGridActionSection.addTypeProduct(product.type_id)}}" stepKey="waitForAddProductDropdown" time="30"/> - <click selector="{{AdminProductGridActionSection.addTypeProduct(product.type_id)}}" stepKey="clickAddProductType"/> - <waitForPageLoad time="30" stepKey="waitForCreateProductPageLoad"/> - <seeInCurrentUrl url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, product.type_id)}}" stepKey="seeNewProductUrl"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Product" stepKey="seeNewProductTitle"/> - </actionGroup> - - <!--Navigate to create product page directly via ID--> - <actionGroup name="goToProductPageViaID"> - <annotations> - <description>Goes to the Product edit page for the provided Product ID.</description> - </annotations> - <arguments> - <argument name="productId" type="string"/> - </arguments> - - <amOnPage url="{{AdminProductEditPage.url(productId)}}" stepKey="goToProduct"/> - </actionGroup> - - <!-- Fill main fields in create product form using a product entity --> - <actionGroup name="fillMainProductForm"> - <annotations> - <description>Fills in the provided Product details (Name, SKU, Price, Quantity, Stock Status, Weight Type and Weight) on the Admin Products creation/edit page.</description> - </annotations> - <arguments> - <argument name="product" defaultValue="_defaultProduct"/> - </arguments> - - <scrollToTopOfPage stepKey="scrollToTopOfPage"/> - <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{product.name}}" stepKey="fillProductName"/> - <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{product.sku}}" stepKey="fillProductSku"/> - <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{product.price}}" stepKey="fillProductPrice"/> - <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{product.quantity}}" stepKey="fillProductQty"/> - <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{product.status}}" stepKey="selectStockStatus"/> - <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeight"/> - <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="{{product.weight}}" stepKey="fillProductWeight"/> - </actionGroup> - - <!-- Fill main fields in create product form using strings for flexibility --> - <actionGroup name="FillMainProductFormByString"> - <annotations> - <description>Fills in the provided Product Name, SKU, Price, Quantity, Stock Status and Weight on the Admin Products creation/edit page.</description> - </annotations> - <arguments> - <argument name="productName" type="string"/> - <argument name="productSku" type="string"/> - <argument name="productPrice" type="string"/> - <argument name="productQuantity" type="string"/> - <argument name="productStatus" type="string"/> - <argument name="productWeight" type="string"/> - </arguments> - - <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{productName}}" stepKey="fillProductName"/> - <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{productSku}}" stepKey="fillProductSku"/> - <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{productPrice}}" stepKey="fillProductPrice"/> - <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{productQuantity}}" stepKey="fillProductQty"/> - <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{productStatus}}" stepKey="selectStockStatus"/> - <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeight"/> - <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="{{productWeight}}" stepKey="fillProductWeight"/> - </actionGroup> - - <!--Fill main fields in create product form with no weight, useful for virtual and downloadable products --> - <actionGroup name="fillMainProductFormNoWeight"> - <annotations> - <description>Fills in the provided Product details (Name, SKU, Price, Quantity, Stock Status and Weight Type) on the Admin Products creation/edit page.</description> - </annotations> - <arguments> - <argument name="product" defaultValue="DownloadableProduct"/> - </arguments> - - <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{product.name}}" stepKey="fillProductName"/> - <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{product.sku}}" stepKey="fillProductSku"/> - <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{product.price}}" stepKey="fillProductPrice"/> - <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{product.quantity}}" stepKey="fillProductQty"/> - <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{product.status}}" stepKey="selectStockStatus"/> - <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has no weight" stepKey="selectWeight"/> - </actionGroup> - - <!--Fill main fields in create product form with name and sku --> - <actionGroup name="fillProductNameAndSkuInProductForm"> - <annotations> - <description>Fills in the provided Product details (Name and SKU) on the Admin Products creation and edit page.</description> - </annotations> - <arguments> - <argument name="product"/> - </arguments> - - <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{product.name}}" stepKey="fillProductName"/> - <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{product.sku}}" stepKey="fillProductSku"/> - </actionGroup> - <actionGroup name="AdminFillProductCountryOfManufactureActionGroup"> - <arguments> - <argument name="countryId" type="string" defaultValue="US"/> - </arguments> - <selectOption selector="{{AdminProductFormBundleSection.countryOfManufactureDropDown}}" userInput="{{countryId}}" stepKey="countryOfManufactureDropDown"/> - </actionGroup> - - <!--Check that required fields are actually required--> - <actionGroup name="checkRequiredFieldsInProductForm"> - <annotations> - <description>Validates that the 'Required Field' error message is present and correct for the Product Name, SKU and Price fields.</description> - </annotations> - - <clearField selector="{{AdminProductFormSection.productName}}" stepKey="clearProductName"/> - <clearField selector="{{AdminProductFormSection.productSku}}" stepKey="clearProductSku"/> - <clearField selector="{{AdminProductFormSection.productPrice}}" stepKey="clearProductPrice"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Product" stepKey="seeStillOnEditPage"/> - <see selector="{{AdminProductFormSection.fieldError('name')}}" userInput="This is a required field." stepKey="seeNameRequired"/> - <see selector="{{AdminProductFormSection.fieldError('sku')}}" userInput="This is a required field." stepKey="seeSkuRequired"/> - <see selector="{{AdminProductFormSection.priceFieldError}}" userInput="This is a required field." stepKey="seePriceRequired"/> - </actionGroup> - - <!--Save product and see success message--> - <actionGroup name="saveProductForm"> - <annotations> - <description>Clicks on the Save button. Validates that the Success Message is present and correct.</description> - </annotations> - - <scrollToTopOfPage stepKey="scrollTopPageProduct"/> - <waitForElementVisible selector="{{AdminProductFormActionSection.saveButton}}" stepKey="waitForSaveProductButton"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> - <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitProductSaveSuccessMessage"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You saved the product." stepKey="seeSaveConfirmation"/> - </actionGroup> - - <actionGroup name="toggleProductEnabled"> - <annotations> - <description>Clicks on the Enable Product toggle.</description> - </annotations> - - <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="toggleEnabled"/> - </actionGroup> - - <!-- Save product but do not expect a success message --> - <actionGroup name="SaveProductFormNoSuccessCheck" extends="saveProductForm"> - <annotations> - <description>EXTENDS: saveProductForm. Removes 'waitProductSaveSuccessMessage' and 'seeSaveConfirmation'.</description> - </annotations> - - <remove keyForRemoval="waitProductSaveSuccessMessage"/> - <remove keyForRemoval="seeSaveConfirmation"/> - </actionGroup> - - <!--Upload image for product--> - <actionGroup name="addProductImage"> - <annotations> - <description>Adds the provided Product Image on the Admin Products creation/edit page.</description> - </annotations> - <arguments> - <argument name="image" defaultValue="ProductImage"/> - </arguments> - - <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductImagesSection"/> - <waitForPageLoad time="30" stepKey="waitForPageRefresh"/> - <waitForElementVisible selector="{{AdminProductImagesSection.imageUploadButton}}" stepKey="seeImageSectionIsReady"/> - <attachFile selector="{{AdminProductImagesSection.imageFileUpload}}" userInput="{{image.file}}" stepKey="uploadFile"/> - <waitForElementNotVisible selector="{{AdminProductImagesSection.uploadProgressBar}}" stepKey="waitForUpload"/> - <waitForElementVisible selector="{{AdminProductImagesSection.imageFile(image.fileName)}}" stepKey="waitForThumbnail"/> - </actionGroup> - - <!--Remove image for product--> - <actionGroup name="removeProductImage"> - <annotations> - <description>Removes a Product Image on the Admin Products creation/edit page.</description> - </annotations> - - <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductImagesSection"/> - <waitForPageLoad time="30" stepKey="waitForPageRefresh"/> - <click selector="{{AdminProductImagesSection.removeImageButton}}" stepKey="clickRemoveImage"/> - </actionGroup> - - <!--Remove Product image by name--> - <actionGroup name="RemoveProductImageByName" extends="removeProductImage"> - <annotations> - <description>Removes a Product Image on the Admin Products creation/edit page by name.</description> - </annotations> - - <arguments> - <argument name="image" defaultValue="ProductImage"/> - </arguments> - <click selector="{{AdminProductImagesSection.removeImageButtonForExactImage(image.fileName)}}" stepKey="clickRemoveImage"/> - </actionGroup> - - <!-- Assert product image in Admin Product page --> - <actionGroup name="assertProductImageAdminProductPage"> - <annotations> - <description>Validates that the provided Product Image is present and correct.</description> - </annotations> - <arguments> - <argument name="image" defaultValue="MagentoLogo"/> - </arguments> - - <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductImagesSection"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <seeElement selector="{{AdminProductImagesSection.imageFile(image.filename)}}" stepKey="seeImage"/> - </actionGroup> - - <!-- Assert no product image in Admin Product page --> - <actionGroup name="assertProductImageNotInAdminProductPage"> - <annotations> - <description>Validates that the provided Product Image is NOT present.</description> - </annotations> - <arguments> - <argument name="image" defaultValue="MagentoLogo"/> - </arguments> - - <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductImagesSection"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <dontSeeElement selector="{{AdminProductImagesSection.imageFile(image.filename)}}" stepKey="seeImage"/> - </actionGroup> - - <!--Fill fields for simple product in a category in Admin--> - <actionGroup name="FillAdminSimpleProductForm"> - <annotations> - <description>Goes to the Admin Product grid page. Clicks on Add. Fills the provided Product details (Name, SKU, Price, Quantity, Category and URL). Clicks on Save. Validates that the Product details are present and correct.</description> - </annotations> - <arguments> - <argument name="category"/> - <argument name="simpleProduct"/> - </arguments> - - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> - <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> - <fillField userInput="{{simpleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> - <fillField userInput="{{simpleProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> - <fillField userInput="{{simpleProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> - <fillField userInput="{{simpleProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> - <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{category.name}}]" stepKey="searchAndSelectCategory"/> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> - <fillField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertSaveMessageSuccess"/> - <seeInField userInput="{{simpleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="assertFieldName"/> - <seeInField userInput="{{simpleProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="assertFieldSku"/> - <seeInField userInput="{{simpleProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="assertFieldPrice"/> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSectionAssert"/> - <seeInField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="assertFieldUrlKey"/> - </actionGroup> - - <!--Fill fields for simple product in a category in Admin, including text option with char limit--> - <actionGroup name="AdminCreateSimpleProductWithTextOptionCharLimit"> - <annotations> - <description>Goes to the Admin Product grid page. Clicks on Add. Fills the provided Product details (Name, SKU, Price, Quantity, Category and URL). Adds a Text Product Option with the provided Char Limits. Clicks on Save. Validates that the Product details are present and correct.</description> - </annotations> - <arguments> - <argument name="category"/> - <argument name="simpleProduct"/> - <argument name="charLimit"/> - </arguments> - - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> - <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> - <fillField userInput="{{simpleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> - <fillField userInput="{{simpleProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> - <fillField userInput="{{simpleProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> - <fillField userInput="{{simpleProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> - <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{category.name}}]" stepKey="searchAndSelectCategory"/> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> - <fillField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> - - <click selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" stepKey="openCustomOptionsSection"/> - <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOption"/> - <fillField selector="{{AdminProductCustomizableOptionsSection.optionTitleInput('0')}}" userInput="option1" stepKey="fillOptionTitle"/> - <click selector="{{AdminProductCustomizableOptionsSection.optionTypeOpenDropDown}}" stepKey="openTypeDropDown"/> - <click selector="{{AdminProductCustomizableOptionsSection.optionTypeTextField}}" stepKey="selectTypeTextField"/> - <fillField userInput="20" selector="{{AdminProductCustomizableOptionsSection.maxCharactersInput}}" stepKey="fillMaxChars"/> - - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertSaveMessageSuccess"/> - <seeInField userInput="{{simpleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="assertFieldName"/> - <seeInField userInput="{{simpleProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="assertFieldSku"/> - <seeInField userInput="{{simpleProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="assertFieldPrice"/> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSectionAssert"/> - <seeInField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="assertFieldUrlKey"/> - </actionGroup> - - <actionGroup name="ProductSetWebsite"> - <annotations> - <description>Sets the provided Website on the Admin Product creation/edit page.</description> - </annotations> - <arguments> - <argument name="website" type="string"/> - </arguments> - - <scrollTo selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="scrollToWebsites"/> - <conditionalClick selector="{{ProductInWebsitesSection.sectionHeader}}" dependentSelector="{{ProductInWebsitesSection.website(website)}}" visible="false" stepKey="clickToOpenProductInWebsite"/> - <waitForPageLoad stepKey="waitForPageOpened"/> - <click selector="{{ProductInWebsitesSection.website(website)}}" stepKey="selectWebsite"/> - <click selector="{{AdminProductFormAdvancedPricingSection.save}}" stepKey="clickSaveProduct"/> - <waitForPageLoad time='60' stepKey="waitForProducrSaved"/> - <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSaveSuccessMessage"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You saved the product." stepKey="seeSaveSuccessMessage"/> - </actionGroup> - - <actionGroup name="ProductSetAdvancedPricing"> - <annotations> - <description>Sets the provided Advanced Pricing on the Admin Product creation/edit page.</description> - </annotations> - <arguments> - <argument name="website" type="string" defaultValue=""/> - <argument name="group" type="string" defaultValue="Retailer"/> - <argument name="quantity" type="string" defaultValue="1"/> - <argument name="price" type="string" defaultValue="Discount"/> - <argument name="amount" type="string" defaultValue="45"/> - </arguments> - - <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton"/> - <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForCustomerGroupPriceAddButton"/> - <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="addCustomerGroupAllGroupsQty1PriceDiscountAnd10percent"/> - <waitForElement selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" stepKey="waitForSelectCustomerGroupNameAttribute2"/> - <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceWebsiteSelect('0')}}" userInput="{{website}}" stepKey="selectProductWebsiteValue"/> - <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" userInput="{{group}}" stepKey="selectProductCustomGroupValue"/> - <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" userInput="{{quantity}}" stepKey="fillProductTierPriceQtyInput"/> - <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceValueTypeSelect('0')}}" userInput="{{price}}" stepKey="selectProductTierPriceValueType"/> - <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPricePercentageValuePriceInput('0')}}" userInput="{{amount}}" stepKey="selectProductTierPricePriceInput"/> - <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton"/> - <waitForPageLoad stepKey="WaitForProductSave"/> - <click selector="{{AdminProductFormAdvancedPricingSection.save}}" stepKey="clickSaveProduct1"/> - <waitForPageLoad time="60" stepKey="WaitForProductSave1"/> - <see userInput="You saved the product." stepKey="seeSaveConfirmation"/> - </actionGroup> - <actionGroup name="ProductSetAdvancedTierFixedPricing" extends="ProductSetAdvancedPricing"> - <remove keyForRemoval="selectProductTierPricePriceInput"/> - <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceFixedPriceInput('0')}}" userInput="{{amount}}" stepKey="selectProductTierPricePriceInput" after="selectProductTierPriceValueType"/> - </actionGroup> - - <!--Assert text in Related, Up-Sell or Cross-Sell section in Admin Product page--> - <actionGroup name="AssertTextInAdminProductRelatedUpSellCrossSellSection"> - <annotations> - <description>Validates that provided Text appears in the provided Element on the Admin Product creation/edit page under 'Related Products, Up-Sells, and Cross-Sells' section.</description> - </annotations> - <arguments> - <argument name="element" defaultValue="AdminProductFormRelatedUpSellCrossSellSection.relatedProductSectionText"/> - <argument name="expectedText"/> - </arguments> - - <conditionalClick selector="{{AdminProductFormSection.productFormTab('Related Products')}}" dependentSelector="{{AdminProductFormSection.productFormTabState('Related Products', 'closed')}}" visible="true" stepKey="openTab"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad"/> - <see selector="{{element}}" userInput="{{expectedText}}" stepKey="assertText"/> - </actionGroup> - - <!--Related products--> - <actionGroup name="addRelatedProductBySku"> - <annotations> - <description>Adds the provided Product SKU as a Related Product on the Admin Product creation/edit page.</description> - </annotations> - <arguments> - <argument name="sku"/> - </arguments> - - <!--Scroll up to avoid error--> - <scrollTo selector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDropdown}}" x="0" y="-100" stepKey="scrollTo"/> - <conditionalClick selector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDropdown}}" dependentSelector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDependent}}" visible="false" stepKey="openDropDownIfClosedRelatedUpSellCrossSell"/> - <click selector="{{AdminProductFormRelatedUpSellCrossSellSection.AddRelatedProductsButton}}" stepKey="clickAddRelatedProductButton"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{sku}}" stepKey="fillProductSkuFilter"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> - <click selector="{{AdminProductModalSlideGridSection.productGridXRowYColumnButton('1', '1')}}" stepKey="selectProduct"/> - <click selector="{{AdminAddRelatedProductsModalSection.AddSelectedProductsButton}}" stepKey="addRelatedProductSelected"/> - <waitForElementNotVisible selector="{{AdminAddRelatedProductsModalSection.AddSelectedProductsButton}}" stepKey="waitForElementNotVisible"/> - </actionGroup> - - <!--Click AddCrossSellProducts and adds product by SKU--> - <actionGroup name="addCrossSellProductBySku"> - <annotations> - <description>Adds the provided Product SKU as a Cross Sell Product on the Admin Product creation/edit page.</description> - </annotations> - <arguments> - <argument name="sku"/> - </arguments> - - <!--Scroll up to avoid error--> - <scrollTo selector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDropdown}}" x="0" y="-100" stepKey="scrollTo"/> - <conditionalClick selector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDropdown}}" dependentSelector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDependent}}" visible="false" stepKey="openDropDownIfClosedRelatedUpSellCrossSell"/> - <click selector="{{AdminProductFormRelatedUpSellCrossSellSection.AddCrossSellProductsButton}}" stepKey="clickAddCrossSellButton"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{sku}}" stepKey="fillProductSkuFilter"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> - <click selector="{{AdminProductModalSlideGridSection.productGridXRowYColumnButton('1', '1')}}" stepKey="selectProduct"/> - <click selector="{{AdminProductCrossSellModalSection.addSelectedProducts}}" stepKey="addRelatedProductSelected"/> - <waitForPageLoad stepKey="waitForModalDisappear"/> - </actionGroup> - - <!--Add special price to product in Admin product page--> - <actionGroup name="AddSpecialPriceToProductActionGroup"> - <annotations> - <description>Sets the provided Special Price on the Admin Product creation/edit page.</description> - </annotations> - <arguments> - <argument name="price" type="string" defaultValue="8"/> - </arguments> - - <waitForPageLoad stepKey="waitForPageLoad"/> - <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink"/> - <waitForPageLoad stepKey="waitForAdvancedPricingModal"/> - <waitForElementVisible selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="waitSpecialPrice"/> - <fillField userInput="{{price}}" selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="fillSpecialPrice"/> - <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDone"/> - <waitForPageLoad stepKey="waitForAdvancedPricingModalGone"/> - <waitForElementNotVisible selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="waitForCloseModalWindow"/> - </actionGroup> - - <!--Select Product In Websites--> - <actionGroup name="SelectProductInWebsitesActionGroup"> - <annotations> - <description>Sets the provided Website on the Admin Product creation/edit page.</description> - </annotations> - <arguments> - <argument name="website" type="string"/> - </arguments> - - <scrollTo selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="scrollToWebsites"/> - <conditionalClick selector="{{ProductInWebsitesSection.sectionHeader}}" dependentSelector="{{AdminProductContentSection.sectionHeaderShow}}" visible="false" stepKey="expandSection"/> - <waitForPageLoad stepKey="waitForPageOpened"/> - <checkOption selector="{{ProductInWebsitesSection.website(website)}}" stepKey="selectWebsite"/> - </actionGroup> - <actionGroup name="unassignWebsiteFromProductActionGroup" extends="SelectProductInWebsitesActionGroup"> - <remove keyForRemoval="selectWebsite"/> - <uncheckOption selector="{{ProductInWebsitesSection.website(website)}}" stepKey="unSelectWebsite" after="waitForPageOpened"/> - </actionGroup> - - <actionGroup name="AdminProductAddSpecialPrice"> - <annotations> - <description>Sets the provided Special Price on the Admin Product creation/edit page. Clicks on Save.</description> - </annotations> - <arguments> - <argument name="specialPrice" type="string"/> - </arguments> - - <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink1"/> - <waitForElementVisible selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="waitSpecialPrice1"/> - <click selector="{{AdminProductFormAdvancedPricingSection.useDefaultPrice}}" stepKey="checkUseDefault"/> - <fillField userInput="{{specialPrice}}" selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="fillSpecialPrice"/> - <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDone"/> - <waitForElementNotVisible selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="waitForCloseModalWindow"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> - </actionGroup> - - <!--Switch to New Store view--> - <actionGroup name="SwitchToTheNewStoreView"> - <annotations> - <description>Switches the New Store View.</description> - </annotations> - <arguments> - <argument name="storeViewName" type="string"/> - </arguments> - - <scrollTo selector="{{AdminProductContentSection.pageHeader}}" stepKey="scrollToUp"/> - <waitForElementVisible selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="waitForElementBecomeVisible"/> - <click selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="clickStoreviewSwitcher"/> - <click selector="{{AdminProductFormActionSection.selectStoreView(storeViewName)}}" stepKey="chooseStoreView"/> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="acceptStoreSwitchingMessage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - </actionGroup> - - <!--Create a Simple Product--> - <actionGroup name="createSimpleProductAndAddToWebsite"> - <annotations> - <description>Goes to the Admin Product grid page. Clicks on Add Product. Fills the provided Product Details (Name, SKU, Price, Quantity and Website). Clicks on Save.</description> - </annotations> - <arguments> - <argument name="product"/> - <argument name="website" type="string"/> - </arguments> - - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToCatalogProductGrid"/> - <waitForPageLoad stepKey="waitForProductGrid"/> - <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> - <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> - <fillField userInput="{{product.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillProductName"/> - <fillField userInput="{{product.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillProductSKU"/> - <fillField userInput="{{product.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillProductPrice"/> - <fillField userInput="{{product.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillProductQuantity"/> - <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="openProductInWebsites"/> - <click selector="{{ProductInWebsitesSection.website(website)}}" stepKey="selectWebsite"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> - <waitForLoadingMaskToDisappear stepKey="waitForProductPageSave"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> - </actionGroup> - - <actionGroup name="CreatedProductConnectToWebsite"> - <annotations> - <description>Clicks on 'Edit' for the provided Product. Clicks on the provided Website. Clicks on Save.</description> - </annotations> - <arguments> - <argument name="website"/> - <argument name="product"/> - </arguments> - - <click stepKey="openProduct" selector="{{AdminProductGridActionSection.productName(product.sku)}}"/> - <waitForPageLoad stepKey="waitForProductPage"/> - <scrollTo selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="ScrollToWebsites"/> - <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="openWebsitesList"/> - <waitForPageLoad stepKey="waitForWebsitesList"/> - <click selector="{{ProductInWebsitesSection.website(website.name)}}" stepKey="SelectWebsite"/> - <click selector="{{AdminProductFormAdvancedPricingSection.save}}" stepKey="clickSaveProduct"/> - <waitForPageLoad stepKey="waitForSave"/> - </actionGroup> - - <!-- Action group assign to one website and unassign from another --> - <actionGroup name="AdminProcessProductWebsitesActionGroup" extends="CreatedProductConnectToWebsite"> - <arguments> - <argument name="websiteToUnassign"/> - </arguments> - <uncheckOption selector="{{ProductInWebsitesSection.website(websiteToUnassign.name)}}" after="SelectWebsite" stepKey="uncheckWebsite"/> - </actionGroup> - - <!--Check tier price with a discount percentage on product--> - <actionGroup name="AssertDiscountsPercentageOfProducts"> - <annotations> - <description>Validates that the provided Product Tier Price is present and correct.</description> - </annotations> - <arguments> - <argument name="amount" type="string" defaultValue="45"/> - </arguments> - - <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton"/> - <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForCustomerGroupPriceAddButton"/> - <grabValueFrom selector="{{AdminProductFormAdvancedPricingSection.productTierPricePercentageValuePriceInput('0')}}" stepKey="grabProductTierPriceInput"/> - <assertEquals stepKey="assertProductTierPriceInput"> - <expectedResult type="string">{{amount}}</expectedResult> - <actualResult type="string">$grabProductTierPriceInput</actualResult> - </assertEquals> - </actionGroup> - - <!-- This action group goes to the product index page, opens the drop down and clicks the specified product type for adding a product --> - <actionGroup name="GoToSpecifiedCreateProductPage"> - <annotations> - <description>Goes to the Admin Product grid page. Clicks on the Add Product toggle. Clicks on the provided Product Type.</description> - </annotations> - <arguments> - <argument type="string" name="productType" defaultValue="simple"/> - </arguments> - - <comment userInput="actionGroup:GoToSpecifiedCreateProductPage" stepKey="actionGroupComment"/> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> - <click selector="{{AdminProductGridActionSection.addTypeProduct(productType)}}" stepKey="clickAddProduct"/> - <waitForPageLoad stepKey="waitForFormToLoad"/> - </actionGroup> - - <!-- This action group simply navigates to the product catalog page --> - <actionGroup name="GoToProductCatalogPage"> - <annotations> - <description>Goes to the Admin Products grid page.</description> - </annotations> - - <comment userInput="actionGroup:GoToProductCatalogPage" stepKey="actionGroupComment"/> - <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="GoToCatalogProductPage"/> - <waitForPageLoad stepKey="WaitForPageToLoad"/> - </actionGroup> - - <actionGroup name="SetProductUrlKey"> - <annotations> - <description>Fills the Product details (URL) for the SEO section.</description> - </annotations> - <arguments> - <argument name="product" defaultValue="_defaultProduct"/> - </arguments> - - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> - <fillField userInput="{{product.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> - </actionGroup> - - <actionGroup name="SetProductUrlKeyByString"> - <annotations> - <description>Fills the Product SEO URL Key.</description> - </annotations> - <arguments> - <argument name="urlKey" type="string"/> - </arguments> - - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> - <fillField userInput="{{urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> - </actionGroup> - - <actionGroup name="SetCategoryByName"> - <annotations> - <description>Sets the provided Category Name for a Product on the Product creation/edit page.</description> - </annotations> - <arguments> - <argument name="categoryName" type="string"/> - </arguments> - - <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{categoryName}}]" stepKey="searchAndSelectCategory"/> - </actionGroup> - <!--Remove category from product in ProductFrom Page--> - <actionGroup name="removeCategoryFromProduct"> - <arguments> - <argument name="categoryName" type="string" defaultValue="{{_defaultCategory.name}}"/> - </arguments> - <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDown"/> - <click selector="{{AdminProductFormSection.unselectCategories(categoryName)}}" stepKey="unselectCategories"/> - <click selector="{{AdminProductFormSection.done}}" stepKey="clickOnDoneAdvancedCategory"/> - </actionGroup> - - <actionGroup name="expandAdminProductSection"> - <annotations> - <description>Expand the provided Section Selector based on the provided dependant Section Selector.</description> - </annotations> - <arguments> - <argument name="sectionSelector" defaultValue="{{AdminProductContentSection.sectionHeader}}" type="string"/> - <argument name="sectionDependentSelector" defaultValue="{{AdminProductContentSection.sectionHeaderShow}}" type="string"/> - </arguments> - - <scrollToTopOfPage stepKey="scrollToTopOfPage"/> - <waitForElementVisible time="30" selector="{{sectionSelector}}" stepKey="waitForSection"/> - <conditionalClick selector="{{sectionSelector}}" dependentSelector="{{sectionDependentSelector}}" visible="false" stepKey="expandSection"/> - <waitForPageLoad time="30" stepKey="waitForSectionToExpand"/> - </actionGroup> - - <actionGroup name="navigateToCreatedProductEditPage"> - <annotations> - <description>Goes to the Admin Product grid page. Filters the Product grid based on the provided Product details (SKU). Edits the provided Product. Validates that the Product SKU is present and correct.</description> - </annotations> - <arguments> - <argument name="product" defaultValue="_defaultProduct"/> - </arguments> - - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToAdminProductIndexPage"/> - <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> - <waitForPageLoad stepKey="waitForClearFilters"/> - <dontSeeElement selector="{{AdminProductGridFilterSection.clearFilters}}" stepKey="dontSeeClearFilters"/> - <click selector="{{AdminProductGridFilterSection.viewDropdown}}" stepKey="openViewBookmarksTab"/> - <click selector="{{AdminProductGridFilterSection.viewBookmark('Default View')}}" stepKey="resetToDefaultGridView"/> - <waitForPageLoad stepKey="waitForResetToDefaultView"/> - <see selector="{{AdminProductGridFilterSection.viewDropdown}}" userInput="Default View" stepKey="seeDefaultViewSelected"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillProductSkuFilter"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <waitForPageLoad stepKey="waitForFilterOnGrid"/> - <click selector="{{AdminProductGridSection.selectRowBasedOnName(product.name)}}" stepKey="clickProduct"/> - <waitForPageLoad stepKey="waitForProductEditPageLoad"/> - <waitForElementVisible selector="{{AdminProductFormBundleSection.productSku}}" stepKey="waitForProductSKUField"/> - <seeInField selector="{{AdminProductFormBundleSection.productSku}}" userInput="{{product.sku}}" stepKey="seeProductSKU"/> - </actionGroup> - - <actionGroup name="addUpSellProductBySku" extends="addRelatedProductBySku"> - <annotations> - <description>EXTENDS: addRelatedProductBySku. Add the provided Product as an Up Sell Product.</description> - </annotations> - - <click selector="{{AdminProductFormRelatedUpSellCrossSellSection.AddUpSellProductsButton}}" stepKey="clickAddRelatedProductButton"/> - <conditionalClick selector="{{AdminAddUpSellProductsModalSection.Modal}} {{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminAddUpSellProductsModalSection.Modal}} {{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> - <click selector="{{AdminAddUpSellProductsModalSection.Modal}} {{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> - <fillField selector="{{AdminAddUpSellProductsModalSection.Modal}} {{AdminProductGridFilterSection.skuFilter}}" userInput="{{sku}}" stepKey="fillProductSkuFilter"/> - <click selector="{{AdminAddUpSellProductsModalSection.Modal}} {{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> - <click selector="{{AdminAddUpSellProductsModalSection.Modal}}{{AdminProductModalSlideGridSection.productGridXRowYColumnButton('1', '1')}}" stepKey="selectProduct"/> - <click selector="{{AdminAddUpSellProductsModalSection.AddSelectedProductsButton}}" stepKey="addRelatedProductSelected"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> - </actionGroup> - <actionGroup name="adminProductAdvancedPricingNewCustomerGroupPrice"> - <arguments> - <argument name="qty" type="string" defaultValue="2"/> - <argument name="priceType" type="string" defaultValue="Discount"/> - <argument name="discount" type="string" defaultValue="75"/> - <argument name="customerGroup" type="string" defaultValue="0"/> - </arguments> - <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="addCustomerGroup"/> - <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput(customerGroup)}}" userInput="{{qty}}" stepKey="fillProductTierPriceQtyInput1"/> - <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceValueTypeSelect(customerGroup)}}" userInput="{{priceType}}" stepKey="selectProductTierPriceValueType1"/> - <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPricePercentageValuePriceInput(customerGroup)}}" userInput="{{discount}}" stepKey="selectProductTierPricePriceInput"/> - <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton1"/> - </actionGroup> - <actionGroup name="AdminSetProductDisabled"> - <conditionalClick selector="{{AdminProductFormSection.enableProductLabel}}" dependentSelector="{{AdminProductFormSection.productStatusValue('1')}}" visible="true" stepKey="disableProduct"/> - <seeElement selector="{{AdminProductFormSection.productStatusValue('2')}}" stepKey="assertThatProductSetToDisabled"/> - </actionGroup> - - <!-- You are on product Edit Page --> - <!-- Assert checkbox available for website in Product In Websites --> - <actionGroup name="AssertWebsiteIsAvailableInProductWebsites"> - <arguments> - <argument name="website" type="string"/> - </arguments> - <scrollTo selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="scrollToProductInWebsitesSection"/> - <conditionalClick selector="{{ProductInWebsitesSection.sectionHeader}}" dependentSelector="{{ProductInWebsitesSection.sectionHeaderOpened}}" visible="false" stepKey="expandProductWebsitesSection"/> - <seeElement selector="{{ProductInWebsitesSection.website(website)}}" stepKey="seeCheckboxForWebsite"/> - </actionGroup> - - <!-- You are on product Edit Page --> - <!-- Assert checkbox not available for website in Product In Websites --> - <actionGroup name="AssertWebsiteIsNotAvailableInProductWebsites" extends="AssertWebsiteIsAvailableInProductWebsites"> - <remove keyForRemoval="seeCheckboxForWebsite"/> - <dontSeeElement selector="{{ProductInWebsitesSection.website(website)}}" after="expandProductWebsitesSection" stepKey="dontSeeCheckboxForWebsite"/> - </actionGroup> - - <!-- You are on product Edit Page --> - <!-- Assert checkbox Is checked for website in Product In Websites --> - <actionGroup name="AssertProductIsAssignedToWebsite" extends="AssertWebsiteIsAvailableInProductWebsites"> - <remove keyForRemoval="seeCheckboxForWebsite"/> - <seeCheckboxIsChecked selector="{{ProductInWebsitesSection.website(website)}}" after="expandProductWebsitesSection" stepKey="seeCustomWebsiteIsChecked"/> - </actionGroup> - - <!-- You are on product Edit Page --> - <!-- Assert checkbox is not checked for website in Product In Websites --> - <actionGroup name="AssertProductIsNotAssignedToWebsite" extends="AssertWebsiteIsAvailableInProductWebsites"> - <remove keyForRemoval="seeCheckboxForWebsite"/> - <dontSeeCheckboxIsChecked selector="{{ProductInWebsitesSection.website(website)}}" after="expandProductWebsitesSection" stepKey="seeCustomWebsiteIsNotChecked"/> - </actionGroup> - - <!-- You are on product Edit Page --> - <!-- Assert Product Name in admin Product page --> - <actionGroup name="AssertProductNameInProductEditForm"> - <arguments> - <argument name="productName" type="string"/> - </arguments> - <seeInField selector="{{AdminProductFormSection.productName}}" userInput="{{productName}}" stepKey="seeProductNameOnEditProductPage"/> - </actionGroup> - - <!-- You are on product Edit Page --> - <!-- Assert Product Description in admin Product page --> - <actionGroup name="AssertProductDescriptionInProductEditForm"> - <arguments> - <argument name="productDescription" type="string"/> - </arguments> - <conditionalClick selector="{{AdminProductContentSection.sectionHeader}}" dependentSelector="{{AdminProductContentSection.sectionHeaderShow}}" visible="false" stepKey="expandContentSection"/> - <seeInField selector="{{AdminProductContentSection.descriptionTextArea}}" userInput="{{productDescription}}" stepKey="seeProductDescription"/> - </actionGroup> - - <!-- You are on StorefrontProductPage --> - <!-- Check active product image --> - <actionGroup name="StorefrontAssertActiveProductImage"> - <arguments> - <argument name="fileName" defaultValue="magento-logo" type="string"/> - </arguments> - <seeElement selector="{{StorefrontProductMediaSection.productImageActive(fileName)}}" stepKey="seeActiveImageDefault"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAddSpecialPriceActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAddSpecialPriceActionGroup.xml new file mode 100644 index 0000000000000..fc0f908ff5fbe --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAddSpecialPriceActionGroup.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="AdminProductAddSpecialPriceActionGroup"> + <annotations> + <description>Sets the provided Special Price on the Admin Product creation/edit page. Clicks on Save.</description> + </annotations> + <arguments> + <argument name="specialPrice" type="string"/> + </arguments> + + <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink1"/> + <waitForElementVisible selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="waitSpecialPrice1"/> + <click selector="{{AdminProductFormAdvancedPricingSection.useDefaultPrice}}" stepKey="checkUseDefault"/> + <fillField userInput="{{specialPrice}}" selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="fillSpecialPrice"/> + <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDone"/> + <waitForElementNotVisible selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="waitForCloseModalWindow"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAdvancedPricingNewCustomerGroupPriceActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAdvancedPricingNewCustomerGroupPriceActionGroup.xml new file mode 100644 index 0000000000000..f465eceb8f008 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAdvancedPricingNewCustomerGroupPriceActionGroup.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="AdminProductAdvancedPricingNewCustomerGroupPriceActionGroup"> + <arguments> + <argument name="qty" type="string" defaultValue="2"/> + <argument name="priceType" type="string" defaultValue="Discount"/> + <argument name="discount" type="string" defaultValue="75"/> + <argument name="customerGroup" type="string" defaultValue="0"/> + </arguments> + <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="addCustomerGroup"/> + <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput(customerGroup)}}" userInput="{{qty}}" stepKey="fillProductTierPriceQtyInput1"/> + <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceValueTypeSelect(customerGroup)}}" userInput="{{priceType}}" stepKey="selectProductTierPriceValueType1"/> + <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPricePercentageValuePriceInput(customerGroup)}}" userInput="{{discount}}" stepKey="selectProductTierPricePriceInput"/> + <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton1"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml deleted file mode 100644 index 3e54574c553e3..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml +++ /dev/null @@ -1,373 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="navigateToCreatedProductAttribute"> - <annotations> - <description>Goes to the Product Attributes grid page. Filters the grid based on the provided Product Attribute. Clicks on the 1st row.</description> - </annotations> - <arguments> - <argument name="ProductAttribute"/> - </arguments> - - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> - <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="{{ProductAttribute.attribute_code}}" stepKey="setAttributeCode"/> - <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromTheGrid"/> - <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="clickOnAttributeRow"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - </actionGroup> - - <actionGroup name="navigateToEditProductAttribute"> - <annotations> - <description>Goes to the Product Attributes grid page. Filters the grid based on the provided Product Attribute. Clicks on the 1st row.</description> - </annotations> - <arguments> - <argument name="ProductAttribute" type="string"/> - </arguments> - - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> - <fillField selector="{{AdminProductAttributeGridSection.GridFilterFrontEndLabel}}" userInput="{{ProductAttribute}}" stepKey="navigateToAttributeEditPage1"/> - <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="navigateToAttributeEditPage2"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="navigateToAttributeEditPage3"/> - <waitForPageLoad stepKey="waitForPageLoad3"/> - </actionGroup> - - <actionGroup name="AdminCreateAttributeFromProductPage"> - <annotations> - <description>From the Product creation/edit page, under 'Configurations', Clicks on 'Create Configurations'. Clicks on 'Create New Attribute'. Fills Label. Selects Type. Clicks on Save.</description> - </annotations> - <arguments> - <argument name="attributeName" type="string"/> - <argument name="attributeType" type="string" defaultValue="TextField"/> - </arguments> - - <click selector="{{AdminProductFormSection.addAttributeBtn}}" stepKey="clickAddAttributeBtn"/> - <see userInput="Select Attribute" stepKey="checkNewAttributePopUpAppeared"/> - <click selector="{{AdminProductFormAttributeSection.createNewAttribute}}" stepKey="clickCreateNewAttribute"/> - <fillField selector="{{AdminProductFormNewAttributeSection.attributeLabel}}" userInput="{{attributeName}}" stepKey="fillAttributeLabel"/> - <selectOption selector="{{AdminProductFormNewAttributeSection.attributeType}}" userInput="{{attributeType}}" stepKey="selectAttributeType"/> - <click selector="{{AdminProductFormNewAttributeSection.saveAttribute}}" stepKey="saveAttribute"/> - </actionGroup> - <actionGroup name="AdminCreateAttributeFromProductPageWithScope" extends="AdminCreateAttributeFromProductPage" insertAfter="selectAttributeType"> - <arguments> - <argument name="scope" type="string" defaultValue="Store View"/> - </arguments> - <conditionalClick selector="{{AdminProductFormNewAttributeAdvancedSection.sectionHeader}}" dependentSelector="{{AdminProductFormNewAttributeAdvancedSection.scope}}" visible="false" stepKey="openAttributeAdvancedSection"/> - <selectOption selector="{{AdminProductFormNewAttributeAdvancedSection.scope}}" userInput="{{scope}}" stepKey="selectScope"/> - </actionGroup> - - <actionGroup name="AdminCreateAttributeWithValueWithTwoStoreViesFromProductPage" extends="AdminCreateAttributeFromProductPage"> - <remove keyForRemoval="saveAttribute"/> - <annotations> - <description>EXTENDS: AdminCreateAttributeFromProductPage. Adds the 2 provided Store Views to a Product. Clicks on Save.</description> - </annotations> - <arguments> - <argument name="firstStoreViewName" type="string"/> - <argument name="secondStoreViewName" type="string"/> - </arguments> - - <click selector="{{AdminProductFormNewAttributeSection.addValue}}" stepKey="addValue" after="selectAttributeType"/> - <seeElement selector="{{AdminProductFormNewAttributeSection.optionViewName(firstStoreViewName))}}" stepKey="seeFirstStoreView"/> - <seeElement selector="{{AdminProductFormNewAttributeSection.optionViewName(firstStoreViewName))}}" stepKey="seeSecondStoreView"/> - <fillField selector="{{AdminProductFormNewAttributeSection.optionValue('1'))}}" userInput="default" stepKey="fillDefaultStoreView"/> - <fillField selector="{{AdminProductFormNewAttributeSection.optionValue('2'))}}" userInput="admin" stepKey="fillAdminStoreView"/> - <fillField selector="{{AdminProductFormNewAttributeSection.optionValue('3'))}}" userInput="view1" stepKey="fillFirstStoreView"/> - <fillField selector="{{AdminProductFormNewAttributeSection.optionValue('4'))}}" userInput="view2" stepKey="fillSecondStoreView"/> - - <!--Check store view in Manage Titles section--> - <click selector="{{AdminProductFormNewAttributeSection.manageTitlesHeader}}" stepKey="openManageTitlesSection"/> - <seeElement selector="{{AdminProductFormNewAttributeSection.manageTitlesViewName(customStoreEN.name)}}" stepKey="seeFirstStoreViewName"/> - <seeElement selector="{{AdminProductFormNewAttributeSection.manageTitlesViewName(customStoreFR.name)}}" stepKey="seeSecondStoreViewName"/> - <click selector="{{AdminProductFormNewAttributeSection.saveAttribute}}" stepKey="saveAttribute1"/> - </actionGroup> - - <!-- Creates attribute and attribute set from the product page--> - <actionGroup name="AdminProductPageCreateAttributeSetWithAttribute"> - <annotations> - <description>Adds the provided Product Attribute Set to a Product on the Product creation/edit page.</description> - </annotations> - <arguments> - <argument name="attributeName" type="string"/> - <argument name="attributeSetName" type="string"/> - <argument name="attributeType" type="string" defaultValue="TextField"/> - </arguments> - - <click selector="{{AdminProductFormSection.addAttributeBtn}}" stepKey="clickAddAttributeBtn"/> - <waitForPageLoad stepKey="waitForSidePanel"/> - <see userInput="Select Attribute" stepKey="checkNewAttributePopUpAppeared"/> - <click selector="{{AdminProductFormAttributeSection.createNewAttribute}}" stepKey="clickCreateNewAttribute"/> - <fillField selector="{{AdminProductFormNewAttributeSection.attributeLabel}}" userInput="{{attributeName}}" stepKey="fillAttributeLabel"/> - <selectOption selector="{{AdminProductFormNewAttributeSection.attributeType}}" userInput="{{attributeType}}" stepKey="selectAttributeType"/> - <click selector="{{AdminProductFormNewAttributeSection.saveInNewSet}}" stepKey="saveAttribute"/> - <fillField selector="{{AdminProductFormNewAttributeNewSetSection.setName}}" userInput="{{attributeSetName}}" stepKey="fillSetName"/> - <click selector="{{AdminProductFormNewAttributeNewSetSection.accept}}" stepKey="acceptPopup"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingToFinish"/> - <!-- Product page will hang if there is no reload--> - <reloadPage stepKey="reloadPage"/> - <waitForPageLoad stepKey="waitForReload"/> - </actionGroup> - - <!-- Create attribute and set with given search weight and defaultValue from the Edit Product Page --> - <actionGroup name="AdminCreateAttributeWithSearchWeight" extends="AdminProductPageCreateAttributeSetWithAttribute" insertAfter="selectAttributeType"> - <annotations> - <description>EXTENDS: AdminProductPageCreateAttributeSetWithAttribute. Sets the provided Search Weight and Default Values.</description> - </annotations> - <arguments> - <argument name="weight" type="string" defaultValue="1"/> - <argument name="defaultValue" type="string" defaultValue="default"/> - </arguments> - - <click selector="{{AdminProductFormNewAttributeAdvancedSection.sectionHeader}}" stepKey="openAdvancedSection"/> - <fillField selector="{{AdminProductFormNewAttributeAdvancedSection.defaultValue}}" userInput="{{defaultValue}}" stepKey="inputDefault"/> - <click selector="{{AdminProductFormNewAttributeStorefrontSection.sectionHeader}}" stepKey="openStorefrontSection"/> - <checkOption selector="{{AdminProductFormNewAttributeStorefrontSection.useInSearch}}" stepKey="checkUseInSearch"/> - <waitForElementVisible selector="{{AdminProductFormNewAttributeStorefrontSection.searchWeight}}" stepKey="waitForSearchWeight"/> - <selectOption selector="{{AdminProductFormNewAttributeStorefrontSection.searchWeight}}" userInput="{{weight}}" stepKey="selectWeight"/> - </actionGroup> - - <actionGroup name="AdminProductPageSelectAttributeSet"> - <annotations> - <description>Selects the provided Attribute Set from the Admin Product creation/edit page.</description> - </annotations> - <arguments> - <argument name="attributeSetName" type="string"/> - </arguments> - - <click stepKey="openDropdown" selector="{{AdminProductFormSection.attributeSet}}"/> - <fillField stepKey="filter" selector="{{AdminProductFormSection.attributeSetFilter}}" userInput="{{attributeSetName}}"/> - <click stepKey="clickResult" selector="{{AdminProductFormSection.attributeSetFilterResult}}"/> - </actionGroup> - - <actionGroup name="AdminProductPageFillTextAttributeValueByName"> - <annotations> - <description>Fills in the Attribute Name field with the provided value.</description> - </annotations> - <arguments> - <argument name="attributeName" type="string"/> - <argument name="value" type="string"/> - </arguments> - - <click stepKey="openSection" selector="{{AdminProductAttributeSection.attributeSectionHeader}}"/> - <fillField stepKey="fillValue" selector="{{AdminProductAttributeSection.textAttributeByName(attributeName)}}" userInput="{{value}}"/> - </actionGroup> - - <actionGroup name="changeUseForPromoRuleConditionsProductAttribute"> - <annotations> - <description>Select the provided value for the 'Use for Promo Rule Conditions' dropdown menu. Clicks on Save. Validates that the Save message is present.</description> - </annotations> - <arguments> - <argument name="option" type="string"/> - </arguments> - - <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="clickStoreFrontPropertiesTab"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <selectOption selector="{{StorefrontPropertiesSection.useForPromoRuleConditions}}" userInput="{{option}}" stepKey="changeOption"/> - <click selector="{{AttributePropertiesSection.Save}}" stepKey="saveAttribute"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You saved the product attribute." stepKey="successMessage"/> - </actionGroup> - - <actionGroup name="deleteProductAttribute" extends="navigateToCreatedProductAttribute"> - <annotations> - <description>EXTENDS: navigateToCreatedProductAttribute. Deletes the Product Attribute. Validates that the success message is present.</description> - </annotations> - - <click selector="{{AttributePropertiesSection.DeleteAttribute}}" stepKey="deleteAttribute"/> - <click selector="{{ModalConfirmationSection.OkButton}}" stepKey="ClickOnDeleteButton"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> - </actionGroup> - - <actionGroup name="deleteProductAttributeByLabel"> - <annotations> - <description>Goes to the Admin Product Attributes grid page. Filters the grid for the provided Product Attribute (Label). Deletes the Product Attribute from the grid. Validates that the Success Message is present.</description> - </annotations> - <arguments> - <argument name="ProductAttribute"/> - </arguments> - - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> - <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="{{ProductAttribute.default_label}}" stepKey="setAttributeCode"/> - <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromTheGrid"/> - <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="clickOnAttributeRow"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <click selector="{{AttributePropertiesSection.DeleteAttribute}}" stepKey="deleteAttribute"/> - <click selector="{{ModalConfirmationSection.OkButton}}" stepKey="ClickOnDeleteButton"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> - </actionGroup> - - <!-- Delete product attribute by Attribute Code --> - <actionGroup name="deleteProductAttributeByAttributeCode"> - <annotations> - <description>Goes to the Admin Product Attributes grid page. Filters the grid for the provided Product Attribute Code. Deletes the Product Attribute from the grid. Validates that the Success Message is present.</description> - </annotations> - <arguments> - <argument name="ProductAttributeCode" type="string"/> - </arguments> - - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> - <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="{{ProductAttributeCode}}" stepKey="setAttributeCode"/> - <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromTheGrid"/> - <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="clickOnAttributeRow"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <click selector="{{AttributePropertiesSection.DeleteAttribute}}" stepKey="deleteAttribute"/> - <click selector="{{ModalConfirmationSection.OkButton}}" stepKey="ClickOnDeleteButton"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> - </actionGroup> - - <!--Filter product attribute by Attribute Code --> - <actionGroup name="filterProductAttributeByAttributeCode"> - <annotations> - <description>Filters the Product Attributes grid by the provided Product Attribute Code.</description> - </annotations> - <arguments> - <argument name="ProductAttributeCode" type="string"/> - </arguments> - - <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGrid"/> - <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="{{ProductAttributeCode}}" stepKey="setAttributeCode"/> - <waitForPageLoad stepKey="waitForUserInput"/> - <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromTheGrid"/> - </actionGroup> - - <!--Filter product attribute by Default Label --> - <actionGroup name="filterProductAttributeByDefaultLabel"> - <annotations> - <description>Filters the Product Attributes grid by the provided Product Attribute Label.</description> - </annotations> - <arguments> - <argument name="productAttributeLabel" type="string"/> - </arguments> - - <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGrid"/> - <fillField selector="{{AdminProductAttributeGridSection.GridFilterFrontEndLabel}}" userInput="{{productAttributeLabel}}" stepKey="setDefaultLabel"/> - <waitForPageLoad stepKey="waitForUserInput"/> - <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromTheGrid"/> - </actionGroup> - - <actionGroup name="saveProductAttribute"> - <annotations> - <description>Clicks on Save. Validates that the Success Message is present.</description> - </annotations> - - <waitForElementVisible selector="{{AttributePropertiesSection.Save}}" stepKey="waitForSaveButton"/> - <click selector="{{AttributePropertiesSection.Save}}" stepKey="clickSaveButton"/> - <waitForPageLoad stepKey="waitForAttributeToSave"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSuccessMessage"/> - </actionGroup> - - <actionGroup name="confirmChangeInputTypeModal"> - <annotations> - <description>Clicks on the Confirm button for the 'Product Data My Be Lost' modal.</description> - </annotations> - - <waitForElementVisible selector="{{AdminEditProductAttributesSection.ProductDataMayBeLostConfirmButton}}" stepKey="waitForChangeInputTypeButton"/> - <click selector="{{AdminEditProductAttributesSection.ProductDataMayBeLostConfirmButton}}" stepKey="clickChangeInputTypeButton"/> - <waitForElementNotVisible selector="{{AdminEditProductAttributesSection.ProductDataMayBeLostModal}}" stepKey="waitForChangeInputTypeModalGone"/> - </actionGroup> - - <actionGroup name="saveProductAttributeInUse"> - <annotations> - <description>Clicks on Save. Validates that the Success Message is present.</description> - </annotations> - - <waitForElementVisible selector="{{AttributePropertiesSection.Save}}" stepKey="waitForSaveButton"/> - <click selector="{{AttributePropertiesSection.Save}}" stepKey="clickSaveButton"/> - <waitForPageLoad stepKey="waitForAttributeToSave"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSuccessMessage"/> - </actionGroup> - - <!--Clicks Add Attribute and adds the given attribute--> - <actionGroup name="addProductAttributeInProductModal"> - <annotations> - <description>Adds the provided Attribute Code to the Product on the Admin Product creation/edit page.</description> - </annotations> - <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"> - <annotations> - <description>Clicks on 'Add New Attribute'. Fills in the Attribute Details (Label, Input Type and Value Required). Clicks on Save.</description> - </annotations> - <arguments> - <argument name="attribute" type="entity" defaultValue="productAttributeWysiwyg"/> - </arguments> - - <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}}"/> - <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> - </actionGroup> - - <!-- Inputs text default value and attribute code--> - <actionGroup name="createProductAttributeWithTextField" extends="createProductAttribute" insertAfter="checkRequired"> - <annotations> - <description>EXTENDS: createProductAttribute. Fills in the Attribute Code and Default Value (Attribute Type: Text Field).</description> - </annotations> - - <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"> - <annotations> - <description>EXTENDS: createProductAttribute. Fills in the Attribute Code and Default Value (Attribute Type: Date Field).</description> - </annotations> - <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"> - <annotations> - <description>Creates an Option for a Product Attribute (Attribute Type: Dropdown).</description> - </annotations> - <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"> - <annotations> - <description>EXTENDS: createAttributeDropdownNthOption. Checks the 'Is Default' option.</description> - </annotations> - - <checkOption selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault(row)}}" stepKey="setAsDefault" after="fillStoreView"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeMassUpdateActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeMassUpdateActionGroup.xml index 57b180ada1536..d20b44b0162f0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeMassUpdateActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeMassUpdateActionGroup.xml @@ -23,7 +23,7 @@ <click selector="{{AdminUpdateAttributesSection.toggleDescription}}" stepKey="clickToChangeDescription"/> <fillField selector="{{AdminUpdateAttributesSection.description}}" userInput="{{product.description}}" stepKey="fillFieldDescription"/> <click selector="{{AdminUpdateAttributesSection.saveButton}}" stepKey="save"/> - <waitForElementVisible selector="{{AdminMessagesSection.successMessage}}" stepKey="waitVisibleSuccessMessage"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeSuccessMessage"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitVisibleSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="Message is added to queue" stepKey="seeSuccessMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml index e20b5f113a7ec..6e05fa614dfb9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.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="AssignAttributeToGroup"> + <actionGroup name="AssignAttributeToGroupActionGroup"> <annotations> <description>Assign the provided Attribute to an Attribute Set from the Attribute Sets creation/edit page.</description> </annotations> @@ -23,118 +23,4 @@ <waitForPageLoad stepKey="waitForPageLoad2"/> <see userInput="{{attribute}}" selector="{{AdminProductAttributeSetEditSection.groupTree}}" stepKey="seeAttributeInGroup"/> </actionGroup> - - <actionGroup name="UnassignAttributeFromGroup"> - <annotations> - <description>Unassign the provided Attribute from an Attribute Set from the Attribute Sets creation/edit page.</description> - </annotations> - <arguments> - <argument name="group" type="string"/> - <argument name="attribute" type="string"/> - </arguments> - - <conditionalClick selector="{{AdminProductAttributeSetEditSection.attributeGroupExtender(group)}}" dependentSelector="{{AdminProductAttributeSetEditSection.attributeGroupCollapsed(group)}}" visible="true" stepKey="extendGroup"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <dragAndDrop selector1="{{AdminProductAttributeSetEditSection.assignedAttribute(attribute)}}" selector2="{{AdminProductAttributeSetEditSection.xThLineItemUnassignedAttribute('1')}}" stepKey="dragAndDropToUnassigned"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <see userInput="{{attribute}}" selector="{{AdminProductAttributeSetEditSection.unassignedAttributesTree}}" stepKey="seeAttributeInUnassigned"/> - </actionGroup> - - <actionGroup name="SaveAttributeSet"> - <annotations> - <description>Save an Attribute Set on the Attribute Set creation/edit page.</description> - </annotations> - - <click selector="{{AdminProductAttributeSetActionSection.save}}" stepKey="clickSave"/> - <see userInput="You saved the attribute set" selector="{{AdminMessagesSection.success}}" stepKey="successMessage"/> - </actionGroup> - - <!--Generic attribute set creation--> - <actionGroup name="CreateDefaultAttributeSet"> - <annotations> - <description>Goes to the Attribute Sets grid page. Clicks on Add. Fill Name. Clicks on Save.</description> - </annotations> - <arguments> - <argument name="label" type="string"/> - </arguments> - - <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/> - <waitForPageLoad stepKey="wait1"/> - <click selector="{{AdminProductAttributeSetGridSection.addAttributeSetBtn}}" stepKey="clickAddAttributeSet"/> - <fillField selector="{{AdminProductAttributeSetSection.name}}" userInput="{{label}}" stepKey="fillName"/> - <click selector="{{AdminProductAttributeSetSection.saveBtn}}" stepKey="clickSave1"/> - </actionGroup> - - <actionGroup name="goToAttributeGridPage"> - <annotations> - <description>Goes to the Attribute Sets grid page.</description> - </annotations> - - <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/> - </actionGroup> - - <actionGroup name="goToAttributeSetByName"> - <annotations> - <description>Searches for the provided Attribute Sets Name. Clicks on the 1st row.</description> - </annotations> - <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"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - </actionGroup> - - <!-- Filter By Attribute Label --> - <actionGroup name="filterProductAttributeByAttributeLabel"> - <annotations> - <description>Searches the Attribute Sets grid page for the provided Attribute Set Name.</description> - </annotations> - <arguments> - <argument name="productAttributeLabel" type="string"/> - </arguments> - - <fillField selector="{{AdminProductAttributeGridSection.attributeLabelFilter}}" userInput="{{productAttributeLabel}}" stepKey="setAttributeLabel"/> - <waitForPageLoad stepKey="waitForUserInput"/> - <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromTheGrid"/> - </actionGroup> - - <actionGroup name="FilterProductAttributeSetGridByAttributeSetName"> - <annotations> - <description>Filters the Attribute Sets grid page for the provided Attribute Set Name.</description> - </annotations> - <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"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> - </actionGroup> - - <!-- Delete attribute set --> - <actionGroup name="deleteAttributeSetByLabel"> - <annotations> - <description>Deletes the provided Attribute Set Name from the Attribute Sets grid page.</description> - </annotations> - <arguments> - <argument name="label" type="string"/> - </arguments> - - <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/> - <waitForPageLoad stepKey="waitForAttributeSetPageLoad"/> - <fillField selector="{{AdminProductAttributeSetGridSection.filter}}" userInput="{{label}}" stepKey="filterByName"/> - <click selector="{{AdminProductAttributeSetGridSection.searchBtn}}" stepKey="clickSearch"/> - <click selector="{{AdminProductAttributeSetGridSection.nthRow('1')}}" stepKey="clickFirstRow"/> - <waitForPageLoad stepKey="waitForClick"/> - <click selector="{{AdminProductAttributeSetSection.deleteBtn}}" stepKey="clickDelete"/> - <click selector="{{AdminProductAttributeSetSection.modalOk}}" stepKey="confirmDelete"/> - <waitForPageLoad stepKey="waitForDeleteToFinish"/> - <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="The attribute set has been removed." stepKey="seeDeleteMessage"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml deleted file mode 100644 index 320a322fc5f8e..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml +++ /dev/null @@ -1,445 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Reset the product grid to the default view--> - <actionGroup name="resetProductGridToDefaultView"> - <annotations> - <description>Sets the Admin Products grid view to the 'Default View'.</description> - </annotations> - - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> - <click selector="{{AdminProductGridFilterSection.viewDropdown}}" stepKey="openViewBookmarksTab"/> - <click selector="{{AdminProductGridFilterSection.viewBookmark('Default View')}}" stepKey="resetToDefaultGridView"/> - <waitForPageLoad stepKey="waitForProductGridLoad"/> - <see selector="{{AdminProductGridFilterSection.viewDropdown}}" userInput="Default View" stepKey="seeDefaultViewSelected"/> - </actionGroup> - - <!--Filter the product grid by the SKU field--> - <actionGroup name="filterProductGridBySku"> - <annotations> - <description>Filters the Admin Products grid by the provided Product (SKU).</description> - </annotations> - <arguments> - <argument name="product" defaultValue="_defaultProduct"/> - </arguments> - - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillProductSkuFilter"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> - </actionGroup> - - <!--Filter the product grid by the SKU string --> - <actionGroup name="filterProductGridBySku2"> - <annotations> - <description>Filters the Admin Products grid by the provided Product SKU.</description> - </annotations> - <arguments> - <argument name="sku" type="string"/> - </arguments> - - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{sku}}" stepKey="fillProductSkuFilter"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> - </actionGroup> - - <!--Filter the product grid by the Name field--> - <actionGroup name="filterProductGridByName"> - <annotations> - <description>Filters the Admin Products grid by the provided Product (Name).</description> - </annotations> - <arguments> - <argument name="product" defaultValue="_defaultProduct"/> - </arguments> - - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> - <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{product.name}}" stepKey="fillProductNameFilter"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> - </actionGroup> - - <!--Filter the product grid by the Name field--> - <actionGroup name="filterProductGridByName2"> - <annotations> - <description>Filters the Admin Products grid by the provided Product Name.</description> - </annotations> - <arguments> - <argument name="name" type="string"/> - </arguments> - - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> - <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{name}}" stepKey="fillProductNameFilter"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> - </actionGroup> - - <!--Filter the product grid by new from date filter--> - <actionGroup name="filterProductGridBySetNewFromDate"> - <annotations> - <description>Filters the Admin Products grid by the New From Data field. PLEASE NOTE: The Start Date is Hardcoded.</description> - </annotations> - - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> - <fillField selector="{{AdminProductGridFilterSection.newFromDateFilter}}" userInput="05/16/2018" stepKey="fillSetAsNewProductFilter"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> - </actionGroup> - - <!--Filter the product grid by a price range--> - <actionGroup name="filterProductGridByPriceRange"> - <annotations> - <description>Filters the Admin Products grid by the provided Price Filter.</description> - </annotations> - <arguments> - <argument name="filter"/> - </arguments> - - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> - <fillField selector="{{AdminProductGridFilterSection.priceFilterFrom}}" userInput="{{filter.from}}" stepKey="fillProductPriceFromFilter"/> - <fillField selector="{{AdminProductGridFilterSection.priceFilterTo}}" userInput="{{filter.to}}" stepKey="fillProductPriceToFilter"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> - </actionGroup> - - <!--Filter the product grid to Enabled products--> - <actionGroup name="filterProductGridByEnabledStatus"> - <annotations> - <description>Filters the Admin Products grid by the 'Enabled' Status. PLEASE NOTE: The Filter is Hardcoded.</description> - </annotations> - - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> - <selectOption selector="{{AdminProductGridFilterSection.statusFilter}}" userInput="Enabled" stepKey="selectEnabledStatusFilter"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> - </actionGroup> - - <!--Filter the product grid to Disabled products--> - <actionGroup name="filterProductGridByDisabledStatus"> - <annotations> - <description>Filters the Admin Products grid by the 'Disabled' Status. PLEASE NOTE: The Filter is Hardcoded.</description> - </annotations> - - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> - <selectOption selector="{{AdminProductGridFilterSection.statusFilter}}" userInput="Disabled" stepKey="selectDisabledStatusFilter"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> - </actionGroup> - - <!--Search product grid with keyword search--> - <actionGroup name="searchProductGridByKeyword"> - <annotations> - <description>Searches the Admin Products grid for the provided Keyword.</description> - </annotations> - <arguments> - <argument name="keyword"/> - </arguments> - - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="{{keyword}}" stepKey="fillKeywordSearchField"/> - <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearch"/> - </actionGroup> - - <!--Search product grid with keyword search--> - <!-- Argument type: string (see more in MQE-965) --> - <actionGroup name="searchProductGridByKeyword2"> - <annotations> - <description>Searches the Admin Products grid for the provided Keyword.</description> - </annotations> - <arguments> - <argument name="keyword" type="string"/> - </arguments> - - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="{{keyword}}" stepKey="fillKeywordSearchField"/> - <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearch"/> - </actionGroup> - - <!--Filter product grid by name, sku, and type; and see expected product--> - <actionGroup name="viewProductInAdminGrid"> - <annotations> - <description>Filters the Admin Products grid by the provided Product (Name, SKU and Type).</description> - </annotations> - <arguments> - <argument name="product" defaultValue="_defaultProduct"/> - </arguments> - - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> - <waitForPageLoad stepKey="waitForPageLoadInitial"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> - <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{product.name}}" stepKey="fillProductNameFilter"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillProductSkuFilter"/> - <selectOption selector="{{AdminProductGridFilterSection.typeFilter}}" userInput="{{product.type_id}}" stepKey="selectionProductType"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="{{product.name}}" stepKey="seeProductNameInGrid"/> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Price')}}" userInput="{{product.price}}" stepKey="seeProductPriceInGrid"/> - <click selector="{{AdminProductGridFilterSection.clearFilters}}" stepKey="clickClearFiltersAfter"/> - </actionGroup> - - <!-- Filter product grid by sku, name --> - <actionGroup name="filterProductGridBySkuAndName"> - <annotations> - <description>Filters the Admin Products grid by the provided Product (Name and SKU).</description> - </annotations> - <arguments> - <argument name="product" defaultValue="_defaultProduct"/> - </arguments> - - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillProductSkuFilter"/> - <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{product.name}}" stepKey="fillProductNameFilter"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - </actionGroup> - - <!--Delete a product by filtering grid and using delete action--> - <actionGroup name="deleteProductUsingProductGrid"> - <annotations> - <description>Deletes the provided Product from the Admin Products grid page.</description> - </annotations> - <arguments> - <argument name="product"/> - </arguments> - - <!--TODO use other action group for filtering grid when MQE-539 is implemented --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> - <waitForPageLoad time="60" stepKey="waitForPageLoadInitial"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillProductSkuFilter"/> - <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{product.name}}" stepKey="fillProductNameFilter"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <see selector="{{AdminProductGridSection.productGridCell('1', 'SKU')}}" userInput="{{product.sku}}" stepKey="seeProductSkuInGrid"/> - <click selector="{{AdminProductGridSection.multicheckDropdown}}" stepKey="openMulticheckDropdown"/> - <click selector="{{AdminProductGridSection.multicheckOption('Select All')}}" stepKey="selectAllProductInFilteredGrid"/> - <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Delete')}}" stepKey="clickDeleteAction"/> - <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/> - <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmProductDelete"/> - </actionGroup> - - <!--Delete all products by filtering grid and using mass delete action--> - <actionGroup name="deleteAllDuplicateProductUsingProductGrid" extends="deleteProductUsingProductGrid"> - <annotations> - <description>EXTENDS: deleteProductUsingProductGrid. Removes 'seeProductSkuInGrid'.</description> - </annotations> - <arguments> - <argument name="product"/> - </arguments> - - <remove keyForRemoval="seeProductSkuInGrid"/> - </actionGroup> - - <!--Delete a product by filtering grid and using delete action--> - <actionGroup name="deleteProductBySku"> - <annotations> - <description>Goes to the Admin Products grid page. Deletes the provided Product SKU.</description> - </annotations> - <arguments> - <argument name="sku" type="string"/> - </arguments> - - <!--TODO use other action group for filtering grid when MQE-539 is implemented --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{sku}}" stepKey="fillProductSkuFilter"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <see selector="{{AdminProductGridSection.productGridCell('1', 'SKU')}}" userInput="{{sku}}" stepKey="seeProductSkuInGrid"/> - <click selector="{{AdminProductGridSection.multicheckDropdown}}" stepKey="openMulticheckDropdown"/> - <click selector="{{AdminProductGridSection.multicheckOption('Select All')}}" stepKey="selectAllProductInFilteredGrid"/> - <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Delete')}}" stepKey="clickDeleteAction"/> - <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForConfirmModal"/> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmProductDelete"/> - <see selector="{{AdminMessagesSection.success}}" userInput="record(s) have been deleted." stepKey="seeSuccessMessage"/> - </actionGroup> - - <actionGroup name="deleteProductByName" extends="deleteProductBySku"> - <annotations> - <description>EXTENDS: deleteProductBySku. Deletes the provided Product Name.</description> - </annotations> - <arguments> - <argument name="sku" type="string" defaultValue=""/> - <argument name="name" type="string"/> - </arguments> - - <remove keyForRemoval="fillProductSkuFilter"/> - <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{name}}" stepKey="fillProductSkuFilter" after="openProductFilters"/> - <remove keyForRemoval="seeProductSkuInGrid"/> - <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="{{name}}" stepKey="seeProductNameInGrid" after="clickApplyFilters"/> - </actionGroup> - - <actionGroup name="deleteProductsByKeyword"> - <annotations> - <description>Delete products by keyword</description> - </annotations> - <arguments> - <argument name="keyword" type="string"/> - </arguments> - - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> - <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="{{keyword}}" stepKey="fillKeywordField"/> - <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="keywordSearchButton"/> - <click selector="{{AdminProductGridSection.multicheckDropdown}}" stepKey="openMulticheckDropdown"/> - <click selector="{{AdminProductGridSection.multicheckOption('Select All')}}" stepKey="selectAllProductInFilteredGrid"/> - <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Delete')}}" stepKey="clickDeleteAction"/> - <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForConfirmModal"/> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmProductDelete"/> - <see selector="{{AdminMessagesSection.success}}" userInput="record(s) have been deleted." stepKey="seeSuccessMessage"/> - </actionGroup> - - <!--Open product for edit by clicking row X and column Y in product grid--> - <actionGroup name="openProducForEditByClickingRowXColumnYInProductGrid"> - <annotations> - <description>Clicks on the 'Edit' button in the Admin Products grid by the provided Grid coordinates (X, Y).</description> - </annotations> - <arguments> - <argument name="X" type="string" defaultValue="1"/> - <argument name="Y" type="string" defaultValue="2"/> - </arguments> - - <click selector="{{AdminProductGridSection.productGridXRowYColumnButton(X, Y)}}" stepKey="openProductForEdit"/> - </actionGroup> - - <!-- Sort products by ID descending --> - <actionGroup name="sortProductsByIdDescending"> - <annotations> - <description>Filters the ID column in Descending Order.</description> - </annotations> - - <conditionalClick selector="{{AdminProductGridTableHeaderSection.id('ascend')}}" dependentSelector="{{AdminProductGridTableHeaderSection.id('descend')}}" visible="false" stepKey="sortById"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - </actionGroup> - - <!-- Sort products by ID ascending --> - <actionGroup name="sortProductsByIdAscending"> - <annotations> - <description>Filters the ID column in Ascending Order.</description> - </annotations> - - <conditionalClick selector="{{AdminProductGridTableHeaderSection.id('descend')}}" dependentSelector="{{AdminProductGridTableHeaderSection.id('ascend')}}" visible="false" stepKey="sortById"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - </actionGroup> - - <!--Disabled a product by filtering grid and using change status action--> - <actionGroup name="ChangeStatusProductUsingProductGridActionGroup"> - <annotations> - <description>Goes to the Admin Products grid page. Changes the Product Status to the provided Status. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <argument name="product"/> - <argument name="status" defaultValue="Enable" type="string"/> - </arguments> - - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> - <waitForPageLoad time="60" stepKey="waitForPageLoadInitial"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillProductSkuFilter"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <see selector="{{AdminProductGridSection.productGridCell('1', 'SKU')}}" userInput="{{product.sku}}" stepKey="seeProductSkuInGrid"/> - <click selector="{{AdminProductGridSection.multicheckDropdown}}" stepKey="openMulticheckDropdown"/> - <click selector="{{AdminProductGridSection.multicheckOption('Select All')}}" stepKey="selectAllProductInFilteredGrid"/> - - <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Change status')}}" stepKey="clickChangeStatusAction"/> - <click selector="{{AdminProductGridSection.changeStatus('status')}}" stepKey="clickChangeStatusDisabled"/> - <waitForPageLoad stepKey="waitForStatusToBeChanged"/> - <see selector="{{AdminMessagesSection.success}}" userInput="A total of 1 record(s) have been updated." stepKey="seeSuccessMessage"/> - <waitForLoadingMaskToDisappear stepKey="waitForMaskToDisappear"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial2"/> - </actionGroup> - - <actionGroup name="NavigateToAndResetProductGridToDefaultView" extends="resetProductGridToDefaultView"> - <annotations> - <description>EXTENDS: resetProductGridToDefaultView. Adds an action to go to the Admin Products grid page.</description> - </annotations> - - <amOnPage url="{{AdminProductIndexPage.url}}" before="clickClearFilters" stepKey="goToAdminProductIndexPage"/> - <waitForPageLoad after="goToAdminProductIndexPage" stepKey="waitForProductIndexPageToLoad"/> - </actionGroup> - - <actionGroup name="NavigateToAndResetProductAttributeGridToDefaultView"> - <annotations> - <description>Goes to the Product Attributes grid. Clicks on 'Clear Filters'.</description> - </annotations> - - <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> - <waitForPageLoad stepKey="waitForGridLoad"/> - </actionGroup> - - <!--Filter and select the product --> - <actionGroup name="filterAndSelectProduct"> - <annotations> - <description>Goes to the Admin Products grid. Filters the Product grid by the provided Product SKU.</description> - </annotations> - <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> - - <actionGroup name="deleteProductsIfTheyExist"> - <annotations> - <description>Deletes all Products in the Admin Products grid.</description> - </annotations> - - <conditionalClick selector="{{AdminProductGridSection.multicheckDropdown}}" dependentSelector="{{AdminProductGridSection.firstProductRow}}" visible="true" stepKey="openMulticheckDropdown"/> - <conditionalClick selector="{{AdminProductGridSection.multicheckOption('Select All')}}" dependentSelector="{{AdminProductGridSection.firstProductRow}}" visible="true" stepKey="selectAllProductInFilteredGrid"/> - <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Delete')}}" stepKey="clickDeleteAction"/> - <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="waitForModalPopUp"/> - <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmProductDelete"/> - <waitForPageLoad stepKey="waitForGridLoad"/> - </actionGroup> - - <actionGroup name="deleteAllProductsUsingProductGrid"> - <annotations> - <description>Deletes all products in Admin Products grid page.</description> - </annotations> - - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="openAdminGridProductsPage"/> - <waitForPageLoad time="60" stepKey="waitForPageFullyLoad"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clearGridFilters"/> - - <conditionalClick selector="{{AdminProductGridSection.multicheckDropdown}}" dependentSelector="{{AdminDataGridTableSection.dataGridEmpty}}" visible="false" stepKey="openMulticheckDropdown"/> - <conditionalClick selector="{{AdminProductGridSection.multicheckOption('Select All')}}" dependentSelector="{{AdminDataGridTableSection.dataGridEmpty}}" visible="false" stepKey="selectAllProductsInGrid"/> - <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> - <click selector="{{AdminProductGridSection.bulkActionOption('Delete')}}" stepKey="clickDeleteAction"/> - - <waitForElementVisible selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForConfirmModal"/> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> - <waitForElementVisible selector="{{AdminDataGridTableSection.dataGridEmpty}}" stepKey="waitGridIsEmpty"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageCreateAttributeSetWithAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageCreateAttributeSetWithAttributeActionGroup.xml new file mode 100644 index 0000000000000..bdf59b20f8e38 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageCreateAttributeSetWithAttributeActionGroup.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminProductPageCreateAttributeSetWithAttributeActionGroup"> + <annotations> + <description>Adds the provided Product Attribute Set to a Product on the Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="attributeName" type="string"/> + <argument name="attributeSetName" type="string"/> + <argument name="attributeType" type="string" defaultValue="TextField"/> + </arguments> + + <click selector="{{AdminProductFormSection.addAttributeBtn}}" stepKey="clickAddAttributeBtn"/> + <waitForPageLoad stepKey="waitForSidePanel"/> + <see userInput="Select Attribute" stepKey="checkNewAttributePopUpAppeared"/> + <click selector="{{AdminProductFormAttributeSection.createNewAttribute}}" stepKey="clickCreateNewAttribute"/> + <fillField selector="{{AdminProductFormNewAttributeSection.attributeLabel}}" userInput="{{attributeName}}" stepKey="fillAttributeLabel"/> + <selectOption selector="{{AdminProductFormNewAttributeSection.attributeType}}" userInput="{{attributeType}}" stepKey="selectAttributeType"/> + <click selector="{{AdminProductFormNewAttributeSection.saveInNewSet}}" stepKey="saveAttribute"/> + <fillField selector="{{AdminProductFormNewAttributeNewSetSection.setName}}" userInput="{{attributeSetName}}" stepKey="fillSetName"/> + <click selector="{{AdminProductFormNewAttributeNewSetSection.accept}}" stepKey="acceptPopup"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingToFinish"/> + <!-- Product page will hang if there is no reload--> + <reloadPage stepKey="reloadPage"/> + <waitForPageLoad stepKey="waitForReload"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageFillTextAttributeValueByNameActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageFillTextAttributeValueByNameActionGroup.xml new file mode 100644 index 0000000000000..336ab90ccec73 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageFillTextAttributeValueByNameActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminProductPageFillTextAttributeValueByNameActionGroup"> + <annotations> + <description>Fills in the Attribute Name field with the provided value.</description> + </annotations> + <arguments> + <argument name="attributeName" type="string"/> + <argument name="value" type="string"/> + </arguments> + + <click stepKey="openSection" selector="{{AdminProductAttributeSection.attributeSectionHeader}}"/> + <fillField stepKey="fillValue" selector="{{AdminProductAttributeSection.textAttributeByName(attributeName)}}" userInput="{{value}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageSelectAttributeSetActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageSelectAttributeSetActionGroup.xml new file mode 100644 index 0000000000000..0b58c65386951 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductPageSelectAttributeSetActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminProductPageSelectAttributeSetActionGroup"> + <annotations> + <description>Selects the provided Attribute Set from the Admin Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="attributeSetName" type="string"/> + </arguments> + + <click stepKey="openDropdown" selector="{{AdminProductFormSection.attributeSet}}"/> + <fillField stepKey="filter" selector="{{AdminProductFormSection.attributeSetFilter}}" userInput="{{attributeSetName}}"/> + <click stepKey="clickResult" selector="{{AdminProductFormSection.attributeSetFilterResult}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSetProductDisabledActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSetProductDisabledActionGroup.xml new file mode 100644 index 0000000000000..a8b413d523ff0 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSetProductDisabledActionGroup.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="AdminSetProductDisabledActionGroup"> + <conditionalClick selector="{{AdminProductFormSection.enableProductLabel}}" dependentSelector="{{AdminProductFormSection.productStatusValue('1')}}" visible="true" stepKey="disableProduct"/> + <seeElement selector="{{AdminProductFormSection.productStatusValue('2')}}" stepKey="assertThatProductSetToDisabled"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSwitchScopeForProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSwitchScopeForProductAttributeActionGroup.xml new file mode 100644 index 0000000000000..bd2414ae6e6c5 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSwitchScopeForProductAttributeActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSwitchScopeForProductAttributeActionGroup"> + <arguments> + <argument name="scope" type="string" defaultValue="1"/> + </arguments> + <click selector="{{AttributePropertiesSection.AdvancedProperties}}" stepKey="expandAdvancedProperties"/> + <waitForElementVisible selector="{{AttributePropertiesSection.Scope}}" stepKey="waitOpenAdvancedProperties"/> + <selectOption selector="{{AttributePropertiesSection.Scope}}" userInput="1" stepKey="selectNecessaryScope"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductStockStatusActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductStockStatusActionGroup.xml new file mode 100644 index 0000000000000..887845b1b51a5 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductStockStatusActionGroup.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="AssertAdminProductStockStatusActionGroup"> + <arguments> + <argument name="productId" type="string"/> + <argument name="stockStatus" type="string"/> + </arguments> + <amOnPage url="{{AdminProductEditPage.url(productId)}}" stepKey="goToProductEditPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <seeOptionIsSelected selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{stockStatus}}" + stepKey="checkProductStatus"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertDiscountsPercentageOfProductsActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertDiscountsPercentageOfProductsActionGroup.xml new file mode 100644 index 0000000000000..e32c423fb09a3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertDiscountsPercentageOfProductsActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertDiscountsPercentageOfProductsActionGroup"> + <annotations> + <description>Validates that the provided Product Tier Price is present and correct.</description> + </annotations> + <arguments> + <argument name="amount" type="string" defaultValue="45"/> + </arguments> + + <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton"/> + <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForCustomerGroupPriceAddButton"/> + <grabValueFrom selector="{{AdminProductFormAdvancedPricingSection.productTierPricePercentageValuePriceInput('0')}}" stepKey="grabProductTierPriceInput"/> + <assertEquals stepKey="assertProductTierPriceInput"> + <expectedResult type="string">{{amount}}</expectedResult> + <actualResult type="string">$grabProductTierPriceInput</actualResult> + </assertEquals> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductAttributeRemovedSuccessfullyActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductAttributeRemovedSuccessfullyActionGroup.xml new file mode 100644 index 0000000000000..60f1e59f9871a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductAttributeRemovedSuccessfullyActionGroup.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="AssertProductAttributeRemovedSuccessfullyActionGroup"> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductDescriptionInProductEditFormActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductDescriptionInProductEditFormActionGroup.xml new file mode 100644 index 0000000000000..f1bca0c36a57e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductDescriptionInProductEditFormActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertProductDescriptionInProductEditFormActionGroup"> + <arguments> + <argument name="productDescription" type="string"/> + </arguments> + <conditionalClick selector="{{AdminProductContentSection.sectionHeader}}" dependentSelector="{{AdminProductContentSection.sectionHeaderShow}}" visible="false" stepKey="expandContentSection"/> + <seeInField selector="{{AdminProductContentSection.descriptionTextArea}}" userInput="{{productDescription}}" stepKey="seeProductDescription"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductImageAdminProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductImageAdminProductPageActionGroup.xml new file mode 100644 index 0000000000000..6ea154d2b01d3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductImageAdminProductPageActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertProductImageAdminProductPageActionGroup"> + <annotations> + <description>Validates that the provided Product Image is present and correct.</description> + </annotations> + <arguments> + <argument name="image" defaultValue="MagentoLogo"/> + </arguments> + + <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductImagesSection"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <seeElement selector="{{AdminProductImagesSection.imageFile(image.filename)}}" stepKey="seeImage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductImageNotInAdminProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductImageNotInAdminProductPageActionGroup.xml new file mode 100644 index 0000000000000..a761f20b4cc5f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductImageNotInAdminProductPageActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertProductImageNotInAdminProductPageActionGroup"> + <annotations> + <description>Validates that the provided Product Image is NOT present.</description> + </annotations> + <arguments> + <argument name="image" defaultValue="MagentoLogo"/> + </arguments> + + <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductImagesSection"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <dontSeeElement selector="{{AdminProductImagesSection.imageFile(image.filename)}}" stepKey="seeImage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductImageNotInStorefrontProductPage2ActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductImageNotInStorefrontProductPage2ActionGroup.xml new file mode 100644 index 0000000000000..4c7fca97a078a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductImageNotInStorefrontProductPage2ActionGroup.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="AssertProductImageNotInStorefrontProductPage2ActionGroup"> + <annotations> + <description>Validates that the provided Product Image is not present.</description> + </annotations> + <arguments> + <argument name="product"/> + <argument name="image" defaultValue="MagentoLogo"/> + </arguments> + + <seeInCurrentUrl url="/{{product.custom_attributes[url_key]}}.html" stepKey="checkUrl"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <dontSeeElement selector="{{StorefrontProductMediaSection.imageFile(image.filename)}}" stepKey="seeImage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductImageNotInStorefrontProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductImageNotInStorefrontProductPageActionGroup.xml new file mode 100644 index 0000000000000..72febb053046e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductImageNotInStorefrontProductPageActionGroup.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="AssertProductImageNotInStorefrontProductPageActionGroup"> + <annotations> + <description>Validates that the provided Product Image is not present.</description> + </annotations> + <arguments> + <argument name="product"/> + <argument name="image" defaultValue="MagentoLogo"/> + </arguments> + + <seeInCurrentUrl url="/{{product.urlKey}}.html" stepKey="checkUrl"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <dontSeeElement selector="{{StorefrontProductMediaSection.imageFile(image.filename)}}" stepKey="seeImage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductImageStorefrontProductPage2ActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductImageStorefrontProductPage2ActionGroup.xml new file mode 100644 index 0000000000000..a4769e675e607 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductImageStorefrontProductPage2ActionGroup.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="AssertProductImageStorefrontProductPage2ActionGroup"> + <annotations> + <description>Validates that the provided Product Image is present.</description> + </annotations> + <arguments> + <argument name="product"/> + <argument name="image" defaultValue="MagentoLogo"/> + </arguments> + + <seeInCurrentUrl url="/{{product.custom_attributes[url_key]}}.html" stepKey="checkUrl"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <seeElement selector="{{StorefrontProductMediaSection.imageFile(image.filename)}}" stepKey="seeImage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductImageStorefrontProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductImageStorefrontProductPageActionGroup.xml new file mode 100644 index 0000000000000..12be0a3d199dc --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductImageStorefrontProductPageActionGroup.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="AssertProductImageStorefrontProductPageActionGroup"> + <annotations> + <description>Validates that the provided Product Image is present.</description> + </annotations> + <arguments> + <argument name="product"/> + <argument name="image" defaultValue="MagentoLogo"/> + </arguments> + + <seeInCurrentUrl url="/{{product.urlKey}}.html" stepKey="checkUrl"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <seeElement selector="{{StorefrontProductMediaSection.imageFile(image.filename)}}" stepKey="seeImage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInStorefrontProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInStorefrontProductPageActionGroup.xml index 8db64ce7c49ac..28ceb82ec6d2e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInStorefrontProductPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductInStorefrontProductPageActionGroup.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="AssertProductInStorefrontProductPage"> + <actionGroup name="AssertProductInStorefrontProductPageActionGroup"> <annotations> <description>Goes to the Storefront page. Validates that the provided Product details are present.</description> </annotations> @@ -24,36 +24,4 @@ <see userInput="{{product.price}}" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="assertProductPrice"/> <see userInput="{{product.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="assertProductSku"/> </actionGroup> - - <actionGroup name="AssertProductNameAndSkuInStorefrontProductPage"> - <annotations> - <description>Goes to the Storefront Product page for the provided Product. Validates that the Product details are present and correct.</description> - </annotations> - <arguments> - <argument name="product"/> - </arguments> - - <!-- Go to storefront product page, assert product name and sku --> - <amOnPage url="{{product.urlKey}}.html" stepKey="navigateToProductPage"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <seeInTitle userInput="{{product.name}}" stepKey="assertProductNameTitle"/> - <see userInput="{{product.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="assertProductName"/> - <see userInput="{{product.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="assertProductSku"/> - </actionGroup> - - <actionGroup name="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey"> - <annotations> - <description>Goes to the Storefront Product page for the provided Product. Validates that the Product details are present and correct.</description> - </annotations> - <arguments> - <argument name="product"/> - </arguments> - - <!-- Go to storefront product page, assert product name and sku --> - <amOnPage url="{{product.custom_attributes[url_key]}}.html" stepKey="navigateToProductPage"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <seeInTitle userInput="{{product.name}}" stepKey="assertProductNameTitle"/> - <see userInput="{{product.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="assertProductName"/> - <see userInput="{{product.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="assertProductSku"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductIsAssignedToWebsiteActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductIsAssignedToWebsiteActionGroup.xml new file mode 100644 index 0000000000000..23937af41ae51 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductIsAssignedToWebsiteActionGroup.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="AssertProductIsAssignedToWebsiteActionGroup" extends="AssertWebsiteIsAvailableInProductWebsitesActionGroup"> + <remove keyForRemoval="seeCheckboxForWebsite"/> + <seeCheckboxIsChecked selector="{{ProductInWebsitesSection.website(website)}}" after="expandProductWebsitesSection" stepKey="seeCustomWebsiteIsChecked"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductIsNotAssignedToWebsiteActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductIsNotAssignedToWebsiteActionGroup.xml new file mode 100644 index 0000000000000..8963d06e9248f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductIsNotAssignedToWebsiteActionGroup.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="AssertProductIsNotAssignedToWebsiteActionGroup" extends="AssertWebsiteIsAvailableInProductWebsitesActionGroup"> + <remove keyForRemoval="seeCheckboxForWebsite"/> + <dontSeeCheckboxIsChecked selector="{{ProductInWebsitesSection.website(website)}}" after="expandProductWebsitesSection" stepKey="seeCustomWebsiteIsNotChecked"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductNameAndSkuInStorefrontProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductNameAndSkuInStorefrontProductPageActionGroup.xml new file mode 100644 index 0000000000000..23b7701010f35 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductNameAndSkuInStorefrontProductPageActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertProductNameAndSkuInStorefrontProductPageActionGroup"> + <annotations> + <description>Goes to the Storefront Product page for the provided Product. Validates that the Product details are present and correct.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <!-- Go to storefront product page, assert product name and sku --> + <amOnPage url="{{product.urlKey}}.html" stepKey="navigateToProductPage"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <seeInTitle userInput="{{product.name}}" stepKey="assertProductNameTitle"/> + <see userInput="{{product.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="assertProductName"/> + <see userInput="{{product.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="assertProductSku"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup.xml new file mode 100644 index 0000000000000..e50e5974023f8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup"> + <annotations> + <description>Goes to the Storefront Product page for the provided Product. Validates that the Product details are present and correct.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <!-- Go to storefront product page, assert product name and sku --> + <amOnPage url="{{product.custom_attributes[url_key]}}.html" stepKey="navigateToProductPage"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <seeInTitle userInput="{{product.name}}" stepKey="assertProductNameTitle"/> + <see userInput="{{product.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="assertProductName"/> + <see userInput="{{product.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="assertProductSku"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductNameInProductEditFormActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductNameInProductEditFormActionGroup.xml new file mode 100644 index 0000000000000..00f940cd4c3c8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductNameInProductEditFormActionGroup.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="AssertProductNameInProductEditFormActionGroup"> + <arguments> + <argument name="productName" type="string"/> + </arguments> + <seeInField selector="{{AdminProductFormSection.productName}}" userInput="{{productName}}" stepKey="seeProductNameOnEditProductPage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnAdminGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnAdminGridActionGroup.xml index 95f1cc2c23a37..a384e993405b9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnAdminGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnAdminGridActionGroup.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="AssertProductOnAdminGridActionGroup" extends="viewProductInAdminGrid"> + <actionGroup name="AssertProductOnAdminGridActionGroup" extends="ViewProductInAdminGridActionGroup"> <annotations> <description>EXTENDS: viewProductInAdminGrid. Removes the 'clickClearFiltersAfter' step from the Action Group.</description> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnCategoryPageActionGroup.xml new file mode 100644 index 0000000000000..d35d6a4778dd6 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertProductOnCategoryPageActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertProductOnCategoryPageActionGroup" extends="StorefrontCheckCategorySimpleProductActionGroup"> + <annotations> + <description>EXTENDS:StorefrontCheckCategorySimpleProduct. Removes 'AssertProductPrice', 'moveMouseOverProduct', 'AssertAddToCart'</description> + </annotations> + <remove keyForRemoval="AssertProductPrice"/> + <remove keyForRemoval="moveMouseOverProduct"/> + <remove keyForRemoval="AssertAddToCart"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertTextInAdminProductRelatedUpSellCrossSellSectionActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertTextInAdminProductRelatedUpSellCrossSellSectionActionGroup.xml new file mode 100644 index 0000000000000..e59f923464250 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertTextInAdminProductRelatedUpSellCrossSellSectionActionGroup.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="AssertTextInAdminProductRelatedUpSellCrossSellSectionActionGroup"> + <annotations> + <description>Validates that provided Text appears in the provided Element on the Admin Product creation/edit page under 'Related Products, Up-Sells, and Cross-Sells' section.</description> + </annotations> + <arguments> + <argument name="element" defaultValue="AdminProductFormRelatedUpSellCrossSellSection.relatedProductSectionText"/> + <argument name="expectedText"/> + </arguments> + + <conditionalClick selector="{{AdminProductFormSection.productFormTab('Related Products')}}" dependentSelector="{{AdminProductFormSection.productFormTabState('Related Products', 'closed')}}" visible="true" stepKey="openTab"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad"/> + <see selector="{{element}}" userInput="{{expectedText}}" stepKey="assertText"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertWebsiteIsAvailableInProductWebsitesActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertWebsiteIsAvailableInProductWebsitesActionGroup.xml new file mode 100644 index 0000000000000..4bd9ce0e09581 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertWebsiteIsAvailableInProductWebsitesActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertWebsiteIsAvailableInProductWebsitesActionGroup"> + <arguments> + <argument name="website" type="string"/> + </arguments> + <scrollTo selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="scrollToProductInWebsitesSection"/> + <conditionalClick selector="{{ProductInWebsitesSection.sectionHeader}}" dependentSelector="{{ProductInWebsitesSection.sectionHeaderOpened}}" visible="false" stepKey="expandProductWebsitesSection"/> + <seeElement selector="{{ProductInWebsitesSection.website(website)}}" stepKey="seeCheckboxForWebsite"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertWebsiteIsNotAvailableInProductWebsitesActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertWebsiteIsNotAvailableInProductWebsitesActionGroup.xml new file mode 100644 index 0000000000000..14eb5a40b184e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertWebsiteIsNotAvailableInProductWebsitesActionGroup.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="AssertWebsiteIsNotAvailableInProductWebsitesActionGroup" extends="AssertWebsiteIsAvailableInProductWebsitesActionGroup"> + <remove keyForRemoval="seeCheckboxForWebsite"/> + <dontSeeElement selector="{{ProductInWebsitesSection.website(website)}}" after="expandProductWebsitesSection" stepKey="dontSeeCheckboxForWebsite"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CategoryPresentActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CategoryPresentActionGroup.xml new file mode 100644 index 0000000000000..76aec793c4eca --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CategoryPresentActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CategoryPresentActionGroup" > + <annotations> + <description>Navigates to category page, asserts category is there. Navigates to storefront category page and asserts category is there. This action group will not work categories where name does NOT equal SEO.</description> + </annotations> + <arguments> + <argument name="categoryName" defaultValue="Test Category" type="string"/> + </arguments> + + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="goToCategoryAdminPage"/> + <waitForPageLoad stepKey="waitForCategoryAdminPageLoad"/> + <see userInput="{{categoryName}}" stepKey="assertCategoryOnAdminPage" selector="{{AdminCategorySidebarTreeSection.treeContainer}}"/> + <amOnPage url="/{{categoryName}}.html" stepKey="goToCustomerFrontPage"/> + <see userInput="{{categoryName}}" stepKey="assertCategoryNameOnStorefront" selector="{{StorefrontCategoryMainSection.CategoryTitle}}"/> + <waitForPageLoad stepKey="waitForCustomerCategoryPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyActionGroup.xml new file mode 100644 index 0000000000000..7107cc2a560d1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyActionGroup.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="ChangeSeoUrlKeyActionGroup"> + <annotations> + <description>Requires navigation to category creation/edit. Updates the Search Engine Optimization.</description> + </annotations> + <arguments> + <argument name="value" type="string"/> + </arguments> + + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSeoSection"/> + <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="{{value}}" stepKey="enterURLKey"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyForSubCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyForSubCategoryActionGroup.xml new file mode 100644 index 0000000000000..42813aef05be5 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyForSubCategoryActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ChangeSeoUrlKeyForSubCategoryActionGroup"> + <annotations> + <description>Requires navigation to subcategory creation/edit. Updates the Search Engine Optimization.</description> + </annotations> + <arguments> + <argument name="value" type="string"/> + </arguments> + + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSeoSection"/> + <uncheckOption selector="{{AdminCategorySEOSection.UrlKeyDefaultValueCheckbox}}" stepKey="uncheckDefaultValue"/> + <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="{{value}}" stepKey="enterURLKey"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeStatusProductUsingProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeStatusProductUsingProductGridActionGroup.xml new file mode 100644 index 0000000000000..b8e8556f53813 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeStatusProductUsingProductGridActionGroup.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ChangeStatusProductUsingProductGridActionGroup"> + <annotations> + <description>Goes to the Admin Products grid page. Changes the Product Status to the provided Status. Validates that the Success Message is present and correct.</description> + </annotations> + <arguments> + <argument name="product"/> + <argument name="status" defaultValue="Enable" type="string"/> + </arguments> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> + <waitForPageLoad time="60" stepKey="waitForPageLoadInitial"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillProductSkuFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'SKU')}}" userInput="{{product.sku}}" stepKey="seeProductSkuInGrid"/> + <click selector="{{AdminProductGridSection.multicheckDropdown}}" stepKey="openMulticheckDropdown"/> + <click selector="{{AdminProductGridSection.multicheckOption('Select All')}}" stepKey="selectAllProductInFilteredGrid"/> + + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Change status')}}" stepKey="clickChangeStatusAction"/> + <click selector="{{AdminProductGridSection.changeStatus('status')}}" stepKey="clickChangeStatusDisabled"/> + <waitForPageLoad stepKey="waitForStatusToBeChanged"/> + <see selector="{{AdminMessagesSection.success}}" userInput="A total of 1 record(s) have been updated." stepKey="seeSuccessMessage"/> + <waitForLoadingMaskToDisappear stepKey="waitForMaskToDisappear"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial2"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeUseForPromoRuleConditionsProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeUseForPromoRuleConditionsProductAttributeActionGroup.xml new file mode 100644 index 0000000000000..5282fe2740fc7 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeUseForPromoRuleConditionsProductAttributeActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ChangeUseForPromoRuleConditionsProductAttributeActionGroup"> + <annotations> + <description>Select the provided value for the 'Use for Promo Rule Conditions' dropdown menu. Clicks on Save. Validates that the Save message is present.</description> + </annotations> + <arguments> + <argument name="option" type="string"/> + </arguments> + + <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="clickStoreFrontPropertiesTab"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <selectOption selector="{{StorefrontPropertiesSection.useForPromoRuleConditions}}" userInput="{{option}}" stepKey="changeOption"/> + <click selector="{{AttributePropertiesSection.Save}}" stepKey="saveAttribute"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the product attribute." stepKey="successMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckAttributeInMoreInformationTabActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckAttributeInMoreInformationTabActionGroup.xml new file mode 100644 index 0000000000000..548d7de79d599 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckAttributeInMoreInformationTabActionGroup.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="CheckAttributeInMoreInformationTabActionGroup"> + <annotations> + <description>Validates that the Product More Information area contains the provided Text.</description> + </annotations> + <arguments> + <argument name="attributeLabel" type="string"/> + <argument name="attributeValue" type="string"/> + </arguments> + + <click selector="{{StorefrontProductMoreInformationSection.moreInformation}}" stepKey="clickTab"/> + <see userInput="{{attributeLabel}}" selector="{{StorefrontProductMoreInformationSection.moreInformationTextArea}}" stepKey="seeAttributeLabel"/> + <see userInput="{{attributeValue}}" selector="{{StorefrontProductMoreInformationSection.moreInformationTextArea}}" stepKey="seeAttributeValue"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckAttributeNotInMoreInformationTabActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckAttributeNotInMoreInformationTabActionGroup.xml new file mode 100644 index 0000000000000..a6d744ebe92be --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckAttributeNotInMoreInformationTabActionGroup.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="CheckAttributeNotInMoreInformationTabActionGroup"> + <annotations> + <description>Validate that the More Information area does not contain the provided Text.</description> + </annotations> + <arguments> + <argument name="attributeLabel" type="string"/> + </arguments> + + <click selector="{{StorefrontProductMoreInformationSection.moreInformation}}" stepKey="clickTab"/> + <dontSee userInput="{{attributeLabel}}" selector="{{StorefrontProductMoreInformationSection.moreInformationTextArea}}" stepKey="seeAttributeLabel"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckCategoryImageInAdminActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckCategoryImageInAdminActionGroup.xml new file mode 100644 index 0000000000000..fd01d4db5babb --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckCategoryImageInAdminActionGroup.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="CheckCategoryImageInAdminActionGroup"> + <annotations> + <description>Requires navigation to the Category creation/edit page. Click on the Upload button. Validates that the Image exists.</description> + </annotations> + <arguments> + <argument name="image" defaultValue="ProductImage"/> + </arguments> + + <conditionalClick selector="{{AdminCategoryContentSection.sectionHeader}}" dependentSelector="{{AdminCategoryContentSection.uploadButton}}" visible="false" stepKey="openContentSection"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <waitForElementVisible selector="{{AdminCategoryContentSection.uploadButton}}" stepKey="seeImageSectionIsReady"/> + <grabTextFrom selector="{{AdminCategoryContentSection.imageFileName}}" stepKey="grabCategoryFileName"/> + <assertRegExp stepKey="assertEquals" message="pass"> + <expectedResult type="string">/magento-logo(_[0-9]+)*?\.png$/</expectedResult> + <actualResult type="variable">grabCategoryFileName</actualResult> + </assertRegExp> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckCategoryNameIsRequiredFieldActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckCategoryNameIsRequiredFieldActionGroup.xml new file mode 100644 index 0000000000000..44e1951a0463b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckCategoryNameIsRequiredFieldActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CheckCategoryNameIsRequiredFieldActionGroup"> + <annotations> + <description>Navigates to category page, attempts to add subcategory without name. Expects required field prompt.</description> + </annotations> + + <seeInCurrentUrl url="{{AdminCategoryPage.url}}" stepKey="seeOnCategoryPage"/> + <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> + <clearField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" stepKey="makeNameFieldEmpty"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <seeInCurrentUrl url="{{AdminCategoryPage.url}}add" stepKey="seeBackOnCreateCategoryPage"/> + <see selector="{{AdminCategoryBasicFieldSection.FieldError('uid')}}" userInput="This is a required field." stepKey="seeErrorMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckCategoryOnStorefrontActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckCategoryOnStorefrontActionGroup.xml new file mode 100644 index 0000000000000..134b3897b5e02 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckCategoryOnStorefrontActionGroup.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="CheckCategoryOnStorefrontActionGroup"> + <annotations> + <description>Navigates to the category page on the storefront and asserts that the title is correct for page and browser.</description> + </annotations> + <arguments> + <argument name="categoryEntity" defaultValue="_defaultCategory"/> + </arguments> + + <amOnPage url="/{{categoryEntity.name_lwr}}.html" stepKey="goToCategoryFrontPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <see selector="{{StorefrontCategoryMainSection.CategoryTitle}}" userInput="{{categoryEntity.name_lwr}}" stepKey="assertCategoryOnStorefront"/> + <seeInTitle userInput="{{categoryEntity.name}}" stepKey="seeCategoryNameInTitle"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckCustomizableOptionImportActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckCustomizableOptionImportActionGroup.xml new file mode 100644 index 0000000000000..efd0986efca06 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckCustomizableOptionImportActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CheckCustomizableOptionImportActionGroup"> + <annotations> + <description>Validate that the Custom Product Option Import value is present and correct.</description> + </annotations> + <arguments> + <argument name="option" defaultValue="ProductOptionField"/> + <argument name="optionIndex" type="string"/> + </arguments> + + <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionTitleInput(optionIndex)}}" stepKey="grabOptionTitle"/> + <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionPrice(optionIndex)}}" stepKey="grabOptionPrice"/> + <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionSku(optionIndex)}}" stepKey="grabOptionSku"/> + <assertEquals expected="{{option.title}}" expectedType="string" actual="$grabOptionTitle" stepKey="assertOptionTitle"/> + <assertEquals expected="{{option.price}}" expectedType="string" actual="$grabOptionPrice" stepKey="assertOptionPrice"/> + <assertEquals expected="{{option.title}}" expectedType="string" actual="$grabOptionSku" stepKey="assertOptionSku"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckProductsOrderActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckProductsOrderActionGroup.xml index 7fbe71cbee301..6f58299fe1446 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckProductsOrderActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckProductsOrderActionGroup.xml @@ -13,12 +13,17 @@ <description>Goes to the Storefront. Validates that the 2 provided Products appear in the correct order.</description> </annotations> <arguments> + <argument name="page" defaultValue="{{StorefrontHomePage.url}}" type="string"/> <argument name="product_1"/> <argument name="product_2"/> </arguments> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> - <waitForPageLoad stepKey="waitForPageLoad5"/> + <amOnPage url="{{page}}" stepKey="goToHomePage"/> + <waitForPageLoad stepKey="waitForPageLoad5" time="60"/> + <executeJS function="window.localStorage.clear()" stepKey="clearWidgetCache" /> + <reloadPage stepKey="goToHomePageAfterCacheCleared"/> + <waitForPageLoad stepKey="waitForPageLoad5AfterCacheCleared" time="60"/> + <waitForElement selector="{{StorefrontCategoryProductSection.ProductImageByNumber('1')}}" time="120" stepKey="waitCompareWidgetLoad" /> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByNumber('1')}}" userInput="alt" stepKey="grabFirstProductName1_1"/> <assertEquals expected="{{product_1.name}}" actual="($grabFirstProductName1_1)" message="notExpectedOrder" stepKey="compare1"/> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByNumber('2')}}" userInput="alt" stepKey="grabFirstProductName2_2"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckRequiredFieldsInProductFormActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckRequiredFieldsInProductFormActionGroup.xml new file mode 100644 index 0000000000000..6aaa66dc70803 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CheckRequiredFieldsInProductFormActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CheckRequiredFieldsInProductFormActionGroup"> + <annotations> + <description>Validates that the 'Required Field' error message is present and correct for the Product Name, SKU and Price fields.</description> + </annotations> + + <clearField selector="{{AdminProductFormSection.productName}}" stepKey="clearProductName"/> + <clearField selector="{{AdminProductFormSection.productSku}}" stepKey="clearProductSku"/> + <clearField selector="{{AdminProductFormSection.productPrice}}" stepKey="clearProductPrice"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Product" stepKey="seeStillOnEditPage"/> + <see selector="{{AdminProductFormSection.fieldError('name')}}" userInput="This is a required field." stepKey="seeNameRequired"/> + <see selector="{{AdminProductFormSection.fieldError('sku')}}" userInput="This is a required field." stepKey="seeSkuRequired"/> + <see selector="{{AdminProductFormSection.priceFieldError}}" userInput="This is a required field." stepKey="seePriceRequired"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ClearProductsFilterActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ClearProductsFilterActionGroup.xml new file mode 100644 index 0000000000000..46960b5e25d51 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ClearProductsFilterActionGroup.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="ClearProductsFilterActionGroup"> + <annotations> + <description>Goto the Product grid page. Clear the Search Filters for the grid.</description> + </annotations> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad time="30" stepKey="waitForProductsPageToLoad"/> + <conditionalClick selector="{{AdminProductFiltersSection.clearFiltersButton}}" dependentSelector="{{AdminProductFiltersSection.clearFiltersButton}}" visible="true" stepKey="cleanFiltersIfTheySet"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ConfirmChangeInputTypeModalActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ConfirmChangeInputTypeModalActionGroup.xml new file mode 100644 index 0000000000000..d207073a01fc0 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ConfirmChangeInputTypeModalActionGroup.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="ConfirmChangeInputTypeModalActionGroup"> + <annotations> + <description>Clicks on the Confirm button for the 'Product Data My Be Lost' modal.</description> + </annotations> + + <waitForElementVisible selector="{{AdminEditProductAttributesSection.ProductDataMayBeLostConfirmButton}}" stepKey="waitForChangeInputTypeButton"/> + <click selector="{{AdminEditProductAttributesSection.ProductDataMayBeLostConfirmButton}}" stepKey="clickChangeInputTypeButton"/> + <waitForElementNotVisible selector="{{AdminEditProductAttributesSection.ProductDataMayBeLostModal}}" stepKey="waitForChangeInputTypeModalGone"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateAttributeDropdownNthOptionActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateAttributeDropdownNthOptionActionGroup.xml new file mode 100644 index 0000000000000..2bf79ca980a44 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateAttributeDropdownNthOptionActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateAttributeDropdownNthOptionActionGroup"> + <annotations> + <description>Creates an Option for a Product Attribute (Attribute Type: Dropdown).</description> + </annotations> + <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> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateAttributeDropdownNthOptionAsDefaultActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateAttributeDropdownNthOptionAsDefaultActionGroup.xml new file mode 100644 index 0000000000000..e9e8a98ac86a6 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateAttributeDropdownNthOptionAsDefaultActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateAttributeDropdownNthOptionAsDefaultActionGroup" extends="CreateAttributeDropdownNthOptionActionGroup"> + <annotations> + <description>EXTENDS: createAttributeDropdownNthOption. Checks the 'Is Default' option.</description> + </annotations> + + <checkOption selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault(row)}}" stepKey="setAsDefault" after="fillStoreView"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateCategoryActionGroup.xml new file mode 100644 index 0000000000000..660b475c02ab0 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateCategoryActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateCategoryActionGroup"> + <annotations> + <description>Requires navigation to the Category creation page. Adds a new Subcategory. Validates that the Category was created.</description> + </annotations> + <arguments> + <argument name="categoryEntity" defaultValue="_defaultCategory"/> + </arguments> + + <seeInCurrentUrl url="{{AdminCategoryPage.url}}" stepKey="seeOnCategoryPage"/> + <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategory"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Category" stepKey="seeCategoryPageTitle"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{categoryEntity.name}}" stepKey="enterCategoryName"/> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSEO"/> + <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="{{categoryEntity.name_lwr}}" stepKey="enterURLKey"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccess"/> + <seeInTitle userInput="{{categoryEntity.name}}" stepKey="seeNewCategoryPageTitle"/> + <seeElement selector="{{AdminCategorySidebarTreeSection.categoryInTree(categoryEntity.name)}}" stepKey="seeCategoryInTree"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateCustomRadioOptionsActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateCustomRadioOptionsActionGroup.xml new file mode 100644 index 0000000000000..8ac87a30afbf6 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateCustomRadioOptionsActionGroup.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateCustomRadioOptionsActionGroup"> + <annotations> + <description>Adds a custom Radio Product Option with 3 Radio Options to a Product based on the provided Options.</description> + </annotations> + <!-- ActionGroup will add a single custom option to a product --> + <!-- You must already be on the product creation page --> + <arguments> + <argument name="customOptionName"/> + <argument name="productOption"/> + <argument name="productOption2"/> + </arguments> + + <click stepKey="clickAddOptions" selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}"/> + <waitForPageLoad stepKey="waitForAddProductPageLoad"/> + + <!-- Fill in the option and select the type of radio (once) --> + <fillField stepKey="fillInOptionTitle" selector="{{AdminProductCustomizableOptionsSection.lastOptionTitle}}" userInput="{{customOptionName}}"/> + <click stepKey="clickOptionTypeParent" selector="{{AdminProductCustomizableOptionsSection.lastOptionTypeParent}}"/> + <waitForPageLoad stepKey="waitForDropdownOpen"/> + <click stepKey="clickOptionType" selector="{{AdminProductCustomizableOptionsSection.optionType('Radio Buttons')}}"/> + + <!-- Add three radio options based on the parameter --> + <click stepKey="clickAddValue" selector="{{AdminProductCustomizableOptionsSection.addValue}}"/> + + <fillField stepKey="fillInValueTitle" selector="{{AdminProductCustomizableOptionsSection.valueTitle}}" userInput="{{productOption.title}}"/> + <fillField stepKey="fillInValuePrice" selector="{{AdminProductCustomizableOptionsSection.valuePrice}}" userInput="{{productOption.price}}"/> + + <click stepKey="clickAddValue2" selector="{{AdminProductCustomizableOptionsSection.addValue}}"/> + + <fillField stepKey="fillInValueTitle2" selector="{{AdminProductCustomizableOptionsSection.valueTitle}}" userInput="{{productOption2.title}}"/> + <fillField stepKey="fillInValuePrice2" selector="{{AdminProductCustomizableOptionsSection.valuePrice}}" userInput="{{productOption2.price}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateDefaultAttributeSetActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateDefaultAttributeSetActionGroup.xml new file mode 100644 index 0000000000000..8b95a77465b01 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateDefaultAttributeSetActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateDefaultAttributeSetActionGroup"> + <annotations> + <description>Goes to the Attribute Sets grid page. Clicks on Add. Fill Name. Clicks on Save.</description> + </annotations> + <arguments> + <argument name="label" type="string"/> + </arguments> + + <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/> + <waitForPageLoad stepKey="wait1"/> + <click selector="{{AdminProductAttributeSetGridSection.addAttributeSetBtn}}" stepKey="clickAddAttributeSet"/> + <fillField selector="{{AdminProductAttributeSetSection.name}}" userInput="{{label}}" stepKey="fillName"/> + <click selector="{{AdminProductAttributeSetSection.saveBtn}}" stepKey="clickSave1"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateProductAttributeActionGroup.xml new file mode 100644 index 0000000000000..0a0c95fdb33cc --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateProductAttributeActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateProductAttributeActionGroup"> + <annotations> + <description>Clicks on 'Add New Attribute'. Fills in the Attribute Details (Label, Input Type and Value Required). Clicks on Save.</description> + </annotations> + <arguments> + <argument name="attribute" type="entity" defaultValue="productAttributeWysiwyg"/> + </arguments> + + <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}}"/> + <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateProductAttributeWithDateFieldActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateProductAttributeWithDateFieldActionGroup.xml new file mode 100644 index 0000000000000..13b1bca0ad40b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateProductAttributeWithDateFieldActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateProductAttributeWithDateFieldActionGroup" extends="CreateProductAttributeActionGroup" insertAfter="checkRequired"> + <annotations> + <description>EXTENDS: createProductAttribute. Fills in the Attribute Code and Default Value (Attribute Type: Date Field).</description> + </annotations> + <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> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateProductAttributeWithDatetimeFieldActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateProductAttributeWithDatetimeFieldActionGroup.xml new file mode 100644 index 0000000000000..a4f0d22f6edb5 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateProductAttributeWithDatetimeFieldActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateProductAttributeWithDatetimeFieldActionGroup" extends="CreateProductAttributeActionGroup" insertAfter="checkRequired"> + <annotations> + <description>EXTENDS: createProductAttribute. Fills in the Attribute Code and Default Value (Attribute Type: Date and Time Field).</description> + </annotations> + <arguments> + <argument name="date" type="string"/> + </arguments> + + <scrollTo selector="{{AdvancedAttributePropertiesSection.AdvancedAttributePropertiesSectionToggle}}" stepKey="scrollToAdvancedSection"/> + <conditionalClick selector="{{AdvancedAttributePropertiesSection.AdvancedAttributePropertiesSectionToggle}}" dependentSelector="{{AdvancedAttributePropertiesSection.AttributeCode}}" visible="false" stepKey="openAdvancedSection"/> + <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" stepKey="waitForSlideOutAdvancedSection"/> + <fillField selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{attribute.attribute_code}}" stepKey="fillCode"/> + <scrollTo selector="{{AdvancedAttributePropertiesSection.defaultValueDatetime}}" stepKey="scrollToDefaultField"/> + <fillField selector="{{AdvancedAttributePropertiesSection.defaultValueDatetime}}" userInput="{{date}}" stepKey="fillDefaultValue"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateProductAttributeWithTextFieldActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateProductAttributeWithTextFieldActionGroup.xml new file mode 100644 index 0000000000000..fccb8ad2413f8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateProductAttributeWithTextFieldActionGroup.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="CreateProductAttributeWithTextFieldActionGroup" extends="CreateProductAttributeActionGroup" insertAfter="checkRequired"> + <annotations> + <description>EXTENDS: createProductAttribute. Fills in the Attribute Code and Default Value (Attribute Type: Text Field).</description> + </annotations> + + <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> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateSimpleProductAndAddToWebsiteActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateSimpleProductAndAddToWebsiteActionGroup.xml new file mode 100644 index 0000000000000..97c7072abe42f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreateSimpleProductAndAddToWebsiteActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateSimpleProductAndAddToWebsiteActionGroup"> + <annotations> + <description>Goes to the Admin Product grid page. Clicks on Add Product. Fills the provided Product Details (Name, SKU, Price, Quantity and Website). Clicks on Save.</description> + </annotations> + <arguments> + <argument name="product"/> + <argument name="website" type="string"/> + </arguments> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToCatalogProductGrid"/> + <waitForPageLoad stepKey="waitForProductGrid"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> + <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> + <fillField userInput="{{product.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillProductName"/> + <fillField userInput="{{product.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillProductSKU"/> + <fillField userInput="{{product.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillProductPrice"/> + <fillField userInput="{{product.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillProductQuantity"/> + <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="openProductInWebsites"/> + <click selector="{{ProductInWebsitesSection.website(website)}}" stepKey="selectWebsite"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> + <waitForLoadingMaskToDisappear stepKey="waitForProductPageSave"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreatedProductConnectToWebsiteActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreatedProductConnectToWebsiteActionGroup.xml new file mode 100644 index 0000000000000..d605aa1910134 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CreatedProductConnectToWebsiteActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreatedProductConnectToWebsiteActionGroup"> + <annotations> + <description>Clicks on 'Edit' for the provided Product. Clicks on the provided Website. Clicks on Save.</description> + </annotations> + <arguments> + <argument name="website"/> + <argument name="product"/> + </arguments> + + <click stepKey="openProduct" selector="{{AdminProductGridActionSection.productName(product.sku)}}"/> + <waitForPageLoad stepKey="waitForProductPage"/> + <scrollTo selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="ScrollToWebsites"/> + <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="openWebsitesList"/> + <waitForPageLoad stepKey="waitForWebsitesList"/> + <click selector="{{ProductInWebsitesSection.website(website.name)}}" stepKey="SelectWebsite"/> + <click selector="{{AdminProductFormAdvancedPricingSection.save}}" stepKey="clickSaveProduct"/> + <waitForPageLoad stepKey="waitForSave"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml deleted file mode 100644 index 753f06cd75e3e..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml +++ /dev/null @@ -1,172 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="CreateCustomRadioOptions"> - <annotations> - <description>Adds a custom Radio Product Option with 3 Radio Options to a Product based on the provided Options.</description> - </annotations> - <!-- ActionGroup will add a single custom option to a product --> - <!-- You must already be on the product creation page --> - <arguments> - <argument name="customOptionName"/> - <argument name="productOption"/> - <argument name="productOption2"/> - </arguments> - - <click stepKey="clickAddOptions" selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}"/> - <waitForPageLoad stepKey="waitForAddProductPageLoad"/> - - <!-- Fill in the option and select the type of radio (once) --> - <fillField stepKey="fillInOptionTitle" selector="{{AdminProductCustomizableOptionsSection.lastOptionTitle}}" userInput="{{customOptionName}}"/> - <click stepKey="clickOptionTypeParent" selector="{{AdminProductCustomizableOptionsSection.lastOptionTypeParent}}"/> - <waitForPageLoad stepKey="waitForDropdownOpen"/> - <click stepKey="clickOptionType" selector="{{AdminProductCustomizableOptionsSection.optionType('Radio Buttons')}}"/> - - <!-- Add three radio options based on the parameter --> - <click stepKey="clickAddValue" selector="{{AdminProductCustomizableOptionsSection.addValue}}"/> - - <fillField stepKey="fillInValueTitle" selector="{{AdminProductCustomizableOptionsSection.valueTitle}}" userInput="{{productOption.title}}"/> - <fillField stepKey="fillInValuePrice" selector="{{AdminProductCustomizableOptionsSection.valuePrice}}" userInput="{{productOption.price}}"/> - - <click stepKey="clickAddValue2" selector="{{AdminProductCustomizableOptionsSection.addValue}}"/> - - <fillField stepKey="fillInValueTitle2" selector="{{AdminProductCustomizableOptionsSection.valueTitle}}" userInput="{{productOption2.title}}"/> - <fillField stepKey="fillInValuePrice2" selector="{{AdminProductCustomizableOptionsSection.valuePrice}}" userInput="{{productOption2.price}}"/> - </actionGroup> - - <!--Add a custom option of type "file" to a product--> - <actionGroup name="AddProductCustomOptionFile"> - <annotations> - <description>Add a custom File Product Option to a Product based on the provided File.</description> - </annotations> - <arguments> - <argument name="option" defaultValue="ProductOptionFile"/> - </arguments> - - <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" visible="false" stepKey="openCustomOptionSection"/> - <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOption"/> - <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.lastOptionTitle}}" stepKey="waitForOption"/> - <fillField selector="{{AdminProductCustomizableOptionsSection.lastOptionTitle}}" userInput="{{option.title}}" stepKey="fillTitle"/> - <click selector="{{AdminProductCustomizableOptionsSection.lastOptionTypeParent}}" stepKey="openTypeSelect"/> - <click selector="{{AdminProductCustomizableOptionsSection.optionType('File')}}" stepKey="selectTypeFile"/> - <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.optionPriceByTitle(option.title)}}" stepKey="waitForElements"/> - <fillField selector="{{AdminProductCustomizableOptionsSection.optionPriceByTitle(option.title)}}" userInput="{{option.price}}" stepKey="fillPrice"/> - <selectOption selector="{{AdminProductCustomizableOptionsSection.optionPriceTypeByTitle(option.title)}}" userInput="{{option.price_type}}" stepKey="selectPriceType"/> - <fillField selector="{{AdminProductCustomizableOptionsSection.optionFileExtensionByTitle(option.title)}}" userInput="{{option.file_extension}}" stepKey="fillCompatibleExtensions"/> - </actionGroup> - - <actionGroup name="AddProductCustomOptionField"> - <annotations> - <description>Add a custom Field Product Option to a Product based on the provided Option.</description> - </annotations> - <arguments> - <argument name="option" defaultValue="ProductOptionField"/> - </arguments> - - <scrollTo selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" stepKey="scrollToAddButtonOption"/> - <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" visible="false" stepKey="openCustomOptionSection"/> - <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOption"/> - <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.lastOptionTitle}}" stepKey="waitForOption"/> - <fillField selector="{{AdminProductCustomizableOptionsSection.lastOptionTitle}}" userInput="{{option.title}}" stepKey="fillTitle"/> - <click selector="{{AdminProductCustomizableOptionsSection.lastOptionTypeParent}}" stepKey="openTypeSelect"/> - <click selector="{{AdminProductCustomizableOptionsSection.optionType('Field')}}" stepKey="selectTypeFile"/> - <waitForElementVisible selector="{{AdminProductCustomizableOptionsSection.optionPriceByTitle(option.title)}}" stepKey="waitForElements"/> - <fillField selector="{{AdminProductCustomizableOptionsSection.optionPriceByTitle(option.title)}}" userInput="{{option.price}}" stepKey="fillPrice"/> - <selectOption selector="{{AdminProductCustomizableOptionsSection.optionPriceTypeByTitle(option.title)}}" userInput="{{option.price_type}}" stepKey="selectPriceType"/> - <fillField selector="{{AdminProductCustomizableOptionsSection.optionSkuByTitle(option.title)}}" userInput="{{option.title}}" stepKey="fillSku"/> - </actionGroup> - - <actionGroup name="importProductCustomizableOptions"> - <annotations> - <description>Import custom Product Options for the provided Product Name.</description> - </annotations> - <arguments> - <argument name="productName" type="string"/> - </arguments> - - <click selector="{{AdminProductCustomizableOptionsSection.importOptions}}" stepKey="clickImportOptions"/> - <waitForElementVisible selector="{{AdminProductImportOptionsSection.selectProductTitle}}" stepKey="waitForTitleVisible"/> - <conditionalClick selector="{{AdminProductImportOptionsSection.resetFiltersButton}}" dependentSelector="{{AdminProductImportOptionsSection.resetFiltersButton}}" visible="true" stepKey="clickResetFilters"/> - <click selector="{{AdminProductImportOptionsSection.filterButton}}" stepKey="clickFilterButton"/> - <waitForElementVisible selector="{{AdminProductImportOptionsSection.nameField}}" stepKey="waitForNameField"/> - <fillField selector="{{AdminProductImportOptionsSection.nameField}}" userInput="{{productName}}" stepKey="fillProductName"/> - <click selector="{{AdminProductImportOptionsSection.applyFiltersButton}}" stepKey="clickApplyFilters"/> - <checkOption selector="{{AdminProductImportOptionsSection.firstRowItemCheckbox}}" stepKey="checkProductCheckbox"/> - <click selector="{{AdminProductImportOptionsSection.importButton}}" stepKey="clickImport"/> - </actionGroup> - - <actionGroup name="resetImportOptionFilter"> - <annotations> - <description>Click on the Reset Filters button for the Import Options filters on the Product grid page.</description> - </annotations> - - <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" visible="false" stepKey="openCustomOptionSection"/> - <click selector="{{AdminProductCustomizableOptionsSection.importOptions}}" stepKey="clickImportOptions"/> - <click selector="{{AdminProductImportOptionsSection.resetFiltersButton}}" stepKey="clickResetFilterButton"/> - </actionGroup> - - <actionGroup name="checkCustomizableOptionImport"> - <annotations> - <description>Validate that the Custom Product Option Import value is present and correct.</description> - </annotations> - <arguments> - <argument name="option" defaultValue="ProductOptionField"/> - <argument name="optionIndex" type="string"/> - </arguments> - - <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionTitleInput(optionIndex)}}" stepKey="grabOptionTitle"/> - <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionPrice(optionIndex)}}" stepKey="grabOptionPrice"/> - <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionSku(optionIndex)}}" stepKey="grabOptionSku"/> - <assertEquals expected="{{option.title}}" expectedType="string" actual="$grabOptionTitle" stepKey="assertOptionTitle"/> - <assertEquals expected="{{option.price}}" expectedType="string" actual="$grabOptionPrice" stepKey="assertOptionPrice"/> - <assertEquals expected="{{option.title}}" expectedType="string" actual="$grabOptionSku" stepKey="assertOptionSku"/> - </actionGroup> - <!-- Assumes we are on product edit page --> - <actionGroup name="AdminDeleteAllProductCustomOptions"> - <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="expandContentTab"/> - <waitForPageLoad time="10" stepKey="waitCustomizableOptionsTabOpened"/> - <executeInSelenium function="function($webdriver) use ($I) { - $buttons = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::cssSelector('[data-index=\'options\'] [data-index=\'delete_button\']')); - while(!empty($buttons)) { - $button = reset($buttons); - $I->executeJS('arguments[0].scrollIntoView(false)', [$button]); - $button->click(); - $webdriver->wait()->until(\Facebook\WebDriver\WebDriverExpectedCondition::stalenessOf($button)); - $buttons = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::cssSelector('[data-index=\'options\'] [data-index=\'delete_button\']')); - } - }" stepKey="deleteCustomOptions"/> - <dontSeeElement selector="{{AdminProductCustomizableOptionsSection.customOptionButtonDelete}}" stepKey="assertNoCustomOptions"/> - </actionGroup> - <!-- Assumes we are on product edit page --> - <actionGroup name="AdminAssertProductHasNoCustomOptions"> - <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="expandContentTab"/> - <waitForPageLoad time="10" stepKey="waitCustomizableOptionsTabOpened"/> - <dontSeeElement selector="{{AdminProductCustomizableOptionsSection.customOption}}" stepKey="assertNoCustomOptions"/> - </actionGroup> - <!-- Assumes we are on product edit page --> - <actionGroup name="AdminAssertProductHasNoCustomOption" extends="AdminAssertProductCustomOptionVisible"> - <remove keyForRemoval="assertCustomOptionVisible"/> - <dontSeeElement selector="{{AdminProductCustomizableOptionsSection.fillOptionTitle(option.title)}}" after="waitCustomizableOptionsTabOpened" stepKey="assertNoCustomOption"/> - </actionGroup> - <!-- Assumes we are on product edit page --> - <actionGroup name="AdminAssertProductCustomOptionVisible"> - <arguments> - <argument name="option" defaultValue="ProductOptionField"/> - </arguments> - <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" visible="true" stepKey="expandContentTab"/> - <waitForPageLoad time="10" stepKey="waitCustomizableOptionsTabOpened"/> - <seeElement selector="{{AdminProductCustomizableOptionsSection.fillOptionTitle(option.title)}}" stepKey="assertCustomOptionVisible"/> - </actionGroup> - <!-- Assumes we are on product edit page --> - <actionGroup name="AdminDeleteProductCustomOption" extends="AdminAssertProductCustomOptionVisible"> - <remove keyForRemoval="assertCustomOptionVisible"/> - <click selector="{{AdminProductCustomizableOptionsSection.deleteCustomOptions(option.title)}}" after="waitCustomizableOptionsTabOpened" stepKey="clickDeleteCustomOption"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteAllDuplicateProductUsingProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteAllDuplicateProductUsingProductGridActionGroup.xml new file mode 100644 index 0000000000000..8f057052d3e2c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteAllDuplicateProductUsingProductGridActionGroup.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="DeleteAllDuplicateProductUsingProductGridActionGroup" extends="DeleteProductUsingProductGridActionGroup"> + <annotations> + <description>EXTENDS: DeleteProductUsingProductGridActionGroup. Removes 'seeProductSkuInGrid'.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <remove keyForRemoval="seeProductSkuInGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteAllProductsUsingProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteAllProductsUsingProductGridActionGroup.xml new file mode 100644 index 0000000000000..3609d992a9f0b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteAllProductsUsingProductGridActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteAllProductsUsingProductGridActionGroup"> + <annotations> + <description>Deletes all products in Admin Products grid page.</description> + </annotations> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="openAdminGridProductsPage"/> + <waitForPageLoad time="60" stepKey="waitForPageFullyLoad"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clearGridFilters"/> + + <conditionalClick selector="{{AdminProductGridSection.multicheckDropdown}}" dependentSelector="{{AdminDataGridTableSection.dataGridEmpty}}" visible="false" stepKey="openMulticheckDropdown"/> + <conditionalClick selector="{{AdminProductGridSection.multicheckOption('Select All')}}" dependentSelector="{{AdminDataGridTableSection.dataGridEmpty}}" visible="false" stepKey="selectAllProductsInGrid"/> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Delete')}}" stepKey="clickDeleteAction"/> + + <waitForElementVisible selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForConfirmModal"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> + <waitForElementVisible selector="{{AdminDataGridTableSection.dataGridEmpty}}" stepKey="waitGridIsEmpty"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteAttributeSetByLabelActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteAttributeSetByLabelActionGroup.xml new file mode 100644 index 0000000000000..c0586367fcaf6 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteAttributeSetByLabelActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteAttributeSetByLabelActionGroup"> + <annotations> + <description>Deletes the provided Attribute Set Name from the Attribute Sets grid page.</description> + </annotations> + <arguments> + <argument name="label" type="string"/> + </arguments> + + <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/> + <waitForPageLoad stepKey="waitForAttributeSetPageLoad"/> + <fillField selector="{{AdminProductAttributeSetGridSection.filter}}" userInput="{{label}}" stepKey="filterByName"/> + <click selector="{{AdminProductAttributeSetGridSection.searchBtn}}" stepKey="clickSearch"/> + <click selector="{{AdminProductAttributeSetGridSection.nthRow('1')}}" stepKey="clickFirstRow"/> + <waitForPageLoad stepKey="waitForClick"/> + <click selector="{{AdminProductAttributeSetSection.deleteBtn}}" stepKey="clickDelete"/> + <click selector="{{AdminProductAttributeSetSection.modalOk}}" stepKey="confirmDelete"/> + <waitForPageLoad stepKey="waitForDeleteToFinish"/> + <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="The attribute set has been removed." stepKey="seeDeleteMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteCategoryActionGroup.xml new file mode 100644 index 0000000000000..a84e92fcbb0f5 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteCategoryActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteCategoryActionGroup"> + <annotations> + <description>Navigates to the category page and deletes the specified category.</description> + </annotations> + <arguments> + <argument name="categoryEntity" defaultValue="_defaultCategory"/> + </arguments> + + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="goToCategoryPage"/> + <waitForPageLoad time="60" stepKey="waitForCategoryPageLoad"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(categoryEntity.name)}}" stepKey="clickCategoryLink"/> + <click selector="{{AdminCategoryMainActionsSection.DeleteButton}}" stepKey="clickDelete"/> + <waitForElementVisible selector="{{AdminCategoryModalSection.message}}" stepKey="waitForConfirmationModal"/> + <see selector="{{AdminCategoryModalSection.message}}" userInput="Are you sure you want to delete this category?" stepKey="seeDeleteConfirmationMessage"/> + <click selector="{{AdminCategoryModalSection.ok}}" stepKey="confirmDelete"/> + <waitForPageLoad time="60" stepKey="waitForDeleteToFinish"/> + <see selector="You deleted the category." stepKey="seeDeleteSuccess"/> + <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="expandToSeeAllCategories"/> + <dontSee selector="{{AdminCategorySidebarTreeSection.categoryInTree(categoryEntity.name)}}" stepKey="dontSeeCategoryInTree"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteDefaultCategoryChildrenActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteDefaultCategoryChildrenActionGroup.xml new file mode 100644 index 0000000000000..2fb4e0e05887a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteDefaultCategoryChildrenActionGroup.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteDefaultCategoryChildrenActionGroup"> + <annotations> + <description>Deletes all children categories of Default Root Category.</description> + </annotations> + + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToAdminCategoryPage"/> + <executeInSelenium function="function ($webdriver) use ($I) { + $children = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::xpath('//ul[contains(@class, \'x-tree-node-ct\')]/li[@class=\'x-tree-node\' and contains(., + \'{{DefaultCategory.name}}\')]/ul[contains(@class, \'x-tree-node-ct\')]/li//a')); + while (!empty($children)) { + $I->click('//ul[contains(@class, \'x-tree-node-ct\')]/li[@class=\'x-tree-node\' and contains(., + \'{{DefaultCategory.name}}\')]/ul[contains(@class, \'x-tree-node-ct\')]/li//a'); + $I->waitForPageLoad(30); + $I->click('#delete'); + $I->waitForElementVisible('aside.confirm .modal-footer button.action-accept'); + $I->click('aside.confirm .modal-footer button.action-accept'); + $I->waitForPageLoad(30); + $I->waitForElementVisible('#messages div.message-success', 30); + $I->see('You deleted the category.', '#messages div.message-success'); + $children = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::xpath('//ul[contains(@class, \'x-tree-node-ct\')]/li[@class=\'x-tree-node\' and contains(., + \'{{DefaultCategory.name}}\')]/ul[contains(@class, \'x-tree-node-ct\')]/li//a')); + } + }" stepKey="deleteAllChildCategories"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteMostRecentCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteMostRecentCategoryActionGroup.xml new file mode 100644 index 0000000000000..8b47b6375c21c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteMostRecentCategoryActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteMostRecentCategoryActionGroup"> + <annotations> + <description>Actions to delete the category last made (the last category on the list).</description> + </annotations> + + <amOnPage url="/{{AdminCategoryPage.url}}" stepKey="goToCategoryFrontPage"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + <click stepKey="goToCreateCategory" selector="{{AdminCategorySidebarTreeSection.lastCreatedCategory}}"/> + <waitForPageLoad stepKey="waitForCreatedCategoryPageLoad"/> + <click stepKey="clickDeleteCategory" selector="{{AdminCategoryMainActionsSection.DeleteButton}}"/> + <waitForPageLoad stepKey="waitForModalVisible"/> + <click stepKey="clickOkToDelete" selector="{{AdminCategoryModalSection.ok}}"/> + <waitForPageLoad stepKey="waitForModalNotVisible"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeActionGroup.xml new file mode 100644 index 0000000000000..8ea0e43d0307f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeActionGroup.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="DeleteProductAttributeActionGroup" extends="NavigateToCreatedProductAttributeActionGroup"> + <annotations> + <description>EXTENDS: navigateToCreatedProductAttribute. Deletes the Product Attribute. Validates that the success message is present.</description> + </annotations> + + <click selector="{{AttributePropertiesSection.DeleteAttribute}}" stepKey="deleteAttribute"/> + <click selector="{{ModalConfirmationSection.OkButton}}" stepKey="ClickOnDeleteButton"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeByAttributeCodeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeByAttributeCodeActionGroup.xml index 66d4378872605..ddba6649161ef 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeByAttributeCodeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeByAttributeCodeActionGroup.xml @@ -15,7 +15,7 @@ <arguments> <argument name="productAttributeCode" type="string"/> </arguments> - + <waitForPageLoad stepKey="waitForViewAdminProductAttributeLoad" time="30"/> <click selector="{{AttributePropertiesSection.DeleteAttribute}}" stepKey="deleteAttribute"/> <click selector="{{ModalConfirmationSection.OkButton}}" stepKey="clickOnConfirmOk"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeByLabelActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeByLabelActionGroup.xml new file mode 100644 index 0000000000000..fb78909eab0b6 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeByLabelActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteProductAttributeByLabelActionGroup"> + <annotations> + <description>Goes to the Admin Product Attributes grid page. Filters the grid for the provided Product Attribute (Label). Deletes the Product Attribute from the grid. Validates that the Success Message is present.</description> + </annotations> + <arguments> + <argument name="ProductAttribute"/> + </arguments> + + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> + <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="{{ProductAttribute.default_label}}" stepKey="setAttributeCode"/> + <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromTheGrid"/> + <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="clickOnAttributeRow"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <click selector="{{AttributePropertiesSection.DeleteAttribute}}" stepKey="deleteAttribute"/> + <click selector="{{ModalConfirmationSection.OkButton}}" stepKey="ClickOnDeleteButton"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductByNameActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductByNameActionGroup.xml new file mode 100644 index 0000000000000..3a4af44158497 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductByNameActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteProductByNameActionGroup" extends="DeleteProductBySkuActionGroup"> + <annotations> + <description>EXTENDS: DeleteProductBySkuActionGroup. Deletes the provided Product Name.</description> + </annotations> + <arguments> + <argument name="sku" type="string" defaultValue=""/> + <argument name="name" type="string"/> + </arguments> + + <remove keyForRemoval="fillProductSkuFilter"/> + <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{name}}" stepKey="fillProductSkuFilter" after="openProductFilters"/> + <remove keyForRemoval="seeProductSkuInGrid"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="{{name}}" stepKey="seeProductNameInGrid" after="clickApplyFilters"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductBySkuActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductBySkuActionGroup.xml new file mode 100644 index 0000000000000..c1f22920ceb99 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductBySkuActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteProductBySkuActionGroup"> + <annotations> + <description>Goes to the Admin Products grid page. Deletes the provided Product SKU.</description> + </annotations> + <arguments> + <argument name="sku" type="string"/> + </arguments> + + <!--TODO use other action group for filtering grid when MQE-539 is implemented --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{sku}}" stepKey="fillProductSkuFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'SKU')}}" userInput="{{sku}}" stepKey="seeProductSkuInGrid"/> + <click selector="{{AdminProductGridSection.multicheckDropdown}}" stepKey="openMulticheckDropdown"/> + <click selector="{{AdminProductGridSection.multicheckOption('Select All')}}" stepKey="selectAllProductInFilteredGrid"/> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Delete')}}" stepKey="clickDeleteAction"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForConfirmModal"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmProductDelete"/> + <see selector="{{AdminMessagesSection.success}}" userInput="record(s) have been deleted." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductUsingProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductUsingProductGridActionGroup.xml new file mode 100644 index 0000000000000..95ad1d743690e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductUsingProductGridActionGroup.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteProductUsingProductGridActionGroup"> + <annotations> + <description>Deletes the provided Product from the Admin Products grid page.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <!--TODO use other action group for filtering grid when MQE-539 is implemented --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> + <waitForPageLoad time="60" stepKey="waitForPageLoadInitial"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillProductSkuFilter"/> + <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{product.name}}" stepKey="fillProductNameFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'SKU')}}" userInput="{{product.sku}}" stepKey="seeProductSkuInGrid"/> + <click selector="{{AdminProductGridSection.multicheckDropdown}}" stepKey="openMulticheckDropdown"/> + <click selector="{{AdminProductGridSection.multicheckOption('Select All')}}" stepKey="selectAllProductInFilteredGrid"/> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Delete')}}" stepKey="clickDeleteAction"/> + <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/> + <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmProductDelete"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductsByKeywordActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductsByKeywordActionGroup.xml new file mode 100644 index 0000000000000..6b181d5f93be2 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductsByKeywordActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteProductsByKeywordActionGroup"> + <annotations> + <description>Delete products by keyword</description> + </annotations> + <arguments> + <argument name="keyword" type="string"/> + </arguments> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="{{keyword}}" stepKey="fillKeywordField"/> + <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="keywordSearchButton"/> + <click selector="{{AdminProductGridSection.multicheckDropdown}}" stepKey="openMulticheckDropdown"/> + <click selector="{{AdminProductGridSection.multicheckOption('Select All')}}" stepKey="selectAllProductInFilteredGrid"/> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Delete')}}" stepKey="clickDeleteAction"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForConfirmModal"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmProductDelete"/> + <see selector="{{AdminMessagesSection.success}}" userInput="record(s) have been deleted." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductsIfTheyExistActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductsIfTheyExistActionGroup.xml new file mode 100644 index 0000000000000..3ef0de8b27a65 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductsIfTheyExistActionGroup.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="DeleteProductsIfTheyExistActionGroup"> + <annotations> + <description>Deletes all Products in the Admin Products grid.</description> + </annotations> + + <conditionalClick selector="{{AdminProductGridSection.multicheckDropdown}}" dependentSelector="{{AdminProductGridSection.firstProductRow}}" visible="true" stepKey="openMulticheckDropdown"/> + <conditionalClick selector="{{AdminProductGridSection.multicheckOption('Select All')}}" dependentSelector="{{AdminProductGridSection.firstProductRow}}" visible="true" stepKey="selectAllProductInFilteredGrid"/> + <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> + <click selector="{{AdminProductGridSection.bulkActionOption('Delete')}}" stepKey="clickDeleteAction"/> + <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="waitForModalPopUp"/> + <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmProductDelete"/> + <waitForPageLoad stepKey="waitForGridLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ExpandAdminProductSectionActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ExpandAdminProductSectionActionGroup.xml new file mode 100644 index 0000000000000..a181cce591e09 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ExpandAdminProductSectionActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ExpandAdminProductSectionActionGroup"> + <annotations> + <description>Expand the provided Section Selector based on the provided dependant Section Selector.</description> + </annotations> + <arguments> + <argument name="sectionSelector" defaultValue="{{AdminProductContentSection.sectionHeader}}" type="string"/> + <argument name="sectionDependentSelector" defaultValue="{{AdminProductContentSection.sectionHeaderShow}}" type="string"/> + </arguments> + + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> + <waitForElementVisible time="30" selector="{{sectionSelector}}" stepKey="waitForSection"/> + <conditionalClick selector="{{sectionSelector}}" dependentSelector="{{sectionDependentSelector}}" visible="false" stepKey="expandSection"/> + <waitForPageLoad time="30" stepKey="waitForSectionToExpand"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillAdminSimpleProductFormActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillAdminSimpleProductFormActionGroup.xml new file mode 100644 index 0000000000000..2abfc546e6bb3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillAdminSimpleProductFormActionGroup.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FillAdminSimpleProductFormActionGroup"> + <annotations> + <description>Goes to the Admin Product grid page. Clicks on Add. Fills the provided Product details (Name, SKU, Price, Quantity, Category and URL). Clicks on Save. Validates that the Product details are present and correct.</description> + </annotations> + <arguments> + <argument name="category"/> + <argument name="simpleProduct"/> + </arguments> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> + <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> + <fillField userInput="{{simpleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> + <fillField userInput="{{simpleProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> + <fillField userInput="{{simpleProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> + <fillField userInput="{{simpleProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{category.name}}]" stepKey="searchAndSelectCategory"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertSaveMessageSuccess"/> + <seeInField userInput="{{simpleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="assertFieldName"/> + <seeInField userInput="{{simpleProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="assertFieldSku"/> + <seeInField userInput="{{simpleProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="assertFieldPrice"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSectionAssert"/> + <seeInField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="assertFieldUrlKey"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillCategoryFormActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillCategoryFormActionGroup.xml new file mode 100644 index 0000000000000..37989ed9ddc44 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillCategoryFormActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FillCategoryFormActionGroup"> + <annotations> + <description>Requires navigation to the Subcategory creation/edit page. Fills the Subcategory Name. Fills the Search Engine Optimization.</description> + </annotations> + <arguments> + <argument name="categoryEntity" defaultValue="_defaultCategory"/> + </arguments> + + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{categoryEntity.name}}" stepKey="enterCategoryName"/> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSEO"/> + <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="{{categoryEntity.name_lwr}}" stepKey="enterURLKey"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillCategoryNameAndUrlKeyAndSaveActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillCategoryNameAndUrlKeyAndSaveActionGroup.xml new file mode 100644 index 0000000000000..bd62dd9e83e55 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillCategoryNameAndUrlKeyAndSaveActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FillCategoryNameAndUrlKeyAndSaveActionGroup"> + <annotations> + <description>Requires navigation to subcategory creation/edit. Fills the name, and sets the Search Engine Optimization for the category.</description> + </annotations> + <arguments> + <argument name="categoryName" type="string"/> + <argument name="categoryUrlKey" type="string"/> + </arguments> + + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{categoryName}}" stepKey="enterCategoryName"/> + <scrollTo selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="scrollToSearchEngineOptimization"/> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSEO"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="{{categoryUrlKey}}" stepKey="enterURLKey"/> + <scrollToTopOfPage stepKey="scrollToTheTopOfPage"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <waitForPageLoad stepKey="waitForPageToLoad1"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillMainProductFormActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillMainProductFormActionGroup.xml new file mode 100644 index 0000000000000..f53059b3de063 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillMainProductFormActionGroup.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="FillMainProductFormActionGroup"> + <annotations> + <description>Fills in the provided Product details (Name, SKU, Price, Quantity, Stock Status, Weight Type and Weight) on the Admin Products creation/edit page.</description> + </annotations> + <arguments> + <argument name="product" defaultValue="_defaultProduct"/> + </arguments> + + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{product.name}}" stepKey="fillProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{product.sku}}" stepKey="fillProductSku"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{product.price}}" stepKey="fillProductPrice"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{product.quantity}}" stepKey="fillProductQty"/> + <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{product.status}}" stepKey="selectStockStatus"/> + <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeight"/> + <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="{{product.weight}}" stepKey="fillProductWeight"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillMainProductFormByStringActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillMainProductFormByStringActionGroup.xml new file mode 100644 index 0000000000000..4f3157292d356 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillMainProductFormByStringActionGroup.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="FillMainProductFormByStringActionGroup"> + <annotations> + <description>Fills in the provided Product Name, SKU, Price, Quantity, Stock Status and Weight on the Admin Products creation/edit page.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + <argument name="productSku" type="string"/> + <argument name="productPrice" type="string"/> + <argument name="productQuantity" type="string"/> + <argument name="productStatus" type="string"/> + <argument name="productWeight" type="string"/> + </arguments> + + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{productName}}" stepKey="fillProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{productSku}}" stepKey="fillProductSku"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{productPrice}}" stepKey="fillProductPrice"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{productQuantity}}" stepKey="fillProductQty"/> + <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{productStatus}}" stepKey="selectStockStatus"/> + <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeight"/> + <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="{{productWeight}}" stepKey="fillProductWeight"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillMainProductFormNoWeightActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillMainProductFormNoWeightActionGroup.xml new file mode 100644 index 0000000000000..b3cc110224b25 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillMainProductFormNoWeightActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FillMainProductFormNoWeightActionGroup"> + <annotations> + <description>Fills in the provided Product details (Name, SKU, Price, Quantity, Stock Status and Weight Type) on the Admin Products creation/edit page.</description> + </annotations> + <arguments> + <argument name="product" defaultValue="DownloadableProduct"/> + </arguments> + + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{product.name}}" stepKey="fillProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{product.sku}}" stepKey="fillProductSku"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{product.price}}" stepKey="fillProductPrice"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{product.quantity}}" stepKey="fillProductQty"/> + <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{product.status}}" stepKey="selectStockStatus"/> + <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has no weight" stepKey="selectWeight"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillNewProductCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillNewProductCategoryActionGroup.xml new file mode 100644 index 0000000000000..9edcfca9416f2 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillNewProductCategoryActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FillNewProductCategoryActionGroup"> + <annotations> + <description>Actions to fill out a new category from the product page with specified category and parent category names.</description> + </annotations> + <arguments> + <argument name="categoryName" defaultValue="Test Category" type="string"/> + <argument name="parentCategoryName" defaultValue="default" type="string"/> + </arguments> + + <!-- Click on new Category --> + <click stepKey="clickNewCategory" selector="{{AdminProductCategoryCreationSection.newCategory}}"/> + <waitForPageLoad stepKey="waitForFieldSet"/> + <fillField stepKey="fillCategoryName" selector="{{AdminProductCategoryCreationSection.nameInput}}" userInput="{{categoryName}}"/> + + <!-- Search and select a parent category for the product --> + <click stepKey="clickParentCategory" selector="{{AdminProductCategoryCreationSection.parentCategory}}"/> + <waitForPageLoad stepKey="waitForDropDownVisible"/> + <fillField stepKey="searchForParent" userInput="{{parentCategoryName}}" selector="{{AdminProductCategoryCreationSection.parentSearch}}"/> + <waitForPageLoad stepKey="waitForFieldResults"/> + <click stepKey="clickParent" selector="{{AdminProductCategoryCreationSection.parentSearchResult}}"/> + <click stepKey="createCategory" selector="{{AdminProductCategoryCreationSection.createCategory}}"/> + <waitForPageLoad stepKey="waitForCategoryCreated"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillProductNameAndSkuInProductFormActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillProductNameAndSkuInProductFormActionGroup.xml new file mode 100644 index 0000000000000..822b74d9e9e6b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FillProductNameAndSkuInProductFormActionGroup.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="FillProductNameAndSkuInProductFormActionGroup"> + <annotations> + <description>Fills in the provided Product details (Name and SKU) on the Admin Products creation and edit page.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{product.name}}" stepKey="fillProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{product.sku}}" stepKey="fillProductSku"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterAndSelectProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterAndSelectProductActionGroup.xml new file mode 100644 index 0000000000000..2cd64ddc2855f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterAndSelectProductActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FilterAndSelectProductActionGroup"> + <annotations> + <description>Goes to the Admin Products grid. Filters the Product grid by the provided Product SKU.</description> + </annotations> + <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/Catalog/Test/Mftf/ActionGroup/FilterProductAttributeByAttributeCodeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductAttributeByAttributeCodeActionGroup.xml new file mode 100644 index 0000000000000..efd1d50cb4929 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductAttributeByAttributeCodeActionGroup.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="FilterProductAttributeByAttributeCodeActionGroup"> + <annotations> + <description>Filters the Product Attributes grid by the provided Product Attribute Code.</description> + </annotations> + <arguments> + <argument name="ProductAttributeCode" type="string"/> + </arguments> + + <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGrid"/> + <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="{{ProductAttributeCode}}" stepKey="setAttributeCode"/> + <waitForPageLoad stepKey="waitForUserInput"/> + <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromTheGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductAttributeByAttributeLabelActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductAttributeByAttributeLabelActionGroup.xml new file mode 100644 index 0000000000000..8d2c966de6074 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductAttributeByAttributeLabelActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FilterProductAttributeByAttributeLabelActionGroup"> + <annotations> + <description>Searches the Attribute Sets grid page for the provided Attribute Set Name.</description> + </annotations> + <arguments> + <argument name="productAttributeLabel" type="string"/> + </arguments> + + <fillField selector="{{AdminProductAttributeGridSection.attributeLabelFilter}}" userInput="{{productAttributeLabel}}" stepKey="setAttributeLabel"/> + <waitForPageLoad stepKey="waitForUserInput"/> + <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromTheGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductAttributeByDefaultLabelActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductAttributeByDefaultLabelActionGroup.xml new file mode 100644 index 0000000000000..31702bfdca212 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductAttributeByDefaultLabelActionGroup.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="FilterProductAttributeByDefaultLabelActionGroup"> + <annotations> + <description>Filters the Product Attributes grid by the provided Product Attribute Label.</description> + </annotations> + <arguments> + <argument name="productAttributeLabel" type="string"/> + </arguments> + + <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGrid"/> + <fillField selector="{{AdminProductAttributeGridSection.GridFilterFrontEndLabel}}" userInput="{{productAttributeLabel}}" stepKey="setDefaultLabel"/> + <waitForPageLoad stepKey="waitForUserInput"/> + <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromTheGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductAttributeSetGridByAttributeSetNameActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductAttributeSetGridByAttributeSetNameActionGroup.xml new file mode 100644 index 0000000000000..c8aa8eb286fc3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductAttributeSetGridByAttributeSetNameActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FilterProductAttributeSetGridByAttributeSetNameActionGroup"> + <annotations> + <description>Filters the Attribute Sets grid page for the provided Attribute Set Name.</description> + </annotations> + <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"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridByDisabledStatusActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridByDisabledStatusActionGroup.xml new file mode 100644 index 0000000000000..52b6972ec92e6 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridByDisabledStatusActionGroup.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="FilterProductGridByDisabledStatusActionGroup"> + <annotations> + <description>Filters the Admin Products grid by the 'Disabled' Status. PLEASE NOTE: The Filter is Hardcoded.</description> + </annotations> + + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <selectOption selector="{{AdminProductGridFilterSection.statusFilter}}" userInput="Disabled" stepKey="selectDisabledStatusFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridByEnabledStatusActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridByEnabledStatusActionGroup.xml new file mode 100644 index 0000000000000..1e4d2315bf75e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridByEnabledStatusActionGroup.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="FilterProductGridByEnabledStatusActionGroup"> + <annotations> + <description>Filters the Admin Products grid by the 'Enabled' Status. PLEASE NOTE: The Filter is Hardcoded.</description> + </annotations> + + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <selectOption selector="{{AdminProductGridFilterSection.statusFilter}}" userInput="Enabled" stepKey="selectEnabledStatusFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridByName2ActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridByName2ActionGroup.xml new file mode 100644 index 0000000000000..850ea3e167daa --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridByName2ActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FilterProductGridByName2ActionGroup"> + <annotations> + <description>Filters the Admin Products grid by the provided Product Name.</description> + </annotations> + <arguments> + <argument name="name" type="string"/> + </arguments> + + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{name}}" stepKey="fillProductNameFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridByNameActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridByNameActionGroup.xml new file mode 100644 index 0000000000000..efd2f1c7be08a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridByNameActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FilterProductGridByNameActionGroup"> + <annotations> + <description>Filters the Admin Products grid by the provided Product (Name).</description> + </annotations> + <arguments> + <argument name="product" defaultValue="_defaultProduct"/> + </arguments> + + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{product.name}}" stepKey="fillProductNameFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridByPriceRangeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridByPriceRangeActionGroup.xml new file mode 100644 index 0000000000000..d3405f977ccc4 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridByPriceRangeActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FilterProductGridByPriceRangeActionGroup"> + <annotations> + <description>Filters the Admin Products grid by the provided Price Filter.</description> + </annotations> + <arguments> + <argument name="filter"/> + </arguments> + + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.priceFilterFrom}}" userInput="{{filter.from}}" stepKey="fillProductPriceFromFilter"/> + <fillField selector="{{AdminProductGridFilterSection.priceFilterTo}}" userInput="{{filter.to}}" stepKey="fillProductPriceToFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridBySetNewFromDateActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridBySetNewFromDateActionGroup.xml new file mode 100644 index 0000000000000..db12d6f9d8440 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridBySetNewFromDateActionGroup.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="FilterProductGridBySetNewFromDateActionGroup"> + <annotations> + <description>Filters the Admin Products grid by the New From Data field. PLEASE NOTE: The Start Date is Hardcoded.</description> + </annotations> + + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.newFromDateFilter}}" userInput="05/16/2018" stepKey="fillSetAsNewProductFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridBySku2ActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridBySku2ActionGroup.xml new file mode 100644 index 0000000000000..eb5f20b7c84e3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridBySku2ActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FilterProductGridBySku2ActionGroup"> + <annotations> + <description>Filters the Admin Products grid by the provided Product SKU.</description> + </annotations> + <arguments> + <argument name="sku" type="string"/> + </arguments> + + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{sku}}" stepKey="fillProductSkuFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridBySkuActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridBySkuActionGroup.xml new file mode 100644 index 0000000000000..d216b0f976efe --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridBySkuActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FilterProductGridBySkuActionGroup"> + <annotations> + <description>Filters the Admin Products grid by the provided Product (SKU).</description> + </annotations> + <arguments> + <argument name="product" defaultValue="_defaultProduct"/> + </arguments> + + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillProductSkuFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridBySkuAndNameActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridBySkuAndNameActionGroup.xml new file mode 100644 index 0000000000000..80e8e2c7c3133 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/FilterProductGridBySkuAndNameActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FilterProductGridBySkuAndNameActionGroup"> + <annotations> + <description>Filters the Admin Products grid by the provided Product (Name and SKU).</description> + </annotations> + <arguments> + <argument name="product" defaultValue="_defaultProduct"/> + </arguments> + + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillProductSkuFilter"/> + <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{product.name}}" stepKey="fillProductNameFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToAdminCategoryPageByIdActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToAdminCategoryPageByIdActionGroup.xml new file mode 100644 index 0000000000000..b1914118cd034 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToAdminCategoryPageByIdActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="GoToAdminCategoryPageByIdActionGroup"> + <annotations> + <description>Goes to the Category edit page for a specified Category ID.</description> + </annotations> + <arguments> + <argument name="id" type="string"/> + </arguments> + + <amOnPage url="{{AdminCategoryEditPage.url(id)}}" stepKey="amOnAdminCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="{{id}}" stepKey="seeCategoryPageTitle"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToAttributeGridPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToAttributeGridPageActionGroup.xml new file mode 100644 index 0000000000000..2b5fe9d76875c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToAttributeGridPageActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="GoToAttributeGridPageActionGroup"> + <annotations> + <description>Goes to the Attribute Sets grid page.</description> + </annotations> + + <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToAttributeSetByNameActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToAttributeSetByNameActionGroup.xml new file mode 100644 index 0000000000000..e732f1bd7341e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToAttributeSetByNameActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="GoToAttributeSetByNameActionGroup"> + <annotations> + <description>Searches for the provided Attribute Sets Name. Clicks on the 1st row.</description> + </annotations> + <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"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToCreateCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToCreateCategoryPageActionGroup.xml new file mode 100644 index 0000000000000..346f47006cf37 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToCreateCategoryPageActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="GoToCreateCategoryPageActionGroup"> + <annotations> + <description>Goes to the Category grid page. Clicks the Add Subcategory button.</description> + </annotations> + <arguments> + <argument name="selector" defaultValue="AdminCategorySidebarActionSection.AddSubcategoryButton"/> + </arguments> + + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="amOnAdminCategoryPage"/> + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <click selector="{{selector}}" stepKey="clickOnAddCategory"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Category" stepKey="seeCategoryPageTitle"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToCreateProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToCreateProductPageActionGroup.xml new file mode 100644 index 0000000000000..7f95765337189 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToCreateProductPageActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!--Navigate to create product page from product grid page--> + <actionGroup name="GoToCreateProductPageActionGroup"> + <annotations> + <description>Clicks on the 'Add Product' toggle on the Admin Products grid page. Clicks on the provided Product (Type).</description> + </annotations> + <arguments> + <argument name="product" defaultValue="_defaultProduct"/> + </arguments> + + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductToggle"/> + <waitForElementVisible selector="{{AdminProductGridActionSection.addTypeProduct(product.type_id)}}" stepKey="waitForAddProductDropdown" time="30"/> + <click selector="{{AdminProductGridActionSection.addTypeProduct(product.type_id)}}" stepKey="clickAddProductType"/> + <waitForPageLoad time="30" stepKey="waitForCreateProductPageLoad"/> + <seeInCurrentUrl url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, product.type_id)}}" stepKey="seeNewProductUrl"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Product" stepKey="seeNewProductTitle"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToProductCatalogPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToProductCatalogPageActionGroup.xml new file mode 100644 index 0000000000000..08bf948c2223b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToProductCatalogPageActionGroup.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="GoToProductCatalogPageActionGroup"> + <annotations> + <description>Goes to the Admin Products grid page.</description> + </annotations> + + <comment userInput="actionGroup:GoToProductCatalogPage" stepKey="actionGroupComment"/> + <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="GoToCatalogProductPage"/> + <waitForPageLoad stepKey="WaitForPageToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToProductPageViaIDActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToProductPageViaIDActionGroup.xml new file mode 100644 index 0000000000000..104ef83771e9d --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToProductPageViaIDActionGroup.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="GoToProductPageViaIDActionGroup"> + <annotations> + <description>Goes to the Product edit page for the provided Product ID.</description> + </annotations> + <arguments> + <argument name="productId" type="string"/> + </arguments> + + <amOnPage url="{{AdminProductEditPage.url(productId)}}" stepKey="goToProduct"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToSpecifiedCreateProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToSpecifiedCreateProductPageActionGroup.xml new file mode 100644 index 0000000000000..acbe990ad4c8c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToSpecifiedCreateProductPageActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="GoToSpecifiedCreateProductPageActionGroup"> + <annotations> + <description>Goes to the Admin Product grid page. Clicks on the Add Product toggle. Clicks on the provided Product Type.</description> + </annotations> + <arguments> + <argument type="string" name="productType" defaultValue="simple"/> + </arguments> + + <comment userInput="actionGroup:GoToSpecifiedCreateProductPageActionGroup" stepKey="actionGroupComment"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> + <click selector="{{AdminProductGridActionSection.addTypeProduct(productType)}}" stepKey="clickAddProduct"/> + <waitForPageLoad stepKey="waitForFormToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToStorefrontCategoryPageByParametersActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToStorefrontCategoryPageByParametersActionGroup.xml new file mode 100644 index 0000000000000..816aeaa09ee9b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToStorefrontCategoryPageByParametersActionGroup.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"> + <!-- Go to storefront category product page by given parameters --> + <actionGroup name="GoToStorefrontCategoryPageByParametersActionGroup"> + <annotations> + <description>Goes to the Storefront Category page using URI Search Parameters.</description> + </annotations> + <arguments> + <argument name="category" type="string"/> + <argument name="mode" type="string"/> + <argument name="numOfProductsPerPage" type="string"/> + <argument name="sortBy" type="string" defaultValue="position"/> + <argument name="sort" type="string" defaultValue="asc"/> + </arguments> + + <!-- Go to storefront category page --> + <amOnPage url="{{StorefrontCategoryPage.url(category)}}?product_list_limit={{numOfProductsPerPage}}&product_list_mode={{mode}}&product_list_order={{sortBy}}&product_list_dir={{sort}}" stepKey="onCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToSubCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToSubCategoryPageActionGroup.xml new file mode 100644 index 0000000000000..8c35c88f151d5 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/GoToSubCategoryPageActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="GoToSubCategoryPageActionGroup"> + <annotations> + <description>Goes to the Storefront page. Open the Parent Category menu in the Top Nav Menu. Click on a Subcategory. Validate that the Subcategory is present and correct.</description> + </annotations> + <arguments> + <argument name="parentCategory"/> + <argument name="subCategory"/> + <argument name="urlPath" type="string"/> + </arguments> + + <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName(parentCategory.name)}}" stepKey="moveMouseOnMainCategory"/> + <waitForElementVisible selector="{{StorefrontHeaderSection.NavigationCategoryByName(subCategory.name)}}" stepKey="waitForSubCategoryVisible"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName(subCategory.name)}}" stepKey="goToCategory"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <seeInCurrentUrl url="{{urlPath}}.html" stepKey="checkUrl"/> + <seeInTitle userInput="{{subCategory.name}}" stepKey="assertCategoryNameInTitle"/> + <see userInput="{{subCategory.name}}" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="assertCategoryName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ImportProductCustomizableOptionsActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ImportProductCustomizableOptionsActionGroup.xml new file mode 100644 index 0000000000000..2a847034add1d --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ImportProductCustomizableOptionsActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ImportProductCustomizableOptionsActionGroup"> + <annotations> + <description>Import custom Product Options for the provided Product Name.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + </arguments> + + <click selector="{{AdminProductCustomizableOptionsSection.importOptions}}" stepKey="clickImportOptions"/> + <waitForElementVisible selector="{{AdminProductImportOptionsSection.selectProductTitle}}" stepKey="waitForTitleVisible"/> + <conditionalClick selector="{{AdminProductImportOptionsSection.resetFiltersButton}}" dependentSelector="{{AdminProductImportOptionsSection.resetFiltersButton}}" visible="true" stepKey="clickResetFilters"/> + <click selector="{{AdminProductImportOptionsSection.filterButton}}" stepKey="clickFilterButton"/> + <waitForElementVisible selector="{{AdminProductImportOptionsSection.nameField}}" stepKey="waitForNameField"/> + <fillField selector="{{AdminProductImportOptionsSection.nameField}}" userInput="{{productName}}" stepKey="fillProductName"/> + <click selector="{{AdminProductImportOptionsSection.applyFiltersButton}}" stepKey="clickApplyFilters"/> + <checkOption selector="{{AdminProductImportOptionsSection.firstRowItemCheckbox}}" stepKey="checkProductCheckbox"/> + <click selector="{{AdminProductImportOptionsSection.importButton}}" stepKey="clickImport"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/NavigateToAndResetProductAttributeGridToDefaultViewActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/NavigateToAndResetProductAttributeGridToDefaultViewActionGroup.xml new file mode 100644 index 0000000000000..6d0091d90175b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/NavigateToAndResetProductAttributeGridToDefaultViewActionGroup.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="NavigateToAndResetProductAttributeGridToDefaultViewActionGroup"> + <annotations> + <description>Goes to the Product Attributes grid. Clicks on 'Clear Filters'.</description> + </annotations> + + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <waitForPageLoad stepKey="waitForGridLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/NavigateToAndResetProductGridToDefaultViewActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/NavigateToAndResetProductGridToDefaultViewActionGroup.xml new file mode 100644 index 0000000000000..c67c0c0e61ab7 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/NavigateToAndResetProductGridToDefaultViewActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="NavigateToAndResetProductGridToDefaultViewActionGroup" extends="ResetProductGridToDefaultViewActionGroup"> + <annotations> + <description>EXTENDS: resetProductGridToDefaultView. Adds an action to go to the Admin Products grid page.</description> + </annotations> + + <amOnPage url="{{AdminProductIndexPage.url}}" before="clickClearFilters" stepKey="goToAdminProductIndexPage"/> + <waitForPageLoad after="goToAdminProductIndexPage" stepKey="waitForProductIndexPageToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/NavigateToCreatedCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/NavigateToCreatedCategoryActionGroup.xml new file mode 100644 index 0000000000000..b1638909652f6 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/NavigateToCreatedCategoryActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="NavigateToCreatedCategoryActionGroup"> + <annotations> + <description>Navigates to category page, selects a category by specified category.</description> + </annotations> + <arguments> + <argument name="Category"/> + </arguments> + + <amOnPage url="{{AdminCategoryPage.page}}" stepKey="amOnCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="expandAll"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(Category.Name)}}" stepKey="navigateToCreatedCategory"/> + <waitForLoadingMaskToDisappear stepKey="waitForSpinner"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/NavigateToCreatedProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/NavigateToCreatedProductAttributeActionGroup.xml new file mode 100644 index 0000000000000..57ce7de7f3c0b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/NavigateToCreatedProductAttributeActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="NavigateToCreatedProductAttributeActionGroup"> + <annotations> + <description>Goes to the Product Attributes grid page. Filters the grid based on the provided Product Attribute. Clicks on the 1st row.</description> + </annotations> + <arguments> + <argument name="ProductAttribute"/> + </arguments> + + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> + <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="{{ProductAttribute.attribute_code}}" stepKey="setAttributeCode"/> + <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromTheGrid"/> + <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="clickOnAttributeRow"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/NavigateToCreatedProductEditPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/NavigateToCreatedProductEditPageActionGroup.xml new file mode 100644 index 0000000000000..0c9b5fc1c40e6 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/NavigateToCreatedProductEditPageActionGroup.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="NavigateToCreatedProductEditPageActionGroup"> + <annotations> + <description>Goes to the Admin Product grid page. Filters the Product grid based on the provided Product details (SKU). Edits the provided Product. Validates that the Product SKU is present and correct.</description> + </annotations> + <arguments> + <argument name="product" defaultValue="_defaultProduct"/> + </arguments> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToAdminProductIndexPage"/> + <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <waitForPageLoad stepKey="waitForClearFilters"/> + <dontSeeElement selector="{{AdminProductGridFilterSection.clearFilters}}" stepKey="dontSeeClearFilters"/> + <click selector="{{AdminProductGridFilterSection.viewDropdown}}" stepKey="openViewBookmarksTab"/> + <click selector="{{AdminProductGridFilterSection.viewBookmark('Default View')}}" stepKey="resetToDefaultGridView"/> + <waitForPageLoad stepKey="waitForResetToDefaultView"/> + <see selector="{{AdminProductGridFilterSection.viewDropdown}}" userInput="Default View" stepKey="seeDefaultViewSelected"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillProductSkuFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForPageLoad stepKey="waitForFilterOnGrid"/> + <click selector="{{AdminProductGridSection.selectRowBasedOnName(product.name)}}" stepKey="clickProduct"/> + <waitForPageLoad stepKey="waitForProductEditPageLoad"/> + <waitForElementVisible selector="{{AdminProductFormBundleSection.productSku}}" stepKey="waitForProductSKUField"/> + <seeInField selector="{{AdminProductFormBundleSection.productSku}}" userInput="{{product.sku}}" stepKey="seeProductSKU"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/NavigateToEditProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/NavigateToEditProductAttributeActionGroup.xml new file mode 100644 index 0000000000000..9a348d2be8208 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/NavigateToEditProductAttributeActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="NavigateToEditProductAttributeActionGroup"> + <annotations> + <description>Goes to the Product Attributes grid page. Filters the grid based on the provided Product Attribute. Clicks on the 1st row.</description> + </annotations> + <arguments> + <argument name="ProductAttribute" type="string"/> + </arguments> + + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> + <fillField selector="{{AdminProductAttributeGridSection.GridFilterFrontEndLabel}}" userInput="{{ProductAttribute}}" stepKey="navigateToAttributeEditPage1"/> + <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="navigateToAttributeEditPage2"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="navigateToAttributeEditPage3"/> + <waitForPageLoad stepKey="waitForPageLoad3"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/NavigateToMediaGalleryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/NavigateToMediaGalleryActionGroup.xml new file mode 100644 index 0000000000000..fea652311d7c1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/NavigateToMediaGalleryActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="NavigateToMediaGalleryActionGroup"> + <annotations> + <description>Navigates to the category page and Opens the Media Gallery.</description> + </annotations> + + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="amOnAdminCategoryPage"/> + <waitForElementVisible selector="{{AdminCategoryContentSection.sectionHeader}}" stepKey="waitForContentSection"/> + <conditionalClick selector="{{AdminCategoryContentSection.sectionHeader}}" dependentSelector="{{AdminCategoryContentSection.uploadButton}}" visible="false" stepKey="openContentSection"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <waitForElementVisible selector="{{AdminCategoryContentSection.selectFromGalleryButton}}" stepKey="waitForSelectFromGalleryButton"/> + <click selector="{{AdminCategoryContentSection.selectFromGalleryButton}}" stepKey="clickSelectFromGalleryButton"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenCategoryFromCategoryTreeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenCategoryFromCategoryTreeActionGroup.xml new file mode 100644 index 0000000000000..d172b9b24ab3e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenCategoryFromCategoryTreeActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="OpenCategoryFromCategoryTreeActionGroup"> + <annotations> + <description>Navigates to category page, selects a category by specified category. Replicates actionGroup:navigateToCreatedCategory.</description> + </annotations> + <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/OpenProductAttributeFromSearchResultInGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenProductAttributeFromSearchResultInGridActionGroup.xml index 2994533d79ed0..b8803fabda00d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenProductAttributeFromSearchResultInGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenProductAttributeFromSearchResultInGridActionGroup.xml @@ -16,7 +16,7 @@ <argument name="productAttributeCode" type="string"/> </arguments> - <waitForElementVisible selector="{{AdminProductAttributeGridSection.AttributeCode(productAttributeCode)}}" stepKey="waitForAdminProductAttributeGridLoad"/> + <waitForElementVisible selector="{{AdminProductAttributeGridSection.AttributeCode(productAttributeCode)}}" before="seeAttributeCodeInGrid" stepKey="waitForAdminProductAttributeGridLoad"/> <click selector="{{AdminProductAttributeGridSection.AttributeCode(productAttributeCode)}}" stepKey="clickAttributeToView"/> <waitForPageLoad stepKey="waitForViewAdminProductAttributeLoad"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenProductForEditByClickingRowXColumnYInProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenProductForEditByClickingRowXColumnYInProductGridActionGroup.xml new file mode 100644 index 0000000000000..d5b6520cb33bb --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/OpenProductForEditByClickingRowXColumnYInProductGridActionGroup.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="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup"> + <annotations> + <description>Clicks on the 'Edit' button in the Admin Products grid by the provided Grid coordinates (X, Y).</description> + </annotations> + <arguments> + <argument name="X" type="string" defaultValue="1"/> + <argument name="Y" type="string" defaultValue="2"/> + </arguments> + + <click selector="{{AdminProductGridSection.productGridXRowYColumnButton(X, Y)}}" stepKey="openProductForEdit"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductSetAdvancedPricingActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductSetAdvancedPricingActionGroup.xml new file mode 100644 index 0000000000000..95bda64202159 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductSetAdvancedPricingActionGroup.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ProductSetAdvancedPricingActionGroup"> + <annotations> + <description>Sets the provided Advanced Pricing on the Admin Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="website" type="string" defaultValue=""/> + <argument name="group" type="string" defaultValue="Retailer"/> + <argument name="quantity" type="string" defaultValue="1"/> + <argument name="price" type="string" defaultValue="Discount"/> + <argument name="amount" type="string" defaultValue="45"/> + </arguments> + + <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton"/> + <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForCustomerGroupPriceAddButton"/> + <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="addCustomerGroupAllGroupsQty1PriceDiscountAnd10percent"/> + <waitForElement selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" stepKey="waitForSelectCustomerGroupNameAttribute2"/> + <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceWebsiteSelect('0')}}" userInput="{{website}}" stepKey="selectProductWebsiteValue"/> + <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" userInput="{{group}}" stepKey="selectProductCustomGroupValue"/> + <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" userInput="{{quantity}}" stepKey="fillProductTierPriceQtyInput"/> + <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceValueTypeSelect('0')}}" userInput="{{price}}" stepKey="selectProductTierPriceValueType"/> + <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPricePercentageValuePriceInput('0')}}" userInput="{{amount}}" stepKey="selectProductTierPricePriceInput"/> + <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton"/> + <waitForPageLoad stepKey="WaitForProductSave"/> + <click selector="{{AdminProductFormAdvancedPricingSection.save}}" stepKey="clickSaveProduct1"/> + <waitForPageLoad time="60" stepKey="WaitForProductSave1"/> + <see userInput="You saved the product." stepKey="seeSaveConfirmation"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductSetAdvancedTierFixedPricingActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductSetAdvancedTierFixedPricingActionGroup.xml new file mode 100644 index 0000000000000..97d09fb3e1018 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductSetAdvancedTierFixedPricingActionGroup.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="ProductSetAdvancedTierFixedPricingActionGroup" extends="ProductSetAdvancedPricingActionGroup"> + <remove keyForRemoval="selectProductTierPricePriceInput"/> + <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceFixedPriceInput('0')}}" userInput="{{amount}}" stepKey="selectProductTierPricePriceInput" after="selectProductTierPriceValueType"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductSetWebsiteActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductSetWebsiteActionGroup.xml new file mode 100644 index 0000000000000..a2439a34bc7be --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductSetWebsiteActionGroup.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="ProductSetWebsiteActionGroup"> + <annotations> + <description>Sets the provided Website on the Admin Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="website" type="string"/> + </arguments> + + <scrollTo selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="scrollToWebsites"/> + <conditionalClick selector="{{ProductInWebsitesSection.sectionHeader}}" dependentSelector="{{ProductInWebsitesSection.website(website)}}" visible="false" stepKey="clickToOpenProductInWebsite"/> + <waitForPageLoad stepKey="waitForPageOpened"/> + <click selector="{{ProductInWebsitesSection.website(website)}}" stepKey="selectWebsite"/> + <click selector="{{AdminProductFormAdvancedPricingSection.save}}" stepKey="clickSaveProduct"/> + <waitForPageLoad time='60' stepKey="waitForProducrSaved"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSaveSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the product." stepKey="seeSaveSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/RemoveCategoryFromProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/RemoveCategoryFromProductActionGroup.xml new file mode 100644 index 0000000000000..37e7d6173d3a4 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/RemoveCategoryFromProductActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="RemoveCategoryFromProductActionGroup"> + <arguments> + <argument name="categoryName" type="string" defaultValue="{{_defaultCategory.name}}"/> + </arguments> + <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDown"/> + <click selector="{{AdminProductFormSection.unselectCategories(categoryName)}}" stepKey="unselectCategories"/> + <click selector="{{AdminProductFormSection.done}}" stepKey="clickOnDoneAdvancedCategory"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/RemoveCategoryImageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/RemoveCategoryImageActionGroup.xml new file mode 100644 index 0000000000000..4febb8fd82c91 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/RemoveCategoryImageActionGroup.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="RemoveCategoryImageActionGroup"> + <annotations> + <description>Requires navigation to the Category creation/edit page. Removes the current Category image. Validates that the Image does not exist.</description> + </annotations> + + <conditionalClick selector="{{AdminCategoryContentSection.sectionHeader}}" dependentSelector="{{AdminCategoryContentSection.uploadButton}}" visible="false" stepKey="openContentSection"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <waitForElementVisible selector="{{AdminCategoryContentSection.uploadButton}}" stepKey="seeImageSectionIsReady"/> + <click selector="{{AdminCategoryContentSection.removeImageButton}}" stepKey="clickRemoveImage"/> + <waitForAjaxLoad time="30" stepKey="waitForAjaxUpload"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> + <dontSee selector="{{AdminCategoryContentSection.imageFileName}}" stepKey="dontSeeImage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/RemoveProductImageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/RemoveProductImageActionGroup.xml new file mode 100644 index 0000000000000..6a56828b30308 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/RemoveProductImageActionGroup.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="RemoveProductImageActionGroup"> + <annotations> + <description>Removes a Product Image on the Admin Products creation/edit page.</description> + </annotations> + + <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductImagesSection"/> + <waitForPageLoad time="30" stepKey="waitForPageRefresh"/> + <click selector="{{AdminProductImagesSection.removeImageButton}}" stepKey="clickRemoveImage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/RemoveProductImageByNameActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/RemoveProductImageByNameActionGroup.xml new file mode 100644 index 0000000000000..fc931cbe37b25 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/RemoveProductImageByNameActionGroup.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="RemoveProductImageByNameActionGroup" extends="RemoveProductImageActionGroup"> + <annotations> + <description>Removes a Product Image on the Admin Products creation/edit page by name.</description> + </annotations> + + <arguments> + <argument name="image" defaultValue="ProductImage"/> + </arguments> + <click selector="{{AdminProductImagesSection.removeImageButtonForExactImage(image.fileName)}}" stepKey="clickRemoveImage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ResetImportOptionFilterActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ResetImportOptionFilterActionGroup.xml new file mode 100644 index 0000000000000..f73a4f4ed5e4f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ResetImportOptionFilterActionGroup.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="ResetImportOptionFilterActionGroup"> + <annotations> + <description>Click on the Reset Filters button for the Import Options filters on the Product grid page.</description> + </annotations> + + <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" visible="false" stepKey="openCustomOptionSection"/> + <click selector="{{AdminProductCustomizableOptionsSection.importOptions}}" stepKey="clickImportOptions"/> + <click selector="{{AdminProductImportOptionsSection.resetFiltersButton}}" stepKey="clickResetFilterButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ResetProductGridToDefaultViewActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ResetProductGridToDefaultViewActionGroup.xml new file mode 100644 index 0000000000000..441fe377bd435 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ResetProductGridToDefaultViewActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!--Reset the product grid to the default view--> + <actionGroup name="ResetProductGridToDefaultViewActionGroup"> + <annotations> + <description>Sets the Admin Products grid view to the 'Default View'.</description> + </annotations> + + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminProductGridFilterSection.viewDropdown}}" stepKey="openViewBookmarksTab"/> + <click selector="{{AdminProductGridFilterSection.viewBookmark('Default View')}}" stepKey="resetToDefaultGridView"/> + <waitForPageLoad stepKey="waitForProductGridLoad"/> + <see selector="{{AdminProductGridFilterSection.viewDropdown}}" userInput="Default View" stepKey="seeDefaultViewSelected"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveAttributeSetActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveAttributeSetActionGroup.xml new file mode 100644 index 0000000000000..4e8efe0885425 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveAttributeSetActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SaveAttributeSetActionGroup"> + <annotations> + <description>Save an Attribute Set on the Attribute Set creation/edit page.</description> + </annotations> + + <click selector="{{AdminProductAttributeSetActionSection.save}}" stepKey="clickSave"/> + <see userInput="You saved the attribute set" selector="{{AdminMessagesSection.success}}" stepKey="successMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveCategoryFormActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveCategoryFormActionGroup.xml new file mode 100644 index 0000000000000..ff6afb4aaf0e9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveCategoryFormActionGroup.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="SaveCategoryFormActionGroup"> + <annotations> + <description>Requires navigation to the Category creation/edit page. Checks that the url contains the AdminCategoryPage url. Saves the Category.</description> + </annotations> + + <seeInCurrentUrl url="{{AdminCategoryPage.url}}" stepKey="seeOnCategoryPage"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccess"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveProductAttributeActionGroup.xml new file mode 100644 index 0000000000000..e1bf2dea21318 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveProductAttributeActionGroup.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="SaveProductAttributeActionGroup"> + <annotations> + <description>Clicks on Save. Validates that the Success Message is present.</description> + </annotations> + + <waitForElementVisible selector="{{AttributePropertiesSection.Save}}" stepKey="waitForSaveButton"/> + <click selector="{{AttributePropertiesSection.Save}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="waitForAttributeToSave"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveProductAttributeInUseActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveProductAttributeInUseActionGroup.xml new file mode 100644 index 0000000000000..4da8232e8405d --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveProductAttributeInUseActionGroup.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="SaveProductAttributeInUseActionGroup"> + <annotations> + <description>Clicks on Save. Validates that the Success Message is present.</description> + </annotations> + + <waitForElementVisible selector="{{AttributePropertiesSection.Save}}" stepKey="waitForSaveButton"/> + <click selector="{{AttributePropertiesSection.Save}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="waitForAttributeToSave"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveProductFormActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveProductFormActionGroup.xml new file mode 100644 index 0000000000000..f2524d6e68bfc --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveProductFormActionGroup.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="SaveProductFormActionGroup"> + <annotations> + <description>Clicks on the Save button. Validates that the Success Message is present and correct.</description> + </annotations> + + <scrollToTopOfPage stepKey="scrollTopPageProduct"/> + <waitForElementVisible selector="{{AdminProductFormActionSection.saveButton}}" stepKey="waitForSaveProductButton"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitProductSaveSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the product." stepKey="seeSaveConfirmation"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveProductFormNoSuccessCheckActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveProductFormNoSuccessCheckActionGroup.xml new file mode 100644 index 0000000000000..eb1cc33de9de8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveProductFormNoSuccessCheckActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SaveProductFormNoSuccessCheckActionGroup" extends="SaveProductFormActionGroup"> + <annotations> + <description>EXTENDS: saveProductForm. Removes 'waitProductSaveSuccessMessage' and 'seeSaveConfirmation'.</description> + </annotations> + + <remove keyForRemoval="waitProductSaveSuccessMessage"/> + <remove keyForRemoval="seeSaveConfirmation"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendActionGroup.xml index 8b289f21f76b4..4c34feff4c943 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendActionGroup.xml @@ -23,26 +23,4 @@ <fillField userInput="{{product.sku}}" selector="{{AdminProductFiltersSection.skuInput}}" stepKey="fillSkuFieldOnFiltersSection"/> <click selector="{{AdminProductFiltersSection.apply}}" stepKey="clickApplyFiltersButton"/> </actionGroup> - - <actionGroup name="SearchForProductOnBackendByNameActionGroup" extends="SearchForProductOnBackendActionGroup"> - <annotations> - <description>Search for the provided Product Name.</description> - </annotations> - <arguments> - <argument name="productName" type="string"/> - </arguments> - - <remove keyForRemoval="fillSkuFieldOnFiltersSection"/> - <fillField userInput="{{productName}}" selector="{{AdminProductFiltersSection.nameInput}}" after="cleanFiltersIfTheySet" stepKey="fillNameFieldOnFiltersSection"/> - </actionGroup> - - <actionGroup name="ClearProductsFilterActionGroup"> - <annotations> - <description>Goto the Product grid page. Clear the Search Filters for the grid.</description> - </annotations> - - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <waitForPageLoad time="30" stepKey="waitForProductsPageToLoad"/> - <conditionalClick selector="{{AdminProductFiltersSection.clearFiltersButton}}" dependentSelector="{{AdminProductFiltersSection.clearFiltersButton}}" visible="true" stepKey="cleanFiltersIfTheySet"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendByNameActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendByNameActionGroup.xml new file mode 100644 index 0000000000000..b46a3f5818aff --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchForProductOnBackendByNameActionGroup.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="SearchForProductOnBackendByNameActionGroup" extends="SearchForProductOnBackendActionGroup"> + <annotations> + <description>Search for the provided Product Name.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + </arguments> + + <remove keyForRemoval="fillSkuFieldOnFiltersSection"/> + <fillField userInput="{{productName}}" selector="{{AdminProductFiltersSection.nameInput}}" after="cleanFiltersIfTheySet" stepKey="fillNameFieldOnFiltersSection"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchProductGridByKeyword2ActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchProductGridByKeyword2ActionGroup.xml new file mode 100644 index 0000000000000..bf9ca75f144b5 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchProductGridByKeyword2ActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SearchProductGridByKeyword2ActionGroup"> + <annotations> + <description>Searches the Admin Products grid for the provided Keyword.</description> + </annotations> + <arguments> + <argument name="keyword" type="string"/> + </arguments> + + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="{{keyword}}" stepKey="fillKeywordSearchField"/> + <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearch"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchProductGridByKeywordActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchProductGridByKeywordActionGroup.xml new file mode 100644 index 0000000000000..e3370864e7f61 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchProductGridByKeywordActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SearchProductGridByKeywordActionGroup"> + <annotations> + <description>Searches the Admin Products grid for the provided Keyword.</description> + </annotations> + <arguments> + <argument name="keyword"/> + </arguments> + + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="{{keyword}}" stepKey="fillKeywordSearchField"/> + <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearch"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SelectProductInWebsitesActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SelectProductInWebsitesActionGroup.xml new file mode 100644 index 0000000000000..bc3a865c3c365 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SelectProductInWebsitesActionGroup.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="SelectProductInWebsitesActionGroup"> + <annotations> + <description>Sets the provided Website on the Admin Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="website" type="string"/> + </arguments> + + <scrollTo selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="scrollToWebsites"/> + <conditionalClick selector="{{ProductInWebsitesSection.sectionHeader}}" dependentSelector="{{AdminProductContentSection.sectionHeaderShow}}" visible="false" stepKey="expandSection"/> + <waitForPageLoad stepKey="waitForPageOpened"/> + <checkOption selector="{{ProductInWebsitesSection.website(website)}}" stepKey="selectWebsite"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SetCategoryByNameActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SetCategoryByNameActionGroup.xml new file mode 100644 index 0000000000000..feca7e59e7b23 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SetCategoryByNameActionGroup.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="SetCategoryByNameActionGroup"> + <annotations> + <description>Sets the provided Category Name for a Product on the Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="categoryName" type="string"/> + </arguments> + + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{categoryName}}]" stepKey="searchAndSelectCategory"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SetProductUrlKeyActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SetProductUrlKeyActionGroup.xml new file mode 100644 index 0000000000000..a75e8f6b764b1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SetProductUrlKeyActionGroup.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="SetProductUrlKeyActionGroup"> + <annotations> + <description>Fills the Product details (URL) for the SEO section.</description> + </annotations> + <arguments> + <argument name="product" defaultValue="_defaultProduct"/> + </arguments> + + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField userInput="{{product.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SetProductUrlKeyByStringActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SetProductUrlKeyByStringActionGroup.xml new file mode 100644 index 0000000000000..d4c654523a40b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SetProductUrlKeyByStringActionGroup.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="SetProductUrlKeyByStringActionGroup"> + <annotations> + <description>Fills the Product SEO URL Key.</description> + </annotations> + <arguments> + <argument name="urlKey" type="string"/> + </arguments> + + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField userInput="{{urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SortProductsByIdAscendingActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SortProductsByIdAscendingActionGroup.xml new file mode 100644 index 0000000000000..38585277476f8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SortProductsByIdAscendingActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SortProductsByIdAscendingActionGroup"> + <annotations> + <description>Filters the ID column in Ascending Order.</description> + </annotations> + + <conditionalClick selector="{{AdminProductGridTableHeaderSection.id('descend')}}" dependentSelector="{{AdminProductGridTableHeaderSection.id('ascend')}}" visible="false" stepKey="sortById"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SortProductsByIdDescendingActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SortProductsByIdDescendingActionGroup.xml new file mode 100644 index 0000000000000..635e36c458519 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SortProductsByIdDescendingActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SortProductsByIdDescendingActionGroup"> + <annotations> + <description>Filters the ID column in Descending Order.</description> + </annotations> + + <conditionalClick selector="{{AdminProductGridTableHeaderSection.id('ascend')}}" dependentSelector="{{AdminProductGridTableHeaderSection.id('descend')}}" visible="false" stepKey="sortById"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddCategoryProductToCompareActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddCategoryProductToCompareActionGroup.xml new file mode 100644 index 0000000000000..d4678a97c5bc2 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddCategoryProductToCompareActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- Add Product to Compare from the category page and check message --> + <actionGroup name="StorefrontAddCategoryProductToCompareActionGroup"> + <annotations> + <description>Add a Product to the Compare Product list from a Category page.</description> + </annotations> + <arguments> + <argument name="productVar"/> + </arguments> + + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(productVar.name)}}" stepKey="moveMouseOverProduct"/> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCompareByName(productVar.name)}}" stepKey="clickAddProductToCompare"/> + <waitForElement selector="{{StorefrontMessagesSection.success}}" time="30" stepKey="waitForAddCategoryProductToCompareSuccessMessage"/> + <see selector="{{StorefrontMessagesSection.success}}" userInput="You added product {{productVar.name}} to the comparison list." stepKey="assertAddCategoryProductToCompareSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddProductToCompareActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddProductToCompareActionGroup.xml new file mode 100644 index 0000000000000..ee3a5067449dc --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddProductToCompareActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAddProductToCompareActionGroup"> + <annotations> + <description>Add a Product to the Compare Product list. Validate that the Success Message is present.</description> + </annotations> + <arguments> + <argument name="productVar"/> + </arguments> + + <click selector="{{StorefrontProductInfoMainSection.productAddToCompare}}" stepKey="clickAddToCompare"/> + <waitForElement selector="{{StorefrontMessagesSection.success}}" time="30" stepKey="waitForAddProductToCompareSuccessMessage"/> + <see selector="{{StorefrontMessagesSection.success}}" userInput="You added product {{productVar.name}} to the comparison list." stepKey="assertAddProductToCompareSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddSimpleProductWithQtyActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddSimpleProductWithQtyActionGroup.xml new file mode 100644 index 0000000000000..273893aa49445 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddSimpleProductWithQtyActionGroup.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="StorefrontAddSimpleProductWithQtyActionGroup" extends="AddSimpleProductToCartActionGroup"> + <arguments> + <argument name="quantity" type="string" defaultValue="1"/> + </arguments> + <fillField userInput="{{quantity}}" selector="{{StorefrontProductPageSection.qtyInput}}" stepKey="fillProductQty" after="goToProductPage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertActiveProductImageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertActiveProductImageActionGroup.xml new file mode 100644 index 0000000000000..0e7da54bd4028 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertActiveProductImageActionGroup.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="StorefrontAssertActiveProductImageActionGroup"> + <arguments> + <argument name="fileName" defaultValue="magento-logo" type="string"/> + </arguments> + <seeElement selector="{{StorefrontProductMediaSection.productImageActive(fileName)}}" stepKey="seeActiveImageDefault"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertFotoramaImageAvailabilityActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertFotoramaImageAvailabilityActionGroup.xml new file mode 100644 index 0000000000000..79ea22403646f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertFotoramaImageAvailabilityActionGroup.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="StorefrontAssertFotoramaImageAvailabilityActionGroup"> + <arguments> + <argument name="fileName" type="string" defaultValue="magento-logo"/> + </arguments> + <seeElement selector="{{StorefrontProductMediaSection.productImageInFotorama(fileName)}}" stepKey="seeActiveImageDefault"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductImagesOnProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductImagesOnProductPageActionGroup.xml index ad9fa9a576c7d..d15686ec3bddc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductImagesOnProductPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductImagesOnProductPageActionGroup.xml @@ -24,12 +24,4 @@ <click selector="{{StorefrontProductMediaSection.closeFullscreenImage}}" stepKey="closeFullScreenImage"/> <waitForPageLoad stepKey="waitForGalleryDisappear"/> </actionGroup> - - <!--Check availability image in fotorama--> - <actionGroup name="StorefrontAssertFotoramaImageAvailablity"> - <arguments> - <argument name="fileName" type="string" defaultValue="magento-logo"/> - </arguments> - <seeElement selector="{{StorefrontProductMediaSection.productImageInFotorama(fileName)}}" stepKey="seeActiveImageDefault"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductInRecentlyComparedWidgetActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductInRecentlyComparedWidgetActionGroup.xml new file mode 100644 index 0000000000000..43db9d96a4f50 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductInRecentlyComparedWidgetActionGroup.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="StorefrontAssertProductInRecentlyComparedWidgetActionGroup"> + <annotations> + <description>Validate that the provided Product appears in the Recently Compared Products widget.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <waitForElementVisible selector="{{StorefrontWidgetsSection.widgetRecentlyComparedProductsGrid}}" stepKey="waitWidgetRecentlyComparedProductsGrid"/> + <see selector="{{StorefrontWidgetsSection.widgetRecentlyComparedProductsGrid}}" userInput="{{product.name}}" stepKey="seeProductInRecentlyComparedWidget"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductInRecentlyOrderedWidgetActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductInRecentlyOrderedWidgetActionGroup.xml new file mode 100644 index 0000000000000..de0cb05f7c5a1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductInRecentlyOrderedWidgetActionGroup.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="StorefrontAssertProductInRecentlyOrderedWidgetActionGroup"> + <annotations> + <description>Validate that the provided Product appears in the Recently Ordered Products widget.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <waitForElementVisible selector="{{StorefrontWidgetsSection.widgetRecentlyOrderedProductsGrid}}" stepKey="waitWidgetRecentlyOrderedProductsGrid"/> + <see selector="{{StorefrontWidgetsSection.widgetRecentlyOrderedProductsGrid}}" userInput="{{product.name}}" stepKey="seeProductInRecentlyOrderedWidget"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductInWidgetActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductInWidgetActionGroup.xml index 3caa58e16654f..81bce368a0c06 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductInWidgetActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductInWidgetActionGroup.xml @@ -20,30 +20,4 @@ <waitForElementVisible selector="{{StorefrontWidgetsSection.widgetRecentlyViewedProductsGrid}}" stepKey="waitWidgetRecentlyViewedProductsGrid"/> <see selector="{{StorefrontWidgetsSection.widgetRecentlyViewedProductsGrid}}" userInput="{{product.name}}" stepKey="seeProductInRecentlyViewedWidget"/> </actionGroup> - - <!-- Check the product in recently compared widget --> - <actionGroup name="StorefrontAssertProductInRecentlyComparedWidgetActionGroup"> - <annotations> - <description>Validate that the provided Product appears in the Recently Compared Products widget.</description> - </annotations> - <arguments> - <argument name="product"/> - </arguments> - - <waitForElementVisible selector="{{StorefrontWidgetsSection.widgetRecentlyComparedProductsGrid}}" stepKey="waitWidgetRecentlyComparedProductsGrid"/> - <see selector="{{StorefrontWidgetsSection.widgetRecentlyComparedProductsGrid}}" userInput="{{product.name}}" stepKey="seeProductInRecentlyComparedWidget"/> - </actionGroup> - - <!-- Check the product in recently ordered widget --> - <actionGroup name="StorefrontAssertProductInRecentlyOrderedWidgetActionGroup"> - <annotations> - <description>Validate that the provided Product appears in the Recently Ordered Products widget.</description> - </annotations> - <arguments> - <argument name="product"/> - </arguments> - - <waitForElementVisible selector="{{StorefrontWidgetsSection.widgetRecentlyOrderedProductsGrid}}" stepKey="waitWidgetRecentlyOrderedProductsGrid"/> - <see selector="{{StorefrontWidgetsSection.widgetRecentlyOrderedProductsGrid}}" userInput="{{product.name}}" stepKey="seeProductInRecentlyOrderedWidget"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductPriceOnCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductPriceOnCategoryPageActionGroup.xml new file mode 100644 index 0000000000000..bc341fa09bfab --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductPriceOnCategoryPageActionGroup.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="StorefrontAssertProductPriceOnCategoryPageActionGroup" extends="StorefrontAssertProductPriceOnProductPageActionGroup"> + <annotations> + <description>Validate that the provided Product Price is correct on category page.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + </arguments> + + <see userInput="{{productPrice}}" selector="{{StorefrontCategoryProductSection.ProductPriceByName(productName)}}" stepKey="seeProductPrice"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml deleted file mode 100644 index 9393669f6e46d..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml +++ /dev/null @@ -1,124 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!-- Go to storefront category product page by given parameters --> - <actionGroup name="GoToStorefrontCategoryPageByParameters"> - <annotations> - <description>Goes to the Storefront Category page using URI Search Parameters.</description> - </annotations> - <arguments> - <argument name="category" type="string"/> - <argument name="mode" type="string"/> - <argument name="numOfProductsPerPage" type="string"/> - <argument name="sortBy" type="string" defaultValue="position"/> - <argument name="sort" type="string" defaultValue="asc"/> - </arguments> - - <!-- Go to storefront category page --> - <amOnPage url="{{StorefrontCategoryPage.url(category)}}?product_list_limit={{numOfProductsPerPage}}&product_list_mode={{mode}}&product_list_order={{sortBy}}&product_list_dir={{sort}}" stepKey="onCategoryPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - </actionGroup> - - <actionGroup name="VerifyCategoryPageParameters"> - <annotations> - <description>Validate that the Category Page parameters are present and correct.</description> - </annotations> - <arguments> - <argument name="category"/> - <argument name="mode" type="string"/> - <argument name="numOfProductsPerPage" type="string"/> - <argument name="sortBy" type="string" defaultValue="position"/> - </arguments> - - <seeInCurrentUrl url="/{{category.custom_attributes[url_key]}}.html" stepKey="checkUrl"/> - <seeInTitle userInput="{{category.name}}" stepKey="assertCategoryNameInTitle"/> - <see userInput="{{category.name}}" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="assertCategoryName"/> - <see userInput="{{mode}}" selector="{{StorefrontCategoryMainSection.modeGridIsActive}}" stepKey="assertViewMode"/> - <see userInput="{{numOfProductsPerPage}}" selector="{{StorefrontCategoryMainSection.perPage}}" stepKey="assertNumberOfProductsPerPage"/> - <see userInput="{{sortBy}}" selector="{{StorefrontCategoryMainSection.sortedBy}}" stepKey="assertSortedBy"/> - </actionGroup> - - <!-- Check the category page --> - <actionGroup name="StorefrontCheckCategoryActionGroup"> - <annotations> - <description>Validate that the Storefront Category is present and correct.</description> - </annotations> - <arguments> - <argument name="category"/> - <argument name="productCount" type="string"/> - </arguments> - - <seeInCurrentUrl url="/{{category.custom_attributes[url_key]}}.html" stepKey="checkUrl"/> - <seeInTitle userInput="{{category.name}}" stepKey="assertCategoryNameInTitle"/> - <see userInput="{{category.name}}" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="assertCategoryName"/> - <see userInput="{{productCount}}" selector="{{StorefrontCategoryMainSection.productCount}} span" stepKey="assertProductCount"/> - </actionGroup> - - <!-- Check simple product on the category page --> - <actionGroup name="StorefrontCheckCategorySimpleProduct"> - <annotations> - <description>Validate that the provided Simple Product is present and correct on a Category page.</description> - </annotations> - <arguments> - <argument name="product"/> - </arguments> - - <seeElement selector="{{StorefrontCategoryProductSection.ProductTitleByName(product.name)}}" stepKey="assertProductName"/> - <see userInput="${{product.price}}.00" selector="{{StorefrontCategoryProductSection.ProductPriceByName(product.name)}}" stepKey="AssertProductPrice"/> - <!-- @TODO: MAGETWO-80272 Move to Magento_Checkout --> - <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(product.name)}}" stepKey="moveMouseOverProduct"/> - <!-- @TODO: MAGETWO-80272 Move to Magento_Checkout --> - <seeElement selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(product.name)}}" stepKey="AssertAddToCart"/> - </actionGroup> - - <actionGroup name="AssertProductOnCategoryPageActionGroup" extends="StorefrontCheckCategorySimpleProduct"> - <annotations> - <description>EXTENDS:StorefrontCheckCategorySimpleProduct. Removes 'AssertProductPrice', 'moveMouseOverProduct', 'AssertAddToCart'</description> - </annotations> - <remove keyForRemoval="AssertProductPrice"/> - <remove keyForRemoval="moveMouseOverProduct"/> - <remove keyForRemoval="AssertAddToCart"/> - </actionGroup> - - <actionGroup name="StorefrontCheckAddToCartButtonAbsence"> - <arguments> - <argument name="product" defaultValue="_defaultProduct"/> - </arguments> - <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(product.name)}}" stepKey="moveMouseOverProduct" /> - <dontSeeElement selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(product.name)}}" stepKey="checkAddToCartButtonAbsence"/> - </actionGroup> - <actionGroup name="StorefrontSwitchCategoryViewToListMode"> - <annotations> - <description>Switch the Storefront Category view to List.</description> - </annotations> - - <click selector="{{StorefrontCategoryMainSection.modeListButton}}" stepKey="switchCategoryViewToListMode"/> - <waitForElement selector="{{StorefrontCategoryMainSection.CategoryTitle}}" time="30" stepKey="waitForCategoryReload"/> - </actionGroup> - - <actionGroup name="GoToSubCategoryPage"> - <annotations> - <description>Goes to the Storefront page. Open the Parent Category menu in the Top Nav Menu. Click on a Subcategory. Validate that the Subcategory is present and correct.</description> - </annotations> - <arguments> - <argument name="parentCategory"/> - <argument name="subCategory"/> - <argument name="urlPath" type="string"/> - </arguments> - - <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName(parentCategory.name)}}" stepKey="moveMouseOnMainCategory"/> - <waitForElementVisible selector="{{StorefrontHeaderSection.NavigationCategoryByName(subCategory.name)}}" stepKey="waitForSubCategoryVisible"/> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName(subCategory.name)}}" stepKey="goToCategory"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <seeInCurrentUrl url="{{urlPath}}.html" stepKey="checkUrl"/> - <seeInTitle userInput="{{subCategory.name}}" stepKey="assertCategoryNameInTitle"/> - <see userInput="{{subCategory.name}}" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="assertCategoryName"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryPageSortAscendingActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryPageSortAscendingActionGroup.xml new file mode 100644 index 0000000000000..f456ca6bece55 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryPageSortAscendingActionGroup.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="StorefrontCategoryPageSortAscendingActionGroup"> + <annotations> + <description>Set Ascending Direction for sorting Products on Category page</description> + </annotations> + <click selector="{{StorefrontCategoryTopToolbarSection.sortDirectionAsc}}" stepKey="setAscendingDirection"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryPageSortDescendingActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryPageSortDescendingActionGroup.xml new file mode 100644 index 0000000000000..839260b3339c8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryPageSortDescendingActionGroup.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="StorefrontCategoryPageSortDescendingActionGroup"> + <annotations> + <description>Set Descending Direction for sorting Products on Category page</description> + </annotations> + <click selector="{{StorefrontCategoryTopToolbarSection.sortDirectionDesc}}" stepKey="setDescendingDirection"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryPageSortProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryPageSortProductActionGroup.xml index 64dd2c97a382f..5744ddaf6c69a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryPageSortProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryPageSortProductActionGroup.xml @@ -17,16 +17,4 @@ </arguments> <selectOption selector="{{StorefrontCategoryTopToolbarSection.sortByDropdown}}" userInput="{{sortBy}}" stepKey="selectSortByParameter"/> </actionGroup> - <actionGroup name="StorefrontCategoryPageSortAscendingActionGroup"> - <annotations> - <description>Set Ascending Direction for sorting Products on Category page</description> - </annotations> - <click selector="{{StorefrontCategoryTopToolbarSection.sortDirectionAsc}}" stepKey="setAscendingDirection"/> - </actionGroup> - <actionGroup name="StorefrontCategoryPageSortDescendingActionGroup"> - <annotations> - <description>Set Descending Direction for sorting Products on Category page</description> - </annotations> - <click selector="{{StorefrontCategoryTopToolbarSection.sortDirectionDesc}}" stepKey="setDescendingDirection"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckAddToCartButtonAbsenceActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckAddToCartButtonAbsenceActionGroup.xml new file mode 100644 index 0000000000000..66eb61b4aa8ec --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckAddToCartButtonAbsenceActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCheckAddToCartButtonAbsenceActionGroup"> + <arguments> + <argument name="product" defaultValue="_defaultProduct"/> + </arguments> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(product.name)}}" stepKey="moveMouseOverProduct" /> + <dontSeeElement selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(product.name)}}" stepKey="checkAddToCartButtonAbsence"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckCategoryActionGroup.xml new file mode 100644 index 0000000000000..695ba39f5d397 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckCategoryActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCheckCategoryActionGroup"> + <annotations> + <description>Validate that the Storefront Category is present and correct.</description> + </annotations> + <arguments> + <argument name="category"/> + <argument name="productCount" type="string"/> + </arguments> + + <seeInCurrentUrl url="/{{category.custom_attributes[url_key]}}.html" stepKey="checkUrl"/> + <seeInTitle userInput="{{category.name}}" stepKey="assertCategoryNameInTitle"/> + <see userInput="{{category.name}}" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="assertCategoryName"/> + <see userInput="{{productCount}}" selector="{{StorefrontCategoryMainSection.productCount}} span" stepKey="assertProductCount"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckCategorySimpleProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckCategorySimpleProductActionGroup.xml new file mode 100644 index 0000000000000..1f8234498ffa7 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckCategorySimpleProductActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCheckCategorySimpleProductActionGroup"> + <annotations> + <description>Validate that the provided Simple Product is present and correct on a Category page.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <seeElement selector="{{StorefrontCategoryProductSection.ProductTitleByName(product.name)}}" stepKey="assertProductName"/> + <see userInput="${{product.price}}.00" selector="{{StorefrontCategoryProductSection.ProductPriceByName(product.name)}}" stepKey="AssertProductPrice"/> + <!-- @TODO: MAGETWO-80272 Move to Magento_Checkout --> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(product.name)}}" stepKey="moveMouseOverProduct"/> + <!-- @TODO: MAGETWO-80272 Move to Magento_Checkout --> + <seeElement selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(product.name)}}" stepKey="AssertAddToCart"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckCompareSidebarProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckCompareSidebarProductActionGroup.xml new file mode 100644 index 0000000000000..220fe29337d5a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckCompareSidebarProductActionGroup.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="StorefrontCheckCompareSidebarProductActionGroup"> + <annotations> + <description>Validate that the Product Name is present and correct in the Compare Product list.</description> + </annotations> + <arguments> + <argument name="productVar"/> + </arguments> + + <waitForElement selector="{{StorefrontComparisonSidebarSection.ProductTitleByName(productVar.name)}}" stepKey="waitForProduct"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckCompareSimpleProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckCompareSimpleProductActionGroup.xml new file mode 100644 index 0000000000000..d7515d19ffbbd --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckCompareSimpleProductActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCheckCompareSimpleProductActionGroup"> + <annotations> + <description>Validate that the Simple Product is present and correct in the Compare Product area.</description> + </annotations> + <arguments> + <argument name="productVar"/> + </arguments> + + <seeElement selector="{{StorefrontProductCompareMainSection.ProductLinkByName(productVar.name)}}" stepKey="assertProductName"/> + <see userInput="${{productVar.price}}.00" selector="{{StorefrontProductCompareMainSection.ProductPriceByName(productVar.name)}}" stepKey="assertProductPrice1"/> + <see userInput="{{productVar.sku}}" selector="{{StorefrontProductCompareMainSection.ProductAttributeByCodeAndProductName('SKU', productVar.name)}}" stepKey="assertProductPrice2"/> + <!-- @TODO: MAGETWO-80272 Move to Magento_Checkout --> + <seeElement selector="{{StorefrontProductCompareMainSection.ProductAddToCartByName(productVar.name)}}" stepKey="assertProductAddToCart"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckProductIsMissingInCategoryProductsPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckProductIsMissingInCategoryProductsPageActionGroup.xml index 01751a32d2e06..a386d81f31999 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckProductIsMissingInCategoryProductsPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckProductIsMissingInCategoryProductsPageActionGroup.xml @@ -15,11 +15,4 @@ </arguments> <dontSee selector="{{StorefrontCategoryProductSection.ProductTitleByName(productName)}}" stepKey="dontSeeCorrectProductsOnStorefront"/> </actionGroup> - <actionGroup name="StorefrontCheckProductPositionActionGroup"> - <arguments> - <argument name="position" type="string"/> - <argument name="productName" type="string"/> - </arguments> - <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber(position)}}" userInput="{{productName}}" stepKey="assertProductPosition"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckProductPositionActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckProductPositionActionGroup.xml new file mode 100644 index 0000000000000..ce92966eb1fbf --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckProductPositionActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCheckProductPositionActionGroup"> + <arguments> + <argument name="position" type="string"/> + <argument name="productName" type="string"/> + </arguments> + <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber(position)}}" userInput="{{productName}}" stepKey="assertProductPosition"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckProductPriceInCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckProductPriceInCategoryActionGroup.xml index ac33727564505..fd843a68b9720 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckProductPriceInCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckProductPriceInCategoryActionGroup.xml @@ -9,7 +9,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- You must already be on the category page --> - <actionGroup name="StorefrontCheckProductPriceInCategoryActionGroup" extends="StorefrontCheckCategorySimpleProduct"> + <actionGroup name="StorefrontCheckProductPriceInCategoryActionGroup" extends="StorefrontCheckCategorySimpleProductActionGroup"> <annotations> <description>EXTENDS: StorefrontCheckCategorySimpleProduct. Removes 'AssertProductPrice'. Validates that the provided Product Price is present and correct on a Storefront Product page.</description> </annotations> @@ -17,14 +17,4 @@ <remove keyForRemoval="AssertProductPrice"/> <see userInput="{{product.price}}" selector="{{StorefrontCategoryProductSection.ProductPriceByName(product.name)}}" stepKey="AssertProductPrice"/> </actionGroup> - <actionGroup name="StorefrontAssertProductPriceOnCategoryPageActionGroup" extends="StorefrontAssertProductPriceOnProductPageActionGroup"> - <annotations> - <description>Validate that the provided Product Price is correct on category page.</description> - </annotations> - <arguments> - <argument name="productName" type="string"/> - </arguments> - - <see userInput="{{productPrice}}" selector="{{StorefrontCategoryProductSection.ProductPriceByName(productName)}}" stepKey="seeProductPrice"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckSimpleProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckSimpleProductActionGroup.xml new file mode 100644 index 0000000000000..a2ebf93805819 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCheckSimpleProductActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- Check the simple product on the product page --> + <actionGroup name="StorefrontCheckSimpleProductActionGroup"> + <annotations> + <description>Validates that the provided Simple Product information is present and correct.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <seeInCurrentUrl url="/{{product.custom_attributes[url_key]}}.html" stepKey="checkUrl"/> + <seeInTitle userInput="{{product.name}}" stepKey="AssertProductNameInTitle"/> + <see userInput="{{product.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="assertProductName"/> + <see userInput="{{product.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="assertProductSku"/> + <see userInput="${{product.price}}.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="assertProductPrice"/> + <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertInStock"/> + <seeElement selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="assertAddToCart"/> + <see userInput="{{product.custom_attributes[description]}}" selector="{{StorefrontProductInfoMainSection.productDescription}}" stepKey="assertProductDescription"/> + <see userInput="{{product.custom_attributes[short_description]}}" selector="{{StorefrontProductInfoMainSection.productShortDescription}}" stepKey="assertProductShortDescription"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontClearCompareActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontClearCompareActionGroup.xml new file mode 100644 index 0000000000000..995bf7efd3f39 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontClearCompareActionGroup.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="StorefrontClearCompareActionGroup"> + <annotations> + <description>Clear the Compare Products list. Validate that the Compare Products list is empty.</description> + </annotations> + + <waitForElementVisible selector="{{StorefrontComparisonSidebarSection.ClearAll}}" time="30" stepKey="waitForClearAll"/> + <click selector="{{StorefrontComparisonSidebarSection.ClearAll}}" stepKey="clickClearAll"/> + <waitForElementVisible selector="{{ModalConfirmationSection.OkButton}}" time="30" stepKey="waitForClearOk"/> + <scrollTo selector="{{ModalConfirmationSection.OkButton}}" stepKey="scrollToClearOk"/> + <click selector="{{ModalConfirmationSection.OkButton}}" stepKey="clickClearOk"/> + <waitForElement selector="{{StorefrontMessagesSection.message('You cleared the comparison list.')}}" time="30" stepKey="AssertMessageCleared"/> + <waitForElement selector="{{StorefrontComparisonSidebarSection.NoItemsMessage}}" time="30" stepKey="assertNoItems"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCompareActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCompareActionGroup.xml deleted file mode 100644 index b10b74c919918..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCompareActionGroup.xml +++ /dev/null @@ -1,95 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!-- Add Product to Compare from the category page and check message --> - <actionGroup name="StorefrontAddCategoryProductToCompareActionGroup"> - <annotations> - <description>Add a Product to the Compare Product list from a Category page.</description> - </annotations> - <arguments> - <argument name="productVar"/> - </arguments> - - <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(productVar.name)}}" stepKey="moveMouseOverProduct"/> - <click selector="{{StorefrontCategoryProductSection.ProductAddToCompareByName(productVar.name)}}" stepKey="clickAddProductToCompare"/> - <waitForElement selector="{{StorefrontMessagesSection.success}}" time="30" stepKey="waitForAddCategoryProductToCompareSuccessMessage"/> - <see selector="{{StorefrontMessagesSection.success}}" userInput="You added product {{productVar.name}} to the comparison list." stepKey="assertAddCategoryProductToCompareSuccessMessage"/> - </actionGroup> - - <!-- Add Product to Compare from the product page and check message --> - <actionGroup name="StorefrontAddProductToCompareActionGroup"> - <annotations> - <description>Add a Product to the Compare Product list. Validate that the Success Message is present.</description> - </annotations> - <arguments> - <argument name="productVar"/> - </arguments> - - <click selector="{{StorefrontProductInfoMainSection.productAddToCompare}}" stepKey="clickAddToCompare"/> - <waitForElement selector="{{StorefrontMessagesSection.success}}" time="30" stepKey="waitForAddProductToCompareSuccessMessage"/> - <see selector="{{StorefrontMessagesSection.success}}" userInput="You added product {{productVar.name}} to the comparison list." stepKey="assertAddProductToCompareSuccessMessage"/> - </actionGroup> - - <!-- Check the product in compare sidebar --> - <actionGroup name="StorefrontCheckCompareSidebarProductActionGroup"> - <annotations> - <description>Validate that the Product Name is present and correct in the Compare Product list.</description> - </annotations> - <arguments> - <argument name="productVar"/> - </arguments> - - <waitForElement selector="{{StorefrontComparisonSidebarSection.ProductTitleByName(productVar.name)}}" stepKey="waitForProduct"/> - </actionGroup> - - <!-- Open and check comparison page --> - <actionGroup name="StorefrontOpenAndCheckComparisionActionGroup"> - <annotations> - <description>Open the Storefront Compare Product page. Validate that the Compare Product fields are present.</description> - </annotations> - - <click selector="{{StorefrontComparisonSidebarSection.Compare}}" stepKey="clickCompare"/> - <waitForLoadingMaskToDisappear stepKey="waitForComparePageloaded"/> - <seeInCurrentUrl url="{{StorefrontProductComparePage.url}}" stepKey="checkUrl"/> - <seeInTitle userInput="Products Comparison List" stepKey="assertPageNameInTitle"/> - <see userInput="Compare Products" selector="{{StorefrontProductCompareMainSection.PageName}}" stepKey="assertPageName"/> - </actionGroup> - - <!-- Check the simple product in comparison page --> - <actionGroup name="StorefrontCheckCompareSimpleProductActionGroup"> - <annotations> - <description>Validate that the Simple Product is present and correct in the Compare Product area.</description> - </annotations> - <arguments> - <argument name="productVar"/> - </arguments> - - <seeElement selector="{{StorefrontProductCompareMainSection.ProductLinkByName(productVar.name)}}" stepKey="assertProductName"/> - <see userInput="${{productVar.price}}.00" selector="{{StorefrontProductCompareMainSection.ProductPriceByName(productVar.name)}}" stepKey="assertProductPrice1"/> - <see userInput="{{productVar.sku}}" selector="{{StorefrontProductCompareMainSection.ProductAttributeByCodeAndProductName('SKU', productVar.name)}}" stepKey="assertProductPrice2"/> - <!-- @TODO: MAGETWO-80272 Move to Magento_Checkout --> - <seeElement selector="{{StorefrontProductCompareMainSection.ProductAddToCartByName(productVar.name)}}" stepKey="assertProductAddToCart"/> - </actionGroup> - - <!-- Clear the compare list --> - <actionGroup name="StorefrontClearCompareActionGroup"> - <annotations> - <description>Clear the Compare Products list. Validate that the Compare Products list is empty.</description> - </annotations> - - <waitForElementVisible selector="{{StorefrontComparisonSidebarSection.ClearAll}}" time="30" stepKey="waitForClearAll"/> - <click selector="{{StorefrontComparisonSidebarSection.ClearAll}}" stepKey="clickClearAll"/> - <waitForElementVisible selector="{{ModalConfirmationSection.OkButton}}" time="30" stepKey="waitForClearOk"/> - <scrollTo selector="{{ModalConfirmationSection.OkButton}}" stepKey="scrollToClearOk"/> - <click selector="{{ModalConfirmationSection.OkButton}}" stepKey="clickClearOk"/> - <waitForElement selector="{{StorefrontMessagesSection.message('You cleared the comparison list.')}}" time="30" stepKey="AssertMessageCleared"/> - <waitForElement selector="{{StorefrontComparisonSidebarSection.NoItemsMessage}}" time="30" stepKey="assertNoItems"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontGoToCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontGoToCategoryPageActionGroup.xml index e8be0db38fe2c..080f3264c037b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontGoToCategoryPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontGoToCategoryPageActionGroup.xml @@ -17,11 +17,4 @@ <click selector="{{StorefrontHeaderSection.NavigationCategoryByName(categoryName)}}" stepKey="toCategory"/> <waitForPageLoad stepKey="waitForCategoryPage"/> </actionGroup> - <actionGroup name="StorefrontGoToSubCategoryPageActionGroup" extends="StorefrontGoToCategoryPageActionGroup"> - <arguments> - <argument name="subCategoryName" type="string"/> - </arguments> - <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName(categoryName)}}" stepKey="toCategory"/> - <click selector="{{StorefrontHeaderSection.NavigationCategoryByName(subCategoryName)}}" stepKey="openSubCategory" after="toCategory"/> - </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontGoToSubCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontGoToSubCategoryPageActionGroup.xml new file mode 100644 index 0000000000000..71c51a9b9f567 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontGoToSubCategoryPageActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontGoToSubCategoryPageActionGroup" extends="StorefrontGoToCategoryPageActionGroup"> + <arguments> + <argument name="subCategoryName" type="string"/> + </arguments> + <moveMouseOver selector="{{StorefrontHeaderSection.NavigationCategoryByName(categoryName)}}" stepKey="toCategory"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName(subCategoryName)}}" stepKey="openSubCategory" after="toCategory"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenAndCheckComparisionActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenAndCheckComparisionActionGroup.xml new file mode 100644 index 0000000000000..1bb58d15bc096 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenAndCheckComparisionActionGroup.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="StorefrontOpenAndCheckComparisionActionGroup"> + <annotations> + <description>Open the Storefront Compare Product page. Validate that the Compare Product fields are present.</description> + </annotations> + + <click selector="{{StorefrontComparisonSidebarSection.Compare}}" stepKey="clickCompare"/> + <waitForLoadingMaskToDisappear stepKey="waitForComparePageloaded"/> + <seeInCurrentUrl url="{{StorefrontProductComparePage.url}}" stepKey="checkUrl"/> + <seeInTitle userInput="Products Comparison List" stepKey="assertPageNameInTitle"/> + <see userInput="Compare Products" selector="{{StorefrontProductCompareMainSection.PageName}}" stepKey="assertPageName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenProductPageActionGroup.xml index e0229906ad558..899603aa27d75 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenProductPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenProductPageActionGroup.xml @@ -18,15 +18,4 @@ <amOnPage url="{{StorefrontProductPage.url(productUrl)}}" stepKey="openProductPage"/> <waitForPageLoad stepKey="waitForProductPageLoaded"/> </actionGroup> - <actionGroup name="StorefrontOpenProductPageOnSecondStore"> - <annotations> - <description>Goes to the Storefront Product page for the provided store code and Product URL.</description> - </annotations> - <arguments> - <argument name="storeCode" type="string"/> - <argument name="productUrl" type="string"/> - </arguments> - - <amOnPage url="{{StorefrontStoreViewProductPage.url(storeCode,productUrl)}}" stepKey="openProductPage"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenProductPageOnSecondStoreActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenProductPageOnSecondStoreActionGroup.xml new file mode 100644 index 0000000000000..00956e2099085 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontOpenProductPageOnSecondStoreActionGroup.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="StorefrontOpenProductPageOnSecondStoreActionGroup"> + <annotations> + <description>Goes to the Storefront Product page for the provided store code and Product URL.</description> + </annotations> + <arguments> + <argument name="storeCode" type="string"/> + <argument name="productUrl" type="string"/> + </arguments> + + <amOnPage url="{{StorefrontStoreViewProductPage.url(storeCode,productUrl)}}" stepKey="openProductPage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml deleted file mode 100644 index 403b5b853d80c..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml +++ /dev/null @@ -1,90 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!-- Check the simple product on the product page --> - <actionGroup name="StorefrontCheckSimpleProduct"> - <annotations> - <description>Validates that the provided Simple Product information is present and correct.</description> - </annotations> - <arguments> - <argument name="product"/> - </arguments> - - <seeInCurrentUrl url="/{{product.custom_attributes[url_key]}}.html" stepKey="checkUrl"/> - <seeInTitle userInput="{{product.name}}" stepKey="AssertProductNameInTitle"/> - <see userInput="{{product.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="assertProductName"/> - <see userInput="{{product.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="assertProductSku"/> - <see userInput="${{product.price}}.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="assertProductPrice"/> - <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertInStock"/> - <seeElement selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="assertAddToCart"/> - <see userInput="{{product.custom_attributes[description]}}" selector="{{StorefrontProductInfoMainSection.productDescription}}" stepKey="assertProductDescription"/> - <see userInput="{{product.custom_attributes[short_description]}}" selector="{{StorefrontProductInfoMainSection.productShortDescription}}" stepKey="assertProductShortDescription"/> - </actionGroup> - - <!-- Assert product image in Storefront Product page --> - <actionGroup name="assertProductImageStorefrontProductPage"> - <annotations> - <description>Validates that the provided Product Image is present.</description> - </annotations> - <arguments> - <argument name="product"/> - <argument name="image" defaultValue="MagentoLogo"/> - </arguments> - - <seeInCurrentUrl url="/{{product.urlKey}}.html" stepKey="checkUrl"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <seeElement selector="{{StorefrontProductMediaSection.imageFile(image.filename)}}" stepKey="seeImage"/> - </actionGroup> - - <!-- Assert product image in Storefront Product page --> - <actionGroup name="assertProductImageStorefrontProductPage2"> - <annotations> - <description>Validates that the provided Product Image is present.</description> - </annotations> - <arguments> - <argument name="product"/> - <argument name="image" defaultValue="MagentoLogo"/> - </arguments> - - <seeInCurrentUrl url="/{{product.custom_attributes[url_key]}}.html" stepKey="checkUrl"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <seeElement selector="{{StorefrontProductMediaSection.imageFile(image.filename)}}" stepKey="seeImage"/> - </actionGroup> - - <!-- Assert no product image in Storefront Product page --> - <actionGroup name="assertProductImageNotInStorefrontProductPage"> - <annotations> - <description>Validates that the provided Product Image is not present.</description> - </annotations> - <arguments> - <argument name="product"/> - <argument name="image" defaultValue="MagentoLogo"/> - </arguments> - - <seeInCurrentUrl url="/{{product.urlKey}}.html" stepKey="checkUrl"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <dontSeeElement selector="{{StorefrontProductMediaSection.imageFile(image.filename)}}" stepKey="seeImage"/> - </actionGroup> - - <!-- Assert no product image in Storefront Product page --> - <actionGroup name="assertProductImageNotInStorefrontProductPage2"> - <annotations> - <description>Validates that the provided Product Image is not present.</description> - </annotations> - <arguments> - <argument name="product"/> - <argument name="image" defaultValue="MagentoLogo"/> - </arguments> - - <seeInCurrentUrl url="/{{product.custom_attributes[url_key]}}.html" stepKey="checkUrl"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <dontSeeElement selector="{{StorefrontProductMediaSection.imageFile(image.filename)}}" stepKey="seeImage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageActionGroup.xml deleted file mode 100644 index c960f14b1cd48..0000000000000 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageActionGroup.xml +++ /dev/null @@ -1,87 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Click Add to Cart button in storefront product page--> - <actionGroup name="addToCartFromStorefrontProductPage"> - <annotations> - <description>Click on the Add to Cart button. Validates that the Success Message is present.</description> - </annotations> - <arguments> - <argument name="productName"/> - </arguments> - - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart"/> - <waitForPageLoad stepKey="waitForAddToCart"/> - <waitForElementNotVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAdding}}" stepKey="waitForElementNotVisibleAddToCartButtonTitleIsAdding"/> - <waitForElementNotVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAdded}}" stepKey="waitForElementNotVisibleAddToCartButtonTitleIsAdded"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> - <see selector="{{StorefrontMessagesSection.success}}" userInput="You added {{productName}} to your shopping cart." stepKey="seeAddToCartSuccessMessage"/> - </actionGroup> - - <actionGroup name="AddProductWithQtyToCartFromStorefrontProductPage" extends="addToCartFromStorefrontProductPage"> - <annotations> - <description>EXTENDS: addToCartFromStorefrontProductPage. Fills in the provided Product Quantity for the provided Product Name.</description> - </annotations> - <arguments> - <argument name="productName" type="string"/> - <argument name="productQty" type="string"/> - </arguments> - - <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="{{productQty}}" stepKey="fillProductQuantity" before="addToCart"/> - </actionGroup> - - <!--Verify text length validation hint with multiple inputs--> - <actionGroup name="testDynamicValidationHint"> - <annotations> - <description>Validates that the Product Text Option Text Length Hint displays the correct Count for multiple inputs based on the provided Character Limit.</description> - </annotations> - <arguments> - <argument name="charLimit"/> - </arguments> - - <fillField userInput="abcde" selector="{{StorefrontProductPageSection.customTextOptionInput}}" stepKey="textInput1"/> - <see selector="{{StorefrontProductPageSection.charCounter}}" userInput="(15 remaining)" stepKey="assertHint1"/> - <fillField userInput="abcdefghjklansdmnbv" selector="{{StorefrontProductPageSection.customTextOptionInput}}" stepKey="textInput2"/> - <see selector="{{StorefrontProductPageSection.charCounter}}" userInput="(1 remaining)" stepKey="assertHint2"/> - <fillField userInput="abcdefghjklansdmnbvd" selector="{{StorefrontProductPageSection.customTextOptionInput}}" stepKey="textInput3"/> - <see selector="{{StorefrontProductPageSection.charCounter}}" userInput="(0 remaining)" stepKey="assertHint3"/> - <fillField userInput="abcdefghjklansdmnbvds" selector="{{StorefrontProductPageSection.customTextOptionInput}}" stepKey="textInput4"/> - <see selector="{{StorefrontProductPageSection.charCounter}}" userInput="(1 too many)" stepKey="assertHint4"/> - <fillField userInput="abcdefghjklansdmnbvdsasdfghjmn" selector="{{StorefrontProductPageSection.customTextOptionInput}}" stepKey="textInput5"/> - <see selector="{{StorefrontProductPageSection.charCounter}}" userInput="(10 too many)" stepKey="assertHint5"/> - </actionGroup> - - <actionGroup name="checkAttributeInMoreInformationTab"> - <annotations> - <description>Validates that the Product More Information area contains the provided Text.</description> - </annotations> - <arguments> - <argument name="attributeLabel" type="string"/> - <argument name="attributeValue" type="string"/> - </arguments> - - <click selector="{{StorefrontProductMoreInformationSection.moreInformation}}" stepKey="clickTab"/> - <see userInput="{{attributeLabel}}" selector="{{StorefrontProductMoreInformationSection.moreInformationTextArea}}" stepKey="seeAttributeLabel"/> - <see userInput="{{attributeValue}}" selector="{{StorefrontProductMoreInformationSection.moreInformationTextArea}}" stepKey="seeAttributeValue"/> - </actionGroup> - - <actionGroup name="checkAttributeNotInMoreInformationTab"> - <annotations> - <description>Validate that the More Information area does not contain the provided Text.</description> - </annotations> - <arguments> - <argument name="attributeLabel" type="string"/> - </arguments> - - <click selector="{{StorefrontProductMoreInformationSection.moreInformation}}" stepKey="clickTab"/> - <dontSee userInput="{{attributeLabel}}" selector="{{StorefrontProductMoreInformationSection.moreInformationTextArea}}" stepKey="seeAttributeLabel"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontSwitchCategoryViewToListModeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontSwitchCategoryViewToListModeActionGroup.xml new file mode 100644 index 0000000000000..7e549294ecfe0 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontSwitchCategoryViewToListModeActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontSwitchCategoryViewToListModeActionGroup"> + <annotations> + <description>Switch the Storefront Category view to List.</description> + </annotations> + + <click selector="{{StorefrontCategoryMainSection.modeListButton}}" stepKey="switchCategoryViewToListMode"/> + <waitForElement selector="{{StorefrontCategoryMainSection.CategoryTitle}}" time="30" stepKey="waitForCategoryReload"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SwitchCategoryStoreViewActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SwitchCategoryStoreViewActionGroup.xml new file mode 100644 index 0000000000000..e75a63581fb3c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SwitchCategoryStoreViewActionGroup.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SwitchCategoryStoreViewActionGroup"> + <annotations> + <description>Navigates to category page, selects a category and changes store view to specified store.</description> + </annotations> + <arguments> + <argument name="Store"/> + <argument name="CatName"/> + </arguments> + + <amOnPage url="{{AdminCategoryPage.page}}" stepKey="amOnCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(CatName)}}" stepKey="navigateToCreatedCategory"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <waitForLoadingMaskToDisappear stepKey="waitForSpinner"/> + <scrollToTopOfPage stepKey="scrollToToggle"/> + <click selector="{{AdminCategoryMainActionsSection.CategoryStoreViewDropdownToggle}}" stepKey="openStoreViewDropDown"/> + <click selector="{{AdminCategoryMainActionsSection.CategoryStoreViewOption(Store)}}" stepKey="selectStoreView"/> + <waitForPageLoad stepKey="waitForPageLoad3"/> + <waitForLoadingMaskToDisappear stepKey="waitForSpinner2"/> + <click selector="{{AdminCategoryMainActionsSection.CategoryStoreViewModalAccept}}" stepKey="selectStoreViewAccept"/> + <waitForPageLoad stepKey="waitForStoreViewChangeLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SwitchCategoryToAllStoreViewActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SwitchCategoryToAllStoreViewActionGroup.xml new file mode 100644 index 0000000000000..28ca577bb98f5 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SwitchCategoryToAllStoreViewActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SwitchCategoryToAllStoreViewActionGroup"> + <annotations> + <description>Navigates to category page, selects a category and changes store view to all stores.</description> + </annotations> + <arguments> + <argument name="CatName"/> + </arguments> + + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(CatName)}}" stepKey="navigateToCreatedCategory"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <waitForLoadingMaskToDisappear stepKey="waitForSpinner1"/> + <scrollToTopOfPage stepKey="scrollToToggle"/> + <click selector="{{AdminCategoryMainActionsSection.CategoryStoreViewDropdownToggle}}" stepKey="openStoreViewDropDown"/> + <click selector="{{AdminCategoryMainActionsSection.allStoreViews}}" stepKey="clickStoreViewByName"/> + <see selector="{{AdminCategoryMainActionsSection.storeSwitcher}}" userInput="All Store Views" stepKey="seeAllStoreView"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <waitForLoadingMaskToDisappear stepKey="waitForSpinner2"/> + <click selector="{{AdminCategoryMainActionsSection.CategoryStoreViewModalAccept}}" stepKey="selectStoreViewAccept"/> + <waitForPageLoad stepKey="waitForStoreViewChangeLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SwitchToTheNewStoreViewActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SwitchToTheNewStoreViewActionGroup.xml new file mode 100644 index 0000000000000..9dd1a2dc56d1e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SwitchToTheNewStoreViewActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SwitchToTheNewStoreViewActionGroup"> + <annotations> + <description>Switches the New Store View.</description> + </annotations> + <arguments> + <argument name="storeViewName" type="string"/> + </arguments> + + <scrollTo selector="{{AdminProductContentSection.pageHeader}}" stepKey="scrollToUp"/> + <waitForElementVisible selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="waitForElementBecomeVisible"/> + <click selector="{{AdminProductFormActionSection.changeStoreButton}}" stepKey="clickStoreviewSwitcher"/> + <click selector="{{AdminProductFormActionSection.selectStoreView(storeViewName)}}" stepKey="chooseStoreView"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="acceptStoreSwitchingMessage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/TestDynamicValidationHintActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/TestDynamicValidationHintActionGroup.xml new file mode 100644 index 0000000000000..9e3f46e0400b2 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/TestDynamicValidationHintActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="TestDynamicValidationHintActionGroup"> + <annotations> + <description>Validates that the Product Text Option Text Length Hint displays the correct Count for multiple inputs based on the provided Character Limit.</description> + </annotations> + <arguments> + <argument name="charLimit"/> + </arguments> + + <fillField userInput="abcde" selector="{{StorefrontProductPageSection.customTextOptionInput}}" stepKey="textInput1"/> + <see selector="{{StorefrontProductPageSection.charCounter}}" userInput="(15 remaining)" stepKey="assertHint1"/> + <fillField userInput="abcdefghjklansdmnbv" selector="{{StorefrontProductPageSection.customTextOptionInput}}" stepKey="textInput2"/> + <see selector="{{StorefrontProductPageSection.charCounter}}" userInput="(1 remaining)" stepKey="assertHint2"/> + <fillField userInput="abcdefghjklansdmnbvd" selector="{{StorefrontProductPageSection.customTextOptionInput}}" stepKey="textInput3"/> + <see selector="{{StorefrontProductPageSection.charCounter}}" userInput="(0 remaining)" stepKey="assertHint3"/> + <fillField userInput="abcdefghjklansdmnbvds" selector="{{StorefrontProductPageSection.customTextOptionInput}}" stepKey="textInput4"/> + <see selector="{{StorefrontProductPageSection.charCounter}}" userInput="(1 too many)" stepKey="assertHint4"/> + <fillField userInput="abcdefghjklansdmnbvdsasdfghjmn" selector="{{StorefrontProductPageSection.customTextOptionInput}}" stepKey="textInput5"/> + <see selector="{{StorefrontProductPageSection.charCounter}}" userInput="(10 too many)" stepKey="assertHint5"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ToggleProductEnabledActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ToggleProductEnabledActionGroup.xml new file mode 100644 index 0000000000000..220d254f70c97 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ToggleProductEnabledActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ToggleProductEnabledActionGroup"> + <annotations> + <description>Clicks on the Enable Product toggle.</description> + </annotations> + + <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="toggleEnabled"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/UnassignAttributeFromGroupActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/UnassignAttributeFromGroupActionGroup.xml new file mode 100644 index 0000000000000..d58fa1e71061d --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/UnassignAttributeFromGroupActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="UnassignAttributeFromGroupActionGroup"> + <annotations> + <description>Unassign the provided Attribute from an Attribute Set from the Attribute Sets creation/edit page.</description> + </annotations> + <arguments> + <argument name="group" type="string"/> + <argument name="attribute" type="string"/> + </arguments> + + <conditionalClick selector="{{AdminProductAttributeSetEditSection.attributeGroupExtender(group)}}" dependentSelector="{{AdminProductAttributeSetEditSection.attributeGroupCollapsed(group)}}" visible="true" stepKey="extendGroup"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <dragAndDrop selector1="{{AdminProductAttributeSetEditSection.assignedAttribute(attribute)}}" selector2="{{AdminProductAttributeSetEditSection.xThLineItemUnassignedAttribute('1')}}" stepKey="dragAndDropToUnassigned"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <see userInput="{{attribute}}" selector="{{AdminProductAttributeSetEditSection.unassignedAttributesTree}}" stepKey="seeAttributeInUnassigned"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/UnassignWebsiteFromProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/UnassignWebsiteFromProductActionGroup.xml new file mode 100644 index 0000000000000..cee17cbc4b45e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/UnassignWebsiteFromProductActionGroup.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="UnassignWebsiteFromProductActionGroup" extends="SelectProductInWebsitesActionGroup"> + <remove keyForRemoval="selectWebsite"/> + <uncheckOption selector="{{ProductInWebsitesSection.website(website)}}" stepKey="unSelectWebsite" after="waitForPageOpened"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/VerifyCategoryPageParametersActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/VerifyCategoryPageParametersActionGroup.xml new file mode 100644 index 0000000000000..dff3ca46640df --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/VerifyCategoryPageParametersActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="VerifyCategoryPageParametersActionGroup"> + <annotations> + <description>Validate that the Category Page parameters are present and correct.</description> + </annotations> + <arguments> + <argument name="category"/> + <argument name="mode" type="string"/> + <argument name="numOfProductsPerPage" type="string"/> + <argument name="sortBy" type="string" defaultValue="position"/> + </arguments> + + <seeInCurrentUrl url="/{{category.custom_attributes[url_key]}}.html" stepKey="checkUrl"/> + <seeInTitle userInput="{{category.name}}" stepKey="assertCategoryNameInTitle"/> + <see userInput="{{category.name}}" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="assertCategoryName"/> + <see userInput="{{mode}}" selector="{{StorefrontCategoryMainSection.modeGridIsActive}}" stepKey="assertViewMode"/> + <see userInput="{{numOfProductsPerPage}}" selector="{{StorefrontCategoryMainSection.perPage}}" stepKey="assertNumberOfProductsPerPage"/> + <see userInput="{{sortBy}}" selector="{{StorefrontCategoryMainSection.sortedBy}}" stepKey="assertSortedBy"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ViewProductInAdminGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ViewProductInAdminGridActionGroup.xml new file mode 100644 index 0000000000000..9e9c7bc323eb3 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ViewProductInAdminGridActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ViewProductInAdminGridActionGroup"> + <annotations> + <description>Filters the Admin Products grid by the provided Product (Name, SKU and Type).</description> + </annotations> + <arguments> + <argument name="product" defaultValue="_defaultProduct"/> + </arguments> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> + <waitForPageLoad stepKey="waitForPageLoadInitial"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{product.name}}" stepKey="fillProductNameFilter"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillProductSkuFilter"/> + <selectOption selector="{{AdminProductGridFilterSection.typeFilter}}" userInput="{{product.type_id}}" stepKey="selectionProductType"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="{{product.name}}" stepKey="seeProductNameInGrid"/> + <see selector="{{AdminProductGridSection.productGridCell('1', 'Price')}}" userInput="{{product.price}}" stepKey="seeProductPriceInGrid"/> + <click selector="{{AdminProductGridFilterSection.clearFilters}}" stepKey="clickClearFiltersAfter"/> + </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 6614fa4b5dbeb..e4a91902cb79b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml @@ -108,6 +108,10 @@ <data key="used_for_sort_by">true</data> <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabel</requiredEntity> </entity> + <entity name="multipleSelectProductAttribute" extends="productDropDownAttribute" type="ProductAttribute"> + <data key="frontend_input">multiselect</data> + <data key="frontend_input_admin">Multiple Select</data> + </entity> <entity name="productDropDownAttributeNotSearchable" type="ProductAttribute"> <data key="attribute_code" unique="suffix">attribute</data> <data key="frontend_input">select</data> @@ -309,6 +313,10 @@ <data key="frontend_input">date</data> <data key="is_required_admin">No</data> </entity> + <entity name="DatetimeProductAttribute" extends="productAttributeWysiwyg" type="ProductAttribute"> + <data key="frontend_input">datetime</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> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml index bb0e85bcbb40b..a8646a58ae39c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeOptionData.xml @@ -86,6 +86,14 @@ <data key="label" unique="suffix">White</data> <data key="value" unique="suffix">white</data> </entity> + <entity name="ProductAttributeOption9" type="ProductAttributeOption"> + <var key="attribute_code" entityKey="attribute_code" entityType="ProductAttribute"/> + <data key="label" unique="suffix">Blue</data> + <data key="is_default">false</data> + <data key="sort_order">3</data> + <requiredEntity type="StoreLabel">Option11Store0</requiredEntity> + <requiredEntity type="StoreLabel">Option11Store1</requiredEntity> + </entity> <!-- Product attribute options from file "export_import_configurable_product.csv" --> <entity name="ProductAttributeOptionOneForExportImport" extends="productAttributeOption1" type="ProductAttributeOption"> <data key="label">option1</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index aad43bb7011c1..46bb6e527608f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -39,6 +39,10 @@ <data key="name">Pursuit Lumaflex&trade; Tone Band</data> <data key="sku" unique="suffix">x&trade;</data> </entity> + <entity name="SimpleProduct_25" type="product" extends="SimpleProduct2"> + <data key="quantity">25</data> + <requiredEntity type="product_extension_attribute">EavStock25</requiredEntity> + </entity> <entity name="ApiSimpleProductWithCustomPrice" type="product" extends="ApiSimpleProduct"> <data key="price">100</data> </entity> @@ -64,6 +68,23 @@ <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> </entity> + <entity name="ProductWithSpecialSymbols" extends="SimpleProduct" type="product"> + <data key="name">SimpleProduct -+~/\\<>\’“:*\$#@()!,.?`=%&^</data> + </entity> + <entity name="SimpleProductBeforeUpdate" type="product"> + <data key="sku">simpleProduct</data> + <data key="type_id">simple</data> + <data key="attribute_set_id">4</data> + <data key="name" unique="suffix">SimpleProduct</data> + <data key="price">123.00</data> + <data key="visibility">4</data> + <data key="status">1</data> + <data key="quantity">1000</data> + <data key="urlKey">simpleProduct</data> + <data key="weight">1</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + </entity> <entity name="SimpleProductAfterImport1" type="product"> <data key="sku">SimpleProductForTest1</data> <data key="type_id">simple</data> @@ -507,6 +528,11 @@ <requiredEntity type="product_option">ProductOptionDateTime</requiredEntity> <requiredEntity type="product_option">ProductOptionTime</requiredEntity> </entity> + <entity name="productWithDateTimeOption" type="product"> + <var key="sku" entityType="product" entityKey="sku" /> + <data key="file">magento.jpg</data> + <requiredEntity type="product_option">ProductOptionDateTime</requiredEntity> + </entity> <entity name="productWithOptionRadiobutton" type="product"> <var key="sku" entityType="product" entityKey="sku" /> <requiredEntity type="product_option">ProductOptionRadiobuttonWithTwoFixedOptions</requiredEntity> @@ -1068,6 +1094,10 @@ <entity name="productAlphabeticalB" type="product" extends="_defaultProduct"> <data key="name" unique="suffix">BBB Product</data> </entity> + <entity name="simpleProductWithShortNameAndSku" type="product" extends="defaultSimpleProduct"> + <data key="name">Test_Product</data> + <data key="sku">test_sku</data> + </entity> <entity name="productWithSpecialCharacters" type="product" extends="_defaultProduct"> <data key="name" unique="suffix">Product "!@#$%^&*()+:;\|}{][?=~` </data> <data key="nameWithSafeChars" unique="suffix">|}{][?=~` </data> @@ -1303,4 +1333,9 @@ <entity name="SimpleProductUpdatePrice16" type="product2"> <data key="price">16.00</data> </entity> + <entity name="ProductWithTwoTextFieldOptions" type="product"> + <var key="sku" entityType="product" entityKey="sku" /> + <requiredEntity type="product_option">ProductOptionField</requiredEntity> + <requiredEntity type="product_option">ProductOptionField2</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductExtensionAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductExtensionAttributeData.xml index 5a6a0b5dd9518..2576d8cdd7022 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductExtensionAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductExtensionAttributeData.xml @@ -26,4 +26,7 @@ <entity name="EavStock777" type="product_extension_attribute"> <requiredEntity type="stock_item">Qty_777</requiredEntity> </entity> + <entity name="EavStock25" type="product_extension_attribute"> + <requiredEntity type="stock_item">Qty_25</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml index 720087917aad4..404a4771a0e73 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductOptionData.xml @@ -11,6 +11,7 @@ <entity name="ProductOptionField" type="product_option"> <var key="product_sku" entityType="product" entityKey="sku" /> <data key="title">OptionField</data> + <data key="sku">OptionField</data> <data key="type">field</data> <data key="is_require">true</data> <data key="sort_order">1</data> @@ -21,6 +22,7 @@ <entity name="ProductOptionField2" type="product_option"> <var key="product_sku" entityType="product" entityKey="sku" /> <data key="title">OptionField2</data> + <data key="sku">OptionField2</data> <data key="type">field</data> <data key="is_require">true</data> <data key="sort_order">1</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/StockItemData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/StockItemData.xml index 32f4dc1404dd7..bef0b9f56eab6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/StockItemData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/StockItemData.xml @@ -40,4 +40,8 @@ <data key="qty">777</data> <data key="is_in_stock">true</data> </entity> + <entity name="Qty_25" type="stock_item"> + <data key="qty">25</data> + <data key="is_in_stock">true</data> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml index 0e51995ac72e8..dcd7fde92283c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/StoreLabelData.xml @@ -72,4 +72,12 @@ <data key="store_id">1</data> <data key="label">Red</data> </entity> + <entity name="Option11Store0" type="StoreLabel"> + <data key="store_id">0</data> + <data key="label">Blue</data> + </entity> + <entity name="Option11Store1" type="StoreLabel"> + <data key="store_id">1</data> + <data key="label">Blue</data> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/WidgetsData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/WidgetsData.xml index 18564ff101fd9..291ff115302f2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/WidgetsData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/WidgetsData.xml @@ -32,4 +32,15 @@ <data key="display_on">All Pages</data> <data key="container">Sidebar Additional</data> </entity> + <entity name="CatalogCategoryLinkWidget" type="widget"> + <data key="type">Catalog Category Link</data> + <data key="design_theme">Magento Luma</data> + <data key="name" unique="suffix">Test Widget</data> + <array key="store_ids"> + <item>All Store Views</item> + </array> + <data key="sort_order">0</data> + <data key="display_on">All Pages</data> + <data key="container">Main Content Area</data> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Metadata/image_content-meta.xml b/app/code/Magento/Catalog/Test/Mftf/Metadata/image_content-meta.xml index 8b12a2fbbbc5d..ac75a819548ae 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Metadata/image_content-meta.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Metadata/image_content-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="CreateImageContent" dataType="ImageContent" type="create"> <field key="base64_encoded_data" required="true">string</field> <field key="type" required="true">string</field> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/StorefrontCategoryPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/StorefrontCategoryPage.xml index 469c153d38b88..416e0d27a1824 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/StorefrontCategoryPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/StorefrontCategoryPage.xml @@ -8,7 +8,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="StorefrontCategoryPage" url="/{{var1}}.html" area="storefront" module="Catalog" parameterized="true"> + <page name="StorefrontCategoryPage" url="/{{var1}}.html" area="storefront" module="Magento_Catalog" parameterized="true"> <section name="StorefrontCategoryMainSection"/> <section name="WYSIWYGToolbarSection"/> </page> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml index af7e2786f6e8d..304c34b404ea5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySidebarTreeSection.xml @@ -19,6 +19,7 @@ <element name="lastCreatedCategory" type="block" selector=".x-tree-root-ct li li:last-child" /> <element name="treeContainer" type="block" selector=".tree-holder" /> <element name="expandRootCategory" type="text" selector="img.x-tree-elbow-end-plus"/> + <element name="expandRootCategoryByName" type="button" selector="//div[@class='x-tree-root-node']/li/div/a/span[contains(., '{{categoryName}}')]/../../img[contains(@class, 'x-tree-elbow-end-plus')]" parameterized="true" timeout="30"/> <element name="categoryByName" type="text" selector="//div[contains(@class, 'categories-side-col')]//a/span[contains(text(), '{{categoryName}}')]" parameterized="true" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index 462f721913b9c..0934e39dcb062 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -93,6 +93,7 @@ <element name="DefaultValueText" type="textarea" selector="#default_value_text"/> <element name="DefaultValueTextArea" type="textarea" selector="#default_value_textarea"/> <element name="DefaultValueDate" type="textarea" selector="#default_value_date"/> + <element name="defaultValueDatetime" type="date" selector="#default_value_datetime"/> <element name="DefaultValueYesNo" type="textarea" selector="#default_value_yesno"/> <element name="Scope" type="select" selector="#is_global"/> <element name="UniqueValue" type="select" selector="#is_unique"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminNewWidgetSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminNewWidgetSection.xml index 5329ad48c8f43..8bc9c03642c15 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminNewWidgetSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminNewWidgetSection.xml @@ -10,5 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminNewWidgetSection"> <element name="selectProduct" type="button" selector=".btn-chooser" timeout="30"/> + <element name="selectCategory" type="button" selector="button[title='Select Category...']" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml index 6d4d5d86ef798..8802372968f65 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductCustomizableOptionsSection.xml @@ -9,6 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductCustomizableOptionsSection"> + <element name="requiredFieldIndicator" type="text" selector=" return window.getComputedStyle(document.querySelector('#product_composite_configure_form_fields .admin__field.required .admin__field-label'), ':after').getPropertyValue('content');"/> <element name="checkIfCustomizableOptionsTabOpen" type="text" selector="//span[text()='Customizable Options']/parent::strong/parent::*[@data-state-collapsible='closed']" timeout="30"/> <element name="customizableOptions" type="text" selector="//strong[contains(@class, 'admin__collapsible-title')]/span[text()='Customizable Options']"/> <element name="useDefaultOptionTitle" type="text" selector="[data-index='options'] tr.data-row [data-index='title'] [name^='options_use_default']"/> @@ -59,12 +60,12 @@ <element name="importOptions" type="button" selector="//button[@data-index='button_import']" timeout="30"/> </section> <section name="AdminProductImportOptionsSection"> - <element name="selectProductTitle" type="text" selector="//h1[contains(text(), 'Select Product')]" timeout="30"/> - <element name="filterButton" type="button" selector="//button[@data-action='grid-filter-expand']" timeout="30"/> - <element name="nameField" type="input" selector="//input[@name='name']" timeout="30"/> - <element name="applyFiltersButton" type="button" selector="//button[@data-action='grid-filter-apply']" timeout="30"/> - <element name="resetFiltersButton" type="button" selector="//button[@data-action='grid-filter-reset']" timeout="30"/> - <element name="firstRowItemCheckbox" type="input" selector="//input[@data-action='select-row']" timeout="30"/> - <element name="importButton" type="button" selector="//button[contains(@class, 'action-primary')]/span[contains(text(), 'Import')]" timeout="30"/> + <element name="selectProductTitle" type="text" selector="//aside[contains(@class, 'product_form_product_form_import_options_modal')]//h1[contains(text(), 'Select Product')]" timeout="30"/> + <element name="filterButton" type="button" selector="aside.product_form_product_form_import_options_modal button[data-action='grid-filter-expand']" timeout="30"/> + <element name="nameField" type="input" selector="aside.product_form_product_form_import_options_modal input[name='name']" timeout="30"/> + <element name="applyFiltersButton" type="button" selector="aside.product_form_product_form_import_options_modal button[data-action='grid-filter-apply']" timeout="30"/> + <element name="resetFiltersButton" type="button" selector="aside.product_form_product_form_import_options_modal button[data-action='grid-filter-reset']" timeout="30"/> + <element name="firstRowItemCheckbox" type="input" selector="aside.product_form_product_form_import_options_modal input[data-action='select-row']" timeout="30"/> + <element name="importButton" type="button" selector="//aside[contains(@class, 'product_form_product_form_import_options_modal')]//button[contains(@class, 'action-primary')]/span[contains(text(), 'Import')]" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index 7388ebc8408dd..7649f6c344c3a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -8,6 +8,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductFormSection"> + <element name="additionalOptions" type="select" selector=".admin__control-multiselect"/> <element name="datepickerNewAttribute" type="input" selector="[data-index='{{attrName}}'] input" timeout="30" parameterized="true"/> <element name="attributeSet" type="select" selector="div[data-index='attribute_set_id'] .admin__field-control"/> <element name="attributeSetFilter" type="input" selector="div[data-index='attribute_set_id'] .admin__field-control input" timeout="30"/> @@ -38,7 +39,7 @@ <element name="categoriesDropdown" type="multiselect" selector="div[data-index='category_ids']" timeout="30"/> <element name="unselectCategories" type="button" selector="//span[@class='admin__action-multiselect-crumb']/span[contains(.,'{{category}}')]/../button[@data-action='remove-selected-item']" parameterized="true" timeout="30"/> <element name="productQuantity" type="input" selector=".admin__field[data-index=qty] input"/> - <element name="advancedInventoryLink" type="button" selector="//button[contains(@data-index, 'advanced_inventory_button')]" timeout="30"/> + <element name="advancedInventoryLink" type="button" selector="button[data-index='advanced_inventory_button'].action-additional" timeout="30"/> <element name="productStockStatus" type="select" selector="select[name='product[quantity_and_stock_status][is_in_stock]']" timeout="30"/> <element name="productStockStatusDisabled" type="select" selector="select[name='product[quantity_and_stock_status][is_in_stock]'][disabled=true]"/> <element name="stockStatus" type="select" selector="[data-index='product-details'] select[name='product[quantity_and_stock_status][is_in_stock]']"/> @@ -157,6 +158,7 @@ <element name="FolderName" type="button" selector="input[data-role='promptField']" /> <element name="AcceptFolderName" type="button" selector=".action-primary.action-accept" timeout="30"/> <element name="StorageRootArrow" type="button" selector="#root > .jstree-icon" /> + <element name="FolderContainer" type="button" selector="div[data-role='tree']" /> <element name="checkIfArrowExpand" type="button" selector="//li[@id='root' and contains(@class,'jstree-closed')]" /> <element name="WysiwygArrow" type="button" selector="#d3lzaXd5Zw-- > .jstree-icon" /> <element name="checkIfWysiwygArrowExpand" type="button" selector="//li[@id='d3lzaXd5Zw--' and contains(@class,'jstree-closed')]" /> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml index 3b6f24c0f259d..4e86f14611c24 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductGridFilterSection.xml @@ -36,5 +36,7 @@ <element name="productCount" type="text" selector="#catalog_category_products-total-count"/> <element name="productPerPage" type="select" selector="#catalog_category_products_page-limit"/> <element name="storeViewDropdown" type="text" selector="//select[@name='store_id']/option[contains(.,'{{storeView}}')]" parameterized="true"/> + <element name="inputByCodeRangeFrom" type="input" selector="input.admin__control-text[name='{{code}}[from]']" parameterized="true"/> + <element name="inputByCodeRangeTo" type="input" selector="input.admin__control-text[name='{{code}}[to]']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml index ac7a15daf56aa..9a84f90edcfc0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml @@ -34,5 +34,6 @@ <element name="productsList" type="block" selector="#maincontent .column.main"/> <element name="productName" type="text" selector=".product-item-name"/> <element name="productOptionList" type="text" selector="#narrow-by-list"/> + <element name="productNameByPosition" type="text" selector=".products-grid li:nth-of-type({{position}}) .product-item-name a" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml index 136a8ceadb89e..2ec7997f87b7e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategorySidebarSection.xml @@ -8,6 +8,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCategorySidebarSection"> + <element name="layeredFilterBlock" type="block" selector="#layered-filter-block"/> <element name="filterOptionsTitle" type="text" selector="//div[@class='filter-options-title' and contains(text(), '{{var1}}')]" parameterized="true"/> <element name="filterOptions" type="text" selector=".filter-options-content .items"/> <element name="filterOption" type="text" selector=".filter-options-content .item"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index fd412d3c7dee1..631649e33b0fd 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -34,6 +34,7 @@ <element name="specialPriceValue" type="text" selector="//span[@class='special-price']//span[@class='price']"/> <element name="mapPrice" type="text" selector="//div[@class='price-box price-final_price']//span[contains(@class, 'price-msrp_price')]"/> <element name="clickForPriceLink" type="text" selector="//div[@class='price-box price-final_price']//a[contains(text(), 'Click for price')]"/> + <element name="addReviewLink" type="text" selector="//div[@class='reviews-actions']//a[@class='action add']"/> <!-- The parameter is the nth custom option that you want to get --> <element name="nthCustomOption" type="block" selector="//*[@id='product-options-wrapper']/*[@class='fieldset']/*[contains(@class, 'field')][{{customOptionNum}}]" parameterized="true" /> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml index 6ed359e35ab59..d3e43d9ea2dfa 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductMediaSection.xml @@ -17,5 +17,7 @@ <element name="imageFile" type="text" selector="//*[@class='product media']//img[contains(@src, '{{filename}}')]" parameterized="true"/> <element name="productImageActive" type="text" selector=".product.media div[data-active=true] > img[src*='{{filename}}']" parameterized="true"/> <element name="productImageInFotorama" type="file" selector=".fotorama__nav__shaft img[src*='{{imageName}}']" parameterized="true"/> + <element name="fotoramaPrevButton" type="button" selector="//*[@data-gallery-role='gallery']//*[@data-gallery-role='nav-wrap']//*[@data-gallery-role='arrow' and contains(@class, 'fotorama__thumb__arr--left')]"/> + <element name="fotoramaNextButton" type="button" selector="//*[@data-gallery-role='gallery']//*[@data-gallery-role='nav-wrap']//*[@data-gallery-role='arrow' and contains(@class, 'fotorama__thumb__arr--right')]"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontWidgetsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontWidgetsSection.xml index 87aab45bd8cb7..ca0c32f142852 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontWidgetsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontWidgetsSection.xml @@ -12,5 +12,6 @@ <element name="widgetRecentlyViewedProductsGrid" type="block" selector=".block.widget.block-viewed-products-grid"/> <element name="widgetRecentlyComparedProductsGrid" type="block" selector=".block.widget.block-compared-products-grid"/> <element name="widgetRecentlyOrderedProductsGrid" type="block" selector=".block.block-reorder"/> + <element name="widgetCategoryLinkByName" type="text" selector="//div[contains(@class, 'block-category-link')]/a/span[contains(., '{{categoryName}}')]" parameterized="true"/> </section> -</sections> \ No newline at end of file +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AddToCartCrossSellTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AddToCartCrossSellTest.xml index 53bb12fda4833..7c0161d443df6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AddToCartCrossSellTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AddToCartCrossSellTest.xml @@ -45,10 +45,10 @@ <amOnPage url="{{AdminProductEditPage.url($simpleProduct1.id$)}}" stepKey="goToProduct1"/> <click stepKey="openHeader1" selector="{{AdminProductFormRelatedUpSellCrossSellSection.sectionHeader}}"/> - <actionGroup ref="addCrossSellProductBySku" stepKey="addProduct2ToSimp1"> + <actionGroup ref="AddCrossSellProductBySkuActionGroup" stepKey="addProduct2ToSimp1"> <argument name="sku" value="$simpleProduct2.sku$"/> </actionGroup> - <actionGroup ref="addCrossSellProductBySku" stepKey="addProduct3ToSimp1"> + <actionGroup ref="AddCrossSellProductBySkuActionGroup" stepKey="addProduct3ToSimp1"> <argument name="sku" value="$simpleProduct3.sku$"/> </actionGroup> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave"/> @@ -58,17 +58,17 @@ <amOnPage url="{{AdminProductEditPage.url($simpleProduct3.id$)}}" stepKey="goToProduct3"/> <click stepKey="openHeader2" selector="{{AdminProductFormRelatedUpSellCrossSellSection.sectionHeader}}"/> - <actionGroup ref="addCrossSellProductBySku" stepKey="addProduct1ToSimp3"> + <actionGroup ref="AddCrossSellProductBySkuActionGroup" stepKey="addProduct1ToSimp3"> <argument name="sku" value="$simpleProduct1.sku$"/> </actionGroup> - <actionGroup ref="addCrossSellProductBySku" stepKey="addProduct2ToSimp3"> + <actionGroup ref="AddCrossSellProductBySkuActionGroup" stepKey="addProduct2ToSimp3"> <argument name="sku" value="$simpleProduct2.sku$"/> </actionGroup> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSave2"/> <waitForPageLoad stepKey="waitForPageLoad2"/> <!-- Go to frontend, add simpleProduct1 to cart--> - <actionGroup ref="AddSimpleProductToCart" stepKey="addSimp1ToCart"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addSimp1ToCart"> <argument name="product" value="$simpleProduct1$"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml index 117f094ee0607..ac1f967b66e41 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageSimpleProductTest.xml @@ -28,32 +28,32 @@ <!--Create product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPage"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateSimpleProduct"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateSimpleProduct"> <argument name="product" value="SimpleProduct3"/> </actionGroup> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillSimpleProductMain"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillSimpleProductMain"> <argument name="product" value="SimpleProduct3"/> </actionGroup> <!-- Add image to product --> - <actionGroup ref="addProductImage" stepKey="addImageForProductSimple"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImageForProductSimple"> <argument name="image" value="MagentoLogo"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveSimpleProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSimpleProduct"/> <!-- Assert product image in admin product form --> - <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertProductImageAdminProductPage"> + <actionGroup ref="AssertProductImageAdminProductPageActionGroup" stepKey="assertProductImageAdminProductPage"> <argument name="image" value="MagentoLogo"/> </actionGroup> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="SimpleProduct3"/> </actionGroup> <!-- Assert product image in storefront product page --> - <actionGroup ref="assertProductImageStorefrontProductPage" stepKey="assertProductImageStorefrontProductPage"> + <actionGroup ref="AssertProductImageStorefrontProductPageActionGroup" stepKey="assertProductImageStorefrontProductPage"> <argument name="product" value="SimpleProduct3"/> <argument name="image" value="MagentoLogo"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageVirtualProductTest.xml index 3f857c258924f..367827f8c0c28 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageVirtualProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultImageVirtualProductTest.xml @@ -28,30 +28,30 @@ <!--Create product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPage"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="defaultVirtualProduct"/> </actionGroup> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductMain"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillProductMain"> <argument name="product" value="defaultVirtualProduct"/> </actionGroup> <!-- Add image to product --> - <actionGroup ref="addProductImage" stepKey="addImageForProduct"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImageForProduct"> <argument name="image" value="MagentoLogo"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Assert product image in admin product form --> - <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertProductImageAdminProductPage"/> + <actionGroup ref="AssertProductImageAdminProductPageActionGroup" stepKey="assertProductImageAdminProductPage"/> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="defaultVirtualProduct"/> </actionGroup> <!-- Assert product image in storefront product page --> - <actionGroup ref="assertProductImageStorefrontProductPage" stepKey="assertProductImageStorefrontProductPage"> + <actionGroup ref="AssertProductImageStorefrontProductPageActionGroup" stepKey="assertProductImageStorefrontProductPage"> <argument name="product" value="defaultVirtualProduct"/> <argument name="image" value="MagentoLogo"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml index f657fbbdae607..0b33ef0ac0783 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml @@ -29,19 +29,19 @@ <!-- Create product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="adminProductIndexPageAdd"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProductPage"> <argument name="product" value="ApiSimpleProduct"/> </actionGroup> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillMainProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillMainProductForm"> <argument name="product" value="ApiSimpleProduct"/> </actionGroup> <!-- Save product --> - <actionGroup ref="saveProductForm" stepKey="saveProductForm"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="ApiSimpleProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoVirtualProductTest.xml index eab36bc90dc18..e89cf6f4242e7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoVirtualProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddDefaultVideoVirtualProductTest.xml @@ -21,13 +21,13 @@ <!-- Replacing steps in base AdminAddDefaultVideoSimpleProductTest --> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProductPage"> <argument name="product" value="defaultVirtualProduct"/> </actionGroup> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillMainProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillMainProductForm"> <argument name="product" value="defaultVirtualProduct"/> </actionGroup> - <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="defaultVirtualProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageForCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageForCategoryTest.xml index 8ac0cfa512b03..ed9eb686d2c86 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageForCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageForCategoryTest.xml @@ -22,27 +22,27 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="DeleteCategory" stepKey="DeleteCategory"> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="DeleteCategory"> <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> </after> <!-- Go to create a new category with image --> - <actionGroup ref="goToCreateCategoryPage" stepKey="goToCreateCategoryPage"/> - <actionGroup ref="fillCategoryForm" stepKey="fillCategoryForm"> + <actionGroup ref="GoToCreateCategoryPageActionGroup" stepKey="goToCreateCategoryPage"/> + <actionGroup ref="FillCategoryFormActionGroup" stepKey="fillCategoryForm"> <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> - <actionGroup ref="addCategoryImage" stepKey="addCategoryImage"/> - <actionGroup ref="saveCategoryForm" stepKey="saveCategoryForm"/> + <actionGroup ref="AddCategoryImageActionGroup" stepKey="addCategoryImage"/> + <actionGroup ref="SaveCategoryFormActionGroup" stepKey="saveCategoryForm"/> <!-- Verify category with image in admin --> - <actionGroup ref="checkCategoryImageInAdmin" stepKey="checkCategoryImageInAdmin"/> + <actionGroup ref="CheckCategoryImageInAdminActionGroup" stepKey="checkCategoryImageInAdmin"/> <!-- Verify category with image in storefront --> - <actionGroup ref="CheckCategoryOnStorefront" stepKey="CheckCategoryOnStorefront"> + <actionGroup ref="CheckCategoryOnStorefrontActionGroup" stepKey="CheckCategoryOnStorefront"> <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> <seeElement selector="{{StorefrontCategoryMainSection.imageSource(ProductImage.filename)}}" stepKey="seeImage"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGCatalogTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGCatalogTest.xml index 50d192a27e46d..b3e6fcd3bfb55 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGCatalogTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGCatalogTest.xml @@ -10,7 +10,7 @@ <test name="AdminAddImageToWYSIWYGCatalogTest"> <before> <actionGroup ref="LoginActionGroup" stepKey="login"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <annotations> @@ -31,27 +31,30 @@ <waitForElementVisible selector="{{CatalogWYSIWYGSection.TinyMCE4}}" stepKey="waitForTinyMCE4" /> <click selector="{{CatalogWYSIWYGSection.InsertImageIcon}}" stepKey="clickInsertImageIcon" /> <waitForPageLoad stepKey="waitForPageLoad" /> - <actionGroup ref="clickBrowseBtnOnUploadPopup" stepKey="clickBrowserBtn"/> - <actionGroup ref="VerifyMediaGalleryStorageActions" stepKey="VerifyMediaGalleryStorageBtn"/> - <actionGroup ref="CreateImageFolder" stepKey="CreateImageFolder"> + <actionGroup ref="ClickBrowseBtnOnUploadPopupActionGroup" stepKey="clickBrowserBtn"/> + <actionGroup ref="VerifyMediaGalleryStorageActionsActionGroup" stepKey="VerifyMediaGalleryStorageBtn"/> + <actionGroup ref="CreateImageFolderActionGroup" stepKey="CreateImageFolder"> <argument name="ImageFolder" value="ImageFolder"/> </actionGroup> - <actionGroup ref="attachImage" stepKey="attachImage1"> + <actionGroup ref="AttachImageActionGroup" stepKey="attachImage1"> <argument name="Image" value="ImageUpload3"/> </actionGroup> - <actionGroup ref="deleteImage" stepKey="deleteImage"/> - <actionGroup ref="attachImage" stepKey="attachImage2"> + <actionGroup ref="DeleteImageActionGroup" stepKey="deleteImage"/> + <actionGroup ref="AttachImageActionGroup" stepKey="attachImage2"> <argument name="Image" value="ImageUpload3"/> </actionGroup> - <actionGroup ref="saveImage" stepKey="insertImage"/> - <actionGroup ref="fillOutUploadImagePopup" stepKey="fillOutUploadImagePopup" /> + <actionGroup ref="SaveImageActionGroup" stepKey="insertImage"/> + <actionGroup ref="FillOutUploadImagePopupActionGroup" stepKey="fillOutUploadImagePopup" /> <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCatalog"/> <amOnPage url="/{{SimpleSubCategory.name_lwr}}.html" stepKey="goToCategoryFrontPage"/> <waitForPageLoad stepKey="waitForPageLoad2"/> <seeElement selector="{{StorefrontCategoryMainSection.mediaDescription(ImageUpload3.content)}}" stepKey="assertMediaDescription"/> <seeElementInDOM selector="{{StorefrontCategoryMainSection.imageSource(ImageUpload3.fileName)}}" stepKey="assertMediaSource"/> <after> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="DeleteCategory"> + <argument name="categoryEntity" value="SimpleSubCategory"/> + </actionGroup> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml index ee105320c5f29..edf37542fd830 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml @@ -19,17 +19,17 @@ </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="login"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <after> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> <amOnPage url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, 'simple')}}" stepKey="navigateToNewProduct"/> <waitForPageLoad stepKey="waitForPageLoadProductCreatePage"/> - <actionGroup ref="fillMainProductForm" stepKey="fillBasicProductInfo" /> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillBasicProductInfo" /> <click selector="{{AdminProductFormSection.contentTab}}" stepKey="clickContentTab" /> <waitForElementVisible selector="{{ProductDescriptionWYSIWYGToolbarSection.TinyMCE4}}" stepKey="waitForDescription" /> @@ -70,11 +70,12 @@ <scrollTo selector="{{ProductDescriptionWYSIWYGToolbarSection.TinyMCE4}}" stepKey="scrollToTinyMCE4" /> <click selector="{{ProductShortDescriptionWYSIWYGToolbarSection.InsertImageIcon}}" stepKey="clickInsertImageIcon2" /> <click selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Browse}}" stepKey="clickBrowse2" /> + <waitForLoadingMaskToDisappear stepKey="waitForLoading13"/> <waitForElementVisible selector="{{ProductDescriptionWYSIWYGToolbarSection.CancelBtn}}" stepKey="waitForCancelButton2"/> <see selector="{{ProductShortDescriptionWYSIWYGToolbarSection.CancelBtn}}" userInput="Cancel" stepKey="seeCancelBtn2" /> - <waitForLoadingMaskToDisappear stepKey="waitForLoading13"/> + <waitForElementVisible selector="{{ProductDescriptionWYSIWYGToolbarSection.CreateFolder}}" stepKey="waitForCreateFolderBtn2"/> <see selector="{{ProductShortDescriptionWYSIWYGToolbarSection.CreateFolder}}" userInput="Create Folder" stepKey="seeCreateFolderBtn2" /> - <waitForLoadingMaskToDisappear stepKey="waitForLoading14"/> + <see selector="{{ProductDescriptionWYSIWYGToolbarSection.FolderContainer}}" userInput="Storage Root" stepKey="seeFolderContainer" /> <click userInput="Storage Root" stepKey="clickOnRootFolder" /> <waitForLoadingMaskToDisappear stepKey="waitForLoading15"/> <dontSeeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.InsertFile}}" stepKey="dontSeeAddSelectedBtn3" /> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml index 51ef7fb77d74c..b98ca3a375a17 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml @@ -36,7 +36,7 @@ <!--Open Product Index Page and filter the product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProduct"> <argument name="product" value="SimpleProduct"/> </actionGroup> <!-- Update product Advanced Inventory setting --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml index 545e7c10379bf..4f1618e076642 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminApplyTierPriceToProductTest.xml @@ -32,7 +32,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex1"/> <waitForPageLoad time="30" stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetGridToDefaultKeywordSearch"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> @@ -51,7 +51,7 @@ <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceValueTypeSelect('0')}}" userInput="Discount" stepKey="selectProductTierPriceValueType1"/> <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPricePercentageValuePriceInput('0')}}" userInput="10" stepKey="selectProductTierPricePriceInput"/> <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton1"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct1"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin1"> <argument name="Customer" value="$$createSimpleUSCustomer$$" /> </actionGroup> @@ -60,7 +60,7 @@ <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('90')}}" stepKey="assertProductFinalPriceIs90_1"/> <seeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('Regular Price')}}" stepKey="assertRegularPriceLabel_1"/> <seeElement selector="{{StorefrontCategoryProductSection.productPriceOld('100')}}" stepKey="assertRegularPriceAmount_1"/> - <amOnPage url="customer/account/logout/" stepKey="logoutCustomer1"/> + <amOnPage url="{{StorefrontCustomerLogoutPage.url}}" stepKey="logoutCustomer1"/> <waitForPageLoad time="30" stepKey="waitForPageLoad2"/> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage2"/> <waitForPageLoad time="30" stepKey="waitForPageLoad3"/> @@ -79,7 +79,7 @@ <waitForElement selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" time="30" stepKey="waitForSelectCustomerGroupNameAttribute1"/> <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" userInput="General" stepKey="selectCustomerGroupGeneral"/> <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton2"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct2"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct2"/> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage3"/> <waitForPageLoad time="30" stepKey="waitForPageLoad4"/> <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('100')}}" stepKey="assertProductFinalPriceIs100_1"/> @@ -110,13 +110,13 @@ <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceValueTypeSelect('1')}}" userInput="Discount" stepKey="selectProductTierPriceValueType2"/> <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPricePercentageValuePriceInput('1')}}" userInput="18" stepKey="selectProductTierPricePriceInput18"/> <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton3"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct3"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct3"/> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage5"/> <waitForPageLoad time="30" stepKey="waitForPageLoad6"/> <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('100')}}" stepKey="assertProductFinalPriceIs100_2"/> <seeElement selector="{{StorefrontCategoryProductSection.productPriceLabel('As low as')}}" stepKey="assertAsLowAsPriceLabel_1"/> <seeElement selector="{{StorefrontCategoryProductSection.productPriceLinkAfterLabel('As low as', '82')}}" stepKey="assertPriceAfterAsLowAsLabel_1"/> - <amOnPage url="customer/account/logout/" stepKey="logoutCustomer2"/> + <amOnPage url="{{StorefrontCustomerLogoutPage.url}}" stepKey="logoutCustomer2"/> <waitForPageLoad time="30" stepKey="waitForPageLoad7"/> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage6"/> <waitForPageLoad time="30" stepKey="waitForPageLoad8"/> @@ -132,10 +132,10 @@ <seeElement selector="{{StorefrontProductInfoMainSection.productTierPriceSavePercentageAmount('1', '10')}}" stepKey="assertProductTierPriceSavePercentageAmountForFirstRow1"/> <seeElement selector="{{StorefrontProductInfoMainSection.productTierPriceSavePercentageAmount('2', '18')}}" stepKey="assertProductTierPriceSavePercentageAmountForSecondRow1"/> <fillField userInput="10" selector="{{StorefrontProductInfoMainSection.qty}}" stepKey="fillProductQuantity1"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> <argument name="productName" value="$$createSimpleProduct.name$$"/> </actionGroup> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToCheckoutFromMinicart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToCheckoutFromMinicart"/> <seeInField userInput="10" selector="{{CheckoutCartProductSection.ProductQuantityByName($$createSimpleProduct.name$$)}}" stepKey="seeInQtyField10"/> <grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($$createSimpleProduct.name$$)}}" stepKey="grabTextFromSubtotalField1"/> <assertEquals message="Shopping cart should contain subtotal $1,000" stepKey="assertSubtotalField1"> @@ -169,7 +169,7 @@ <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForcustomerGroupPriceAddButton4"/> <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPricePercentageValuePriceInput('1')}}" userInput="25" stepKey="selectProductTierPricePercentageValue2"/> <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton4"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct4"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct4"/> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToShoppingCartPage1"/> <waitForPageLoad time="30" stepKey="waitForShoppingCartPagePageLoad1"/> <seeInField userInput="20" selector="{{CheckoutCartProductSection.ProductQuantityByName($$createSimpleProduct.name$$)}}" stepKey="seeInQtyField20"/> @@ -239,7 +239,7 @@ <click selector="(//tr//button[@data-action='remove_row'])[1]" userInput=".product_form_product_form_advanced_pricing_modal" stepKey="deleteFirstRowOfCustomerGroupPrice"/> <click selector="//tr//button[@data-action='remove_row']" userInput=".product_form_product_form_advanced_pricing_modal" stepKey="deleteSecondRowOfCustomerGroupPrice"/> <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" userInput=".product_form_product_form_advanced_pricing_modal" stepKey="clickDoneButton5"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct5"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct5"/> <scrollToTopOfPage stepKey="scrollToTopOfPage6"/> <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton6"/> <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForcustomerGroupPriceAddButton5"/> @@ -291,7 +291,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad time="30" stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetGridToDefaultKeywordSearch"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> @@ -309,7 +309,7 @@ <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceValueTypeSelect('0')}}" userInput="Discount" stepKey="selectProductTierPriceValueType"/> <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPricePercentageValuePriceInput('0')}}" userInput="0.1" stepKey="selectProductTierPricePriceInput"/> <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct1"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1"/> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.sku$$)}}" stepKey="goProductPageOnStorefront"/> <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> <seeElement selector="{{StorefrontCategoryProductSection.productPriceFinal('99.90')}}" stepKey="assertProductFinalPriceProductPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml index 4261721d36064..41b358bbf760e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml @@ -40,14 +40,14 @@ <!-- Assert created attribute in unassigned section --> <see userInput="$$attribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.unassignedAttributesTree}}" stepKey="seeAttributeInUnassigned"/> <!-- Assign attribute to a group --> - <actionGroup ref="AssignAttributeToGroup" stepKey="assignAttributeToGroup"> + <actionGroup ref="AssignAttributeToGroupActionGroup" stepKey="assignAttributeToGroup"> <argument name="group" value="Product Details"/> <argument name="attribute" value="$$attribute.attribute_code$$"/> </actionGroup> <!-- Assert attribute in a group --> <see userInput="$$attribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.groupTree}}" stepKey="seeAttributeInGroup"/> <!-- Save attribute set --> - <actionGroup ref="SaveAttributeSet" stepKey="SaveAttributeSet"/> + <actionGroup ref="SaveAttributeSetActionGroup" stepKey="SaveAttributeSet"/> <!-- Go to create new product page --> <amOnPage url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, 'simple')}}" stepKey="navigateToNewProduct"/> <waitForPageLoad stepKey="waitForPageLoad"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml index 88c524eff387c..9361637a0a935 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml @@ -35,7 +35,7 @@ </after> <!-- Go to the storefront and add the product to the cart --> - <actionGroup ref="AddSimpleProductToCart" stepKey="gotoAndAddProductToCart"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="gotoAndAddProductToCart"> <argument name="product" value="$$createProduct$$"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml index bcfab6ccfdf1f..95620bf75b6d0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml @@ -35,11 +35,11 @@ <actionGroup ref="LoginAsAdmin" stepKey="login"/> <amOnPage url="{{AdminProductAttributeSetEditPage.url}}/$$createAttributeSet.attribute_set_id$$/" stepKey="onAttributeSetEdit"/> - <actionGroup ref="AssignAttributeToGroup" stepKey="assignAttributeToGroup"> + <actionGroup ref="AssignAttributeToGroupActionGroup" stepKey="assignAttributeToGroup"> <argument name="group" value="Product Details"/> <argument name="attribute" value="$$createProductAttribute.attribute_code$$"/> </actionGroup> - <actionGroup ref="SaveAttributeSet" stepKey="SaveAttributeSet"/> + <actionGroup ref="SaveAttributeSetActionGroup" stepKey="SaveAttributeSet"/> </before> <after> <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml index 86978a4121a43..b4381a674827d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml @@ -145,7 +145,7 @@ <!-- Open Product Index Page and Filter First Child product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProduct"> <argument name="product" value="ApiSimpleOne"/> </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="selectFirstRow"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsNotVisibleInCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsNotVisibleInCategoryTest.xml index ee8b48a94b20d..cd03d868838f3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsNotVisibleInCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsNotVisibleInCategoryTest.xml @@ -36,7 +36,7 @@ <!--Open Product Index Page and filter the product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProduct"> <argument name="product" value="SimpleProduct"/> </actionGroup> <!-- Update product Advanced Inventory Setting --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsVisibleInCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsVisibleInCategoryTest.xml index a863de2716c97..bfea4fa7557f5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsVisibleInCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckOutOfStockProductIsVisibleInCategoryTest.xml @@ -39,7 +39,7 @@ <!--Open Product Index Page and filter the product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProduct"> <argument name="product" value="SimpleProduct"/> </actionGroup> <!-- Update product Advanced Inventory Setting --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml index 47e206768527b..5bb9cc8a080df 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckPaginationInStorefrontTest.xml @@ -16,9 +16,6 @@ <severity value="CRITICAL"/> <group value="mtf_migrated"/> <group value="Catalog"/> - <skip> - <issueId value="MC-21962"/> - </skip> </annotations> <before> <magentoCLI stepKey="setFlatCatalogCategory" command="config:set catalog/frontend/flat_catalog_category 1 "/> @@ -118,7 +115,7 @@ <click selector="{{CatalogProductsSection.resetFilter}}" stepKey="clickOnResetFilter"/> <waitForPageLoad stepKey="waitForPageToLoad3"/> <selectOption selector="{{AdminProductGridFilterSection.productPerPage}}" userInput="30" stepKey="selectPagePerView"/> - <wait stepKey="waitFroPageToLoad1" time="30"/> + <waitForPageLoad stepKey="waitForPageToLoadProductPerPage" time="30"/> <fillField selector="{{AdminCategoryContentSection.productTableColumnName}}" userInput="pagi" stepKey="selectProduct1"/> <click selector="{{AdminCategoryContentSection.productSearch}}" stepKey="clickSearchButton"/> <waitForPageLoad stepKey="waitFroPageToLoad2"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCloneProductWithDuplicateUrlTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCloneProductWithDuplicateUrlTest.xml index 99adaeb522786..ee9e364758899 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCloneProductWithDuplicateUrlTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCloneProductWithDuplicateUrlTest.xml @@ -28,10 +28,10 @@ <after> <!--Delete created data--> <comment userInput="Delete created data" stepKey="commentDeleteCreatedData"/> - <actionGroup ref="deleteAllDuplicateProductUsingProductGrid" stepKey="deleteAllDuplicateProducts"> + <actionGroup ref="DeleteAllDuplicateProductUsingProductGridActionGroup" stepKey="deleteAllDuplicateProducts"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetFiltersIfExist"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetFiltersIfExist"/> <actionGroup ref="logout" stepKey="logoutOfAdmin"/> </after> <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToProductEditPage"/> @@ -47,16 +47,16 @@ <comment userInput="Add duplicated product to the simple product" stepKey="commentAddProduct"/> <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToSimpleProductPage"/> <waitForPageLoad stepKey="waitForSimpleProductPageLoad1"/> - <actionGroup ref="addCrossSellProductBySku" stepKey="addCrossSellProduct"> + <actionGroup ref="AddCrossSellProductBySkuActionGroup" stepKey="addCrossSellProduct"> <argument name="sku" value="$$createSimpleProduct.sku$$"/> </actionGroup> - <actionGroup ref="addRelatedProductBySku" stepKey="addRelatedProduct"> + <actionGroup ref="AddRelatedProductBySkuActionGroup" stepKey="addRelatedProduct"> <argument name="sku" value="$$createSimpleProduct.sku$$"/> </actionGroup> - <actionGroup ref="addUpSellProductBySku" stepKey="addUpSellProduct"> + <actionGroup ref="AddUpSellProductBySkuActionGroup" stepKey="addUpSellProduct"> <argument name="sku" value="$$createSimpleProduct.sku$$"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="clickSaveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveProduct"/> <conditionalClick selector="{{AdminProductFormRelatedUpSellCrossSellSection.sectionHeader}}" dependentSelector="{{AdminProductFormRelatedUpSellCrossSellSection.AddRelatedProductsButton}}" visible="false" stepKey="openProductRUSSection"/> <see selector="{{AdminProductFormRelatedUpSellCrossSellSection.selectedProductSku('related')}}" userInput="$$createSimpleProduct.sku$$-1" stepKey="seeRelatedProduct"/> <see selector="{{AdminProductFormRelatedUpSellCrossSellSection.selectedProductSku('upsell')}}" userInput="$$createSimpleProduct.sku$$-1" stepKey="seeUpSellProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml index 4d97dee56f059..b5ab36729c7fe 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminConfigureProductImagePlaceholderTest.xml @@ -62,9 +62,9 @@ <deleteData createDataKey="productNoImages" stepKey="deleteProductNoImages"/> <deleteData createDataKey="productWithImages" stepKey="deleteProductWithImages"/> </after> - + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> - + <!--Admin area: configure Product Image Placeholders--> <comment userInput="Configure product image placeholders in store config" stepKey="configurePlaceholderComment"/> <amOnPage url="{{CatalogConfigPage.url}}" stepKey="goToCatalogConfigurationPage"/> @@ -120,7 +120,7 @@ <actualResult type="variable">$getThumbnailPlaceholderImageSrc</actualResult> <expectedResult type="string">{{placeholderThumbnailImage.name}}</expectedResult> </assertContains> - <actionGroup ref="removeProductFromMiniCart" stepKey="removeProductFromCart1"> + <actionGroup ref="RemoveProductFromMiniCartActionGroup" stepKey="removeProductFromCart1"> <argument name="productName" value="$$productNoImages.name$$"/> </actionGroup> <!--Product which is NOT using placeholder--> @@ -138,7 +138,7 @@ <actualResult type="variable">$getThumbnailImageSrc</actualResult> <expectedResult type="string">{{placeholderThumbnailImage.name}}</expectedResult> </assertNotContains> - <actionGroup ref="removeProductFromMiniCart" stepKey="removeProductFromCart2"> + <actionGroup ref="RemoveProductFromMiniCartActionGroup" stepKey="removeProductFromCart2"> <argument name="productName" value="$$productWithImages.name$$"/> </actionGroup> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditSimpleProductSettingsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditSimpleProductSettingsTest.xml index 8ffab42653b49..80c20a7e0b5d9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditSimpleProductSettingsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditSimpleProductSettingsTest.xml @@ -38,16 +38,16 @@ </after> <!-- Create new simple product --> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="createSimpleProduct"/> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createSimpleProduct"/> <!-- Fill all main fields --> - <actionGroup ref="fillMainProductForm" stepKey="fillAllNecessaryFields"/> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillAllNecessaryFields"/> <!-- Add two related products --> - <actionGroup ref="addRelatedProductBySku" stepKey="addFirstRelatedProduct"> + <actionGroup ref="AddRelatedProductBySkuActionGroup" stepKey="addFirstRelatedProduct"> <argument name="sku" value="$$createFirstRelatedProduct.sku$$"/> </actionGroup> - <actionGroup ref="addRelatedProductBySku" stepKey="addSecondRelatedProduct"> + <actionGroup ref="AddRelatedProductBySkuActionGroup" stepKey="addSecondRelatedProduct"> <argument name="sku" value="$$createSecondRelatedProduct.sku$$"/> </actionGroup> @@ -60,7 +60,7 @@ </actionGroup> <!-- Save product form --> - <actionGroup ref="saveProductForm" stepKey="clickSaveButton"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveButton"/> <!-- Open product page --> <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openStorefrontProductPage"> @@ -96,7 +96,7 @@ </actionGroup> <!-- Edit related products --> - <actionGroup ref="addRelatedProductBySku" stepKey="addThirdRelatedProduct"> + <actionGroup ref="AddRelatedProductBySkuActionGroup" stepKey="addThirdRelatedProduct"> <argument name="sku" value="$$createThirdRelatedProduct.sku$$"/> </actionGroup> <click selector="{{AdminProductFormRelatedUpSellCrossSellSection.removeRelatedProduct($$createFirstRelatedProduct.sku$$)}}" stepKey="removeFirstRelatedProduct"/> @@ -110,7 +110,7 @@ <actionGroup ref="AdminSwitchProductGiftMessageStatusActionGroup" stepKey="disableGiftMessageSettings"/> <!-- Save product form --> - <actionGroup ref="saveProductForm" stepKey="clickSaveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveProduct"/> <!-- Verify Url Key after changing --> <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openProductPage"> @@ -129,7 +129,7 @@ <dontSeeElement selector="{{StorefrontProductCartGiftOptionSection.giftOptions}}" stepKey="dontSeeGiftOptionBtn"/> <!-- Delete created simple product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="_defaultProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditVirtualProductSettingsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditVirtualProductSettingsTest.xml index 90cbab59bd71d..34451aba78f21 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditVirtualProductSettingsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndEditVirtualProductSettingsTest.xml @@ -33,7 +33,7 @@ </before> <after> <!-- Delete created virtual product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="defaultVirtualProduct"/> </actionGroup> @@ -51,15 +51,15 @@ <actionGroup ref="logout" stepKey="adminLogout"/> </after> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <!-- Create new virtual product --> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="createVirtualProduct"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createVirtualProduct"> <argument name="productType" value="virtual"/> </actionGroup> <!-- Fill all main fields --> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillMainProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillMainProductForm"> <argument name="product" value="defaultVirtualProduct"/> </actionGroup> @@ -70,10 +70,10 @@ <fillField selector="{{AdminProductContentSection.shortDescriptionTextArea}}" userInput="{{ApiProductShortDescription.value}}" stepKey="fillShortDescription"/> <!-- Add two related products --> - <actionGroup ref="addRelatedProductBySku" stepKey="addFirstRelatedProduct"> + <actionGroup ref="AddRelatedProductBySkuActionGroup" stepKey="addFirstRelatedProduct"> <argument name="sku" value="$$createFirstRelatedProduct.sku$$"/> </actionGroup> - <actionGroup ref="addRelatedProductBySku" stepKey="addSecondRelatedProduct"> + <actionGroup ref="AddRelatedProductBySkuActionGroup" stepKey="addSecondRelatedProduct"> <argument name="sku" value="$$createSecondRelatedProduct.sku$$"/> </actionGroup> @@ -93,7 +93,7 @@ </actionGroup> <!-- Save product form--> - <actionGroup ref="saveProductForm" stepKey="clickSaveButton"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveButton"/> <!-- Open product page --> <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openStorefrontProductPage"> @@ -139,12 +139,12 @@ </actionGroup> <!-- Edit related products --> - <actionGroup ref="addRelatedProductBySku" stepKey="addThirdRelatedProduct"> + <actionGroup ref="AddRelatedProductBySkuActionGroup" stepKey="addThirdRelatedProduct"> <argument name="sku" value="$$createThirdRelatedProduct.sku$$"/> </actionGroup> <!-- Assert product in assigned to websites --> - <actionGroup ref="AssertProductIsAssignedToWebsite" stepKey="seeCustomWebsiteIsChecked"> + <actionGroup ref="AssertProductIsAssignedToWebsiteActionGroup" stepKey="seeCustomWebsiteIsChecked"> <argument name="website" value="$createWebsite.website[name]$"/> </actionGroup> @@ -162,7 +162,7 @@ <actionGroup ref="AdminSwitchProductGiftMessageStatusActionGroup" stepKey="disableGiftMessageSettings"/> <!-- Save product form --> - <actionGroup ref="saveProductForm" stepKey="clickSaveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveProduct"/> <!-- Verify Url Key after changing --> <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openProductPage"> @@ -185,6 +185,6 @@ <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openCart"/> <dontSeeElement selector="{{StorefrontProductCartGiftOptionSection.giftOptions}}" stepKey="dontSeeGiftOptionBtn"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml index 4deca73504677..12082e1daa6c3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml @@ -23,11 +23,11 @@ <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> </before> <after> - <actionGroup ref="GoToProductCatalogPage" stepKey="goToProductCatalogPage"/> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteSimpleProduct"> + <actionGroup ref="GoToProductCatalogPageActionGroup" stepKey="goToProductCatalogPage"/> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteSimpleProduct"> <argument name="product" value="_defaultProduct"/> </actionGroup> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetSearch"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetSearch"/> <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> </after> @@ -35,28 +35,28 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> <!-- Open Dropdown and select simple product option --> <comment stepKey="beforeOpenProductFillForm" userInput="Selecting Product from the Add Product Dropdown"/> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="openProductFillForm"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> <argument name="productType" value="simple"/> </actionGroup> <!-- Fill form for Virtual Product Type --> <comment stepKey="beforeFillProductForm" userInput="Filling Product Form"/> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillProductForm"> <argument name="product" value="_defaultProduct"/> </actionGroup> - <actionGroup ref="SetProductUrlKey" stepKey="setProductUrl"> + <actionGroup ref="SetProductUrlKeyActionGroup" stepKey="setProductUrl"> <argument name="product" value="_defaultProduct"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProductForm"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> <!-- Check that product was added with implicit type change --> <comment stepKey="beforeVerify" userInput="Verify Product Type Assigned Correctly"/> - <actionGroup ref="GoToProductCatalogPage" stepKey="goToProductCatalogPage"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetSearch"/> - <actionGroup ref="filterProductGridByName" stepKey="searchForProduct"> + <actionGroup ref="GoToProductCatalogPageActionGroup" stepKey="goToProductCatalogPage"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetSearch"/> + <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="searchForProduct"> <argument name="product" value="_defaultProduct"/> </actionGroup> <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Virtual Product" stepKey="seeProductTypeInGrid"/> - <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="_defaultProduct"/> </actionGroup> </test> @@ -71,11 +71,11 @@ <group value="catalog"/> <group value="mtf_migrated"/> </annotations> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="openProductFillForm"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> <argument name="productType" value="virtual"/> </actionGroup> <!-- Fill form for Virtual Product Type --> - <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="_defaultProduct"/> </actionGroup> <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Simple Product" stepKey="seeProductTypeInGrid"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAttributeSetEntityTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAttributeSetEntityTest.xml index d9e410a9a3009..51518dffaf87e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAttributeSetEntityTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAttributeSetEntityTest.xml @@ -30,7 +30,7 @@ <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/> <waitForPageLoad stepKey="waitForPageLoad"/> - <actionGroup ref="goToAttributeSetByName" stepKey="filterProductAttributeSetGridByLabel"> + <actionGroup ref="GoToAttributeSetByNameActionGroup" stepKey="filterProductAttributeSetGridByLabel"> <argument name="name" value="$$createAttributeSet.attribute_set_name$$"/> </actionGroup> @@ -38,17 +38,17 @@ <see userInput="$$createProductAttribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.unassignedAttributesTree}}" stepKey="seeAttributeInUnassignedAttr"/> <!-- Assign attribute in the group --> - <actionGroup ref="AssignAttributeToGroup" stepKey="assignAttributeToGroup"> + <actionGroup ref="AssignAttributeToGroupActionGroup" stepKey="assignAttributeToGroup"> <argument name="group" value="Product Details"/> <argument name="attribute" value="$$createProductAttribute.attribute_code$$"/> </actionGroup> <see userInput="$$createProductAttribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.groupTree}}" stepKey="seeAttributeInGroup"/> - <actionGroup ref="SaveAttributeSet" stepKey="SaveAttributeSet"/> + <actionGroup ref="SaveAttributeSetActionGroup" stepKey="SaveAttributeSet"/> <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets2"/> <waitForPageLoad stepKey="waitForPageLoad2"/> <!-- Assert an attribute in the group--> - <actionGroup ref="goToAttributeSetByName" stepKey="filterProductAttributeSetGridByLabel2"> + <actionGroup ref="GoToAttributeSetByNameActionGroup" stepKey="filterProductAttributeSetGridByLabel2"> <argument name="name" value="$$createAttributeSet.attribute_set_name$$"/> </actionGroup> <see userInput="$$createProductAttribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.groupTree}}" stepKey="seeAttributeInGroup2"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryFromProductPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryFromProductPageTest.xml index a5150a0fb7f24..ef21b53c7613b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryFromProductPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryFromProductPageTest.xml @@ -26,7 +26,7 @@ <after> <!-- Delete the created category --> - <actionGroup ref="DeleteMostRecentCategory" stepKey="getRidOfCreatedCategory"/> + <actionGroup ref="DeleteMostRecentCategoryActionGroup" stepKey="getRidOfCreatedCategory"/> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> <deleteData createDataKey="simpleProduct" stepKey="deleteProduct"/> </after> @@ -35,7 +35,7 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> <waitForPageLoad stepKey="waitForAdminProductGridLoad"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <actionGroup ref="filterProductGridBySku" stepKey="findCreatedProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct"> <argument name="product" value="SimpleTwo"/> </actionGroup> <waitForPageLoad stepKey="waitForFiltersToBeApplied"/> @@ -43,12 +43,12 @@ <waitForPageLoad stepKey="waitForProductPageLoad"/> <!-- Fill out the form for the new category --> - <actionGroup ref="FillNewProductCategory" stepKey="FillNewProductCategory"> + <actionGroup ref="FillNewProductCategoryActionGroup" stepKey="FillNewProductCategory"> <argument name="categoryName" value="{{_defaultCategory.name}}"/> </actionGroup> <!-- Check that category was created --> - <actionGroup ref="CategoryPresent" stepKey="checkIfCategoryPresent"> + <actionGroup ref="CategoryPresentActionGroup" stepKey="checkIfCategoryPresent"> <argument name="categoryName" value="{{_defaultCategory.name}}"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithAnchorFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithAnchorFieldTest.xml index a6890c2ad4905..7de1d38f097c5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithAnchorFieldTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithAnchorFieldTest.xml @@ -23,7 +23,7 @@ <createData entity="defaultSimpleProduct" stepKey="simpleProduct" /> </before> <after> - <actionGroup ref="DeleteCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"/> <actionGroup ref="logout" stepKey="logout"/> <deleteData createDataKey="createDefaultCMSBlock" stepKey="deleteDefaultCMSBlock"/> <deleteData stepKey="deleteSimpleProduct" createDataKey="simpleProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithCustomRootCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithCustomRootCategoryTest.xml index e8c6da476a3d6..a68a585bbf31d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithCustomRootCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithCustomRootCategoryTest.xml @@ -26,7 +26,7 @@ <actionGroup ref="DeleteCustomStoreActionGroup" stepKey="deleteCustomStore"> <argument name="storeGroupName" value="customStore.name"/> </actionGroup> - <actionGroup ref="DeleteCategory" stepKey="deleteCreatedNewRootCategory"> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCreatedNewRootCategory"> <argument name="categoryEntity" value="NewRootCategory"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> @@ -41,7 +41,7 @@ <scrollToTopOfPage stepKey="scrollToTopOfPage"/> <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(NewRootCategory.name)}}" stepKey="clickOnCreatedNewRootCategory"/> <scrollToTopOfPage stepKey="scrollToTopOfPage1"/> - <actionGroup ref="CreateCategory" stepKey="createSubcategory"> + <actionGroup ref="CreateCategoryActionGroup" stepKey="createSubcategory"> <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> <!--Create a Store--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveCategoryTest.xml index 96f945da138b0..fae1a1fa8c2e4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveCategoryTest.xml @@ -21,7 +21,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> </before> <after> - <actionGroup ref="DeleteCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"/> <actionGroup ref="logout" stepKey="logout"/> </after> <!-- Create In active Category --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveIncludeInMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveIncludeInMenuTest.xml index c983089163f78..a695aa33079bd 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveIncludeInMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithInactiveIncludeInMenuTest.xml @@ -21,7 +21,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> </before> <after> - <actionGroup ref="DeleteCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"/> <actionGroup ref="logout" stepKey="logout"/> </after> <!--Create Category with not included in menu Subcategory --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilter.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilter.xml index 79eec02a828f6..6a49b47b75078 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilter.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilter.xml @@ -21,14 +21,14 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> </before> <after> - <actionGroup ref="DeleteCategory" stepKey="deleteCategory"/> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct1"> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"/> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct1"> <argument name="product" value="defaultSimpleProduct"/> </actionGroup> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct2"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct2"> <argument name="product" value="SimpleProduct"/> </actionGroup> - <actionGroup ref="NavigateToAndResetProductGridToDefaultView" stepKey="NavigateToAndResetProductGridToDefaultView"/> + <actionGroup ref="NavigateToAndResetProductGridToDefaultViewActionGroup" stepKey="NavigateToAndResetProductGridToDefaultView"/> <actionGroup ref="logout" stepKey="logout"/> </after> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductList"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithRequiredFieldsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithRequiredFieldsTest.xml index 1b6c9707b0656..dac2a121d107f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithRequiredFieldsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithRequiredFieldsTest.xml @@ -21,7 +21,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> </before> <after> - <actionGroup ref="DeleteCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"/> <actionGroup ref="logout" stepKey="logout"/> </after> <!-- Create subcategory with required fields --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml index a3f543e9cf32a..2d1a58764e20a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml @@ -38,7 +38,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <actionGroup ref="deleteProductAttribute" stepKey="deleteCreatedAttribute"> + <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteCreatedAttribute"> <argument name="ProductAttribute" value="newProductAttribute"/> </actionGroup> @@ -50,7 +50,7 @@ <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> <!-- Select Created Product--> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku"> <argument name="product" value="$$createConfigProduct$$"/> </actionGroup> <click stepKey="openFirstProduct" selector="{{AdminProductGridSection.productRowBySku($$createConfigProduct.sku$$)}}"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml new file mode 100644 index 0000000000000..981af5b5abb4a --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDatetimeProductAttributeTest.xml @@ -0,0 +1,49 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateDatetimeProductAttributeTest"> + <annotations> + <features value="Catalog"/> + <stories value="Datetime product attributes support"/> + <title value="Datetime product attribute type is supported"/> + <description value="Admin should be able to create datetime product attribute"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-21451"/> + <group value="catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + </before> + <after> + <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteAttribute"> + <argument name="ProductAttribute" value="DatetimeProductAttribute"/> + </actionGroup> + <actionGroup ref="AdminGridFilterResetActionGroup" stepKey="resetGridFilter"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Generate the datetime default value --> + <generateDate date="now" format="n/j/y g:i A" stepKey="generateDefaultValue"/> + <!-- Create new datetime product attribute --> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + <waitForPageLoad stepKey="waitForPageLoadAttributes"/> + <actionGroup ref="CreateProductAttributeWithDatetimeFieldActionGroup" stepKey="createAttribute"> + <argument name="attribute" value="DatetimeProductAttribute"/> + <argument name="date" value="{$generateDefaultValue}"/> + </actionGroup> + <!-- Navigate to created product attribute --> + <actionGroup ref="NavigateToCreatedProductAttributeActionGroup" stepKey="navigateToAttribute"> + <argument name="ProductAttribute" value="DatetimeProductAttribute"/> + </actionGroup> + <!-- Check the saved datetime default value --> + <actionGroup ref="AdminNavigateToProductAttributeAdvancedSectionActionGroup" stepKey="goToAdvancedSection"/> + <scrollTo selector="{{AdvancedAttributePropertiesSection.defaultValueDatetime}}" stepKey="scrollToDefaultValue"/> + <seeInField userInput="{$generateDefaultValue}" + selector="{{AdvancedAttributePropertiesSection.defaultValueDatetime}}" + stepKey="checkDefaultValue"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml index 525f81de6c48c..4118356b07e74 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeTest.xml @@ -21,7 +21,7 @@ </before> <after> <!-- Remove attribute --> - <actionGroup ref="deleteProductAttribute" stepKey="deleteAttribute"> + <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteAttribute"> <argument name="ProductAttribute" value="productDropDownAttribute"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> @@ -60,7 +60,7 @@ <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> - <actionGroup ref="navigateToCreatedProductAttribute" stepKey="navigateToAttribute"> + <actionGroup ref="NavigateToCreatedProductAttributeActionGroup" stepKey="navigateToAttribute"> <argument name="ProductAttribute" value="productDropDownAttribute"/> </actionGroup> <!-- Check attribute data --> @@ -69,4 +69,4 @@ <assertEquals actual="$secondOptionAdminLabel" expected="'Fish & Chips'" stepKey="assertSecondOption"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml index 1bc69be642a37..557f0768c98a3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml @@ -39,7 +39,7 @@ <!-- Filter product attribute set by attribute set name --> <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="amOnAttributeSetPage"/> - <actionGroup ref="FilterProductAttributeSetGridByAttributeSetName" stepKey="filterProductAttrSetGridByAttrSetName"> + <actionGroup ref="FilterProductAttributeSetGridByAttributeSetNameActionGroup" stepKey="filterProductAttrSetGridByAttrSetName"> <argument name="name" value="$$createAttributeSet.attribute_set_name$$"/> </actionGroup> @@ -47,12 +47,12 @@ <see userInput="$$attribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.unassignedAttributesTree}}" stepKey="seeAttributeInUnassignedAttr"/> <!-- Assign attribute in the group --> - <actionGroup ref="AssignAttributeToGroup" stepKey="assignAttributeToGroup"> + <actionGroup ref="AssignAttributeToGroupActionGroup" 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"/> + <actionGroup ref="SaveAttributeSetActionGroup" stepKey="saveAttributeSet"/> <!-- Go to Product Attribute Grid page --> <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml index 37ec4e0d32528..cbef9566b2b78 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateCategoryTest.xml @@ -27,10 +27,10 @@ </after> <!-- Open Category Page and select Add category --> - <actionGroup ref="goToCreateCategoryPage" stepKey="goToCategoryPage"/> + <actionGroup ref="GoToCreateCategoryPageActionGroup" stepKey="goToCategoryPage"/> <!-- Fill the Category form with same name and urlKey as initially created category(SimpleSubCategory) --> - <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillCategoryForm"> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSaveActionGroup" stepKey="fillCategoryForm"> <argument name="categoryName" value="$$category.name$$"/> <argument name="categoryUrlKey" value="$$category.custom_attributes[url_key]$$"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml index 575bb56912b25..4507e1f880a86 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDuplicateProductTest.xml @@ -32,10 +32,10 @@ </after> <!-- Go to new simple product page --> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="goToCreateProductPage"/> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="goToCreateProductPage"/> <!-- Fill the main fields in the form --> - <actionGroup ref="FillMainProductFormByString" stepKey="fillMainProductForm"> + <actionGroup ref="FillMainProductFormByStringActionGroup" stepKey="fillMainProductForm"> <argument name="productName" value="$$subCategory.name$$"/> <argument name="productSku" value="{{defaultSimpleProduct.sku}}"/> <argument name="productPrice" value="{{defaultSimpleProduct.price}}"/> @@ -45,17 +45,17 @@ </actionGroup> <!-- Select the category that we created in the before block --> - <actionGroup ref="SetCategoryByName" stepKey="setCategory"> + <actionGroup ref="SetCategoryByNameActionGroup" stepKey="setCategory"> <argument name="categoryName" value="$$category.name$$"/> </actionGroup> <!-- Set the url key to match the subcategory created in the before block --> - <actionGroup ref="SetProductUrlKeyByString" stepKey="fillUrlKey"> + <actionGroup ref="SetProductUrlKeyByStringActionGroup" stepKey="fillUrlKey"> <argument name="urlKey" value="$$subCategory.custom_attributes[url_key]$$"/> </actionGroup> <!-- Save the product and expect to see an error message --> - <actionGroup ref="SaveProductFormNoSuccessCheck" stepKey="tryToSaveProduct"/> + <actionGroup ref="SaveProductFormNoSuccessCheckActionGroup" stepKey="tryToSaveProduct"/> <see selector="{{AdminProductFormSection.successMessage}}" userInput="The value specified in the URL Key field would generate a URL that already exists." stepKey="seeErrorMessage"/> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryAndUpdateAsInactiveTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryAndUpdateAsInactiveTest.xml index 21b3dba7140c0..b0e6fe87be918 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryAndUpdateAsInactiveTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryAndUpdateAsInactiveTest.xml @@ -23,11 +23,11 @@ <!--Create category--> <createData entity="CatNotActive" stepKey="createCategory"/> <!-- Create First StoreView --> - <actionGroup ref="CreateStoreView" stepKey="createCustomStoreViewEn"> + <actionGroup ref="CreateStoreViewActionGroup" stepKey="createCustomStoreViewEn"> <argument name="storeView" value="customStoreEN"/> </actionGroup> <!-- Create Second StoreView --> - <actionGroup ref="CreateStoreView" stepKey="createCustomStoreViewFr"> + <actionGroup ref="CreateStoreViewActionGroup" stepKey="createCustomStoreViewFr"> <argument name="storeView" value="customStoreFR"/> </actionGroup> <!--Run full reindex and clear caches --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryTest.xml index aa3dba85dfadf..7de37b9cb77ef 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveFlatCategoryTest.xml @@ -23,11 +23,11 @@ <!--Create category--> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <!-- Create First StoreView --> - <actionGroup ref="CreateStoreView" stepKey="createCustomStoreViewEn"> + <actionGroup ref="CreateStoreViewActionGroup" stepKey="createCustomStoreViewEn"> <argument name="storeView" value="customStoreEN"/> </actionGroup> <!-- Create Second StoreView --> - <actionGroup ref="CreateStoreView" stepKey="createCustomStoreViewFr"> + <actionGroup ref="CreateStoreViewActionGroup" stepKey="createCustomStoreViewFr"> <argument name="storeView" value="customStoreFR"/> </actionGroup> <!--Run full reindex and clear caches --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveInMenuFlatCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveInMenuFlatCategoryTest.xml index 37417cd7fdb85..c7aba1fe8376f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveInMenuFlatCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateInactiveInMenuFlatCategoryTest.xml @@ -23,11 +23,11 @@ <!--Create category--> <createData entity="SimpleSubCategory" stepKey="category"/> <!-- Create First StoreView --> - <actionGroup ref="CreateStoreView" stepKey="createCustomStoreViewEn"> + <actionGroup ref="CreateStoreViewActionGroup" stepKey="createCustomStoreViewEn"> <argument name="storeView" value="customStoreEN"/> </actionGroup> <!-- Create Second StoreView --> - <actionGroup ref="CreateStoreView" stepKey="createCustomStoreViewFr"> + <actionGroup ref="CreateStoreViewActionGroup" stepKey="createCustomStoreViewFr"> <argument name="storeView" value="customStoreFR"/> </actionGroup> <!--Run full reindex and clear caches --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml index 4e096b7ebb142..6bfd012bc88b2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml @@ -45,7 +45,7 @@ <!-- Filter product attribute set by attribute set name --> <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="amOnAttributeSetPage"/> - <actionGroup ref="FilterProductAttributeSetGridByAttributeSetName" stepKey="filterProductAttrSetGridByAttrSetName"> + <actionGroup ref="FilterProductAttributeSetGridByAttributeSetNameActionGroup" stepKey="filterProductAttrSetGridByAttrSetName"> <argument name="name" value="$$createAttributeSet.attribute_set_name$$"/> </actionGroup> @@ -53,12 +53,12 @@ <see userInput="$$attribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.unassignedAttributesTree}}" stepKey="seeAttributeInUnassignedAttr"/> <!-- Assign attribute in the group --> - <actionGroup ref="AssignAttributeToGroup" stepKey="assignAttributeToGroup"> + <actionGroup ref="AssignAttributeToGroupActionGroup" 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"/> + <actionGroup ref="SaveAttributeSetActionGroup" stepKey="saveAttributeSet"/> <!-- Go to Product Attribute Grid page --> <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductPageTest.xml index 2706d00038e4b..51c4a3250d609 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductPageTest.xml @@ -26,8 +26,8 @@ <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="GoToProductCatalogPage" stepKey="goToProductCatalogPage"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="GoToProductCatalogPageActionGroup" stepKey="goToProductCatalogPage"/> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="SimpleProduct"/> </actionGroup> <actionGroup ref="AdminClickAddAttributeOnProductEditPageActionGroup" stepKey="clickAddAttribute"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductTest.xml index 02615ca5dd254..8fb226f5f5585 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewAttributeFromProductTest.xml @@ -38,7 +38,7 @@ </actionGroup> <!--Delete Attribute--> - <actionGroup ref="deleteProductAttribute" stepKey="deleteAttribute"> + <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteAttribute"> <argument name="ProductAttribute" value="productDropDownAttribute"/> </actionGroup> @@ -55,7 +55,7 @@ <!--Go to created product page and create new attribute--> <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="openAdminEditPage"/> - <actionGroup ref="AdminCreateAttributeWithValueWithTwoStoreViesFromProductPage" stepKey="createAttribute"> + <actionGroup ref="AdminCreateAttributeWithValueWithTwoStoreViesFromProductPageActionGroup" stepKey="createAttribute"> <argument name="attributeName" value="{{productDropDownAttribute.attribute_code}}"/> <argument name="attributeType" value="Dropdown"/> <argument name="firstStoreViewName" value="{{customStoreEN.name}}"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewGroupForAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewGroupForAttributeSetTest.xml index 3219bca233bee..a105f343d3e21 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewGroupForAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewGroupForAttributeSetTest.xml @@ -29,13 +29,13 @@ <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <actionGroup ref="logout" stepKey="logout"/> </after> - + <!-- Navigate to Stores > Attributes > Attribute Set --> <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSetPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> <!-- Search and open Attribute Set from preconditions --> - <actionGroup ref="goToAttributeSetByName" stepKey="searchAttribute"> + <actionGroup ref="GoToAttributeSetByNameActionGroup" stepKey="searchAttribute"> <argument name="name" value="$$createAttributeSet.attribute_set_name$$"/> </actionGroup> @@ -68,9 +68,9 @@ <see userInput="$$createConfigProductAttribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.groupTree}}" stepKey="seeAttribute"/> <!-- Click 'Save' --> - <actionGroup ref="SaveAttributeSet" stepKey="saveAttribute"/> + <actionGroup ref="SaveAttributeSetActionGroup" stepKey="saveAttribute"/> - <actionGroup ref="goToAttributeSetByName" stepKey="backTohAttributeSet"> + <actionGroup ref="GoToAttributeSetByNameActionGroup" stepKey="backTohAttributeSet"> <argument name="name" value="$$createAttributeSet.attribute_set_name$$"/> </actionGroup> @@ -85,7 +85,7 @@ <!-- Empty group is created. No attributes are assigned to it. --> <seeElement selector="{{AdminProductAttributeSetEditSection.attributeGroup(emptyGroup.name)}}" stepKey="assertEmptyGroup"/> <dontSeeElement selector="{{AdminProductAttributeSetEditSection.attributesInGroup(emptyGroup.name)}}" stepKey="seeNoAttributes"/> - + <!-- Navigate to Catalog > Products --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductPage"/> <waitForPageLoad stepKey="waitForProductPageLoad"/> @@ -93,8 +93,8 @@ <!-- Start to create a new simple product with the custom attribute set from the preconditions --> <click selector="{{AdminProductGridActionSection.addProductBtn}}" stepKey="clickAddProduct"/> <waitForPageLoad stepKey="waitForNewProductPage"/> - - <actionGroup ref="AdminProductPageSelectAttributeSet" stepKey="selectAttribute"> + + <actionGroup ref="AdminProductPageSelectAttributeSetActionGroup" stepKey="selectAttribute"> <argument name="attributeSetName" value="$$createAttributeSet.attribute_set_name$$"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml index 63a964f4b5e91..2e502f58041e6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml @@ -35,7 +35,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!--<deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/>--> - <actionGroup ref="deleteProductAttribute" stepKey="deleteCreatedAttribute"> + <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteCreatedAttribute"> <argument name="ProductAttribute" value="newProductAttribute"/> </actionGroup> @@ -47,7 +47,7 @@ <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> <!-- Select Created Product--> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> <click stepKey="openFirstProduct" selector="{{AdminProductGridSection.productRowBySku($$createSimpleProduct.sku$$)}}"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeRequiredTextFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeRequiredTextFieldTest.xml index d4d6496e018f5..63eed37b1e84f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeRequiredTextFieldTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeRequiredTextFieldTest.xml @@ -34,7 +34,7 @@ <!--Delete created entity --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="deleteProductAttribute" stepKey="deleteCreatedAttribute"> + <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteCreatedAttribute"> <argument name="ProductAttribute" value="newProductAttribute"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> @@ -45,7 +45,7 @@ <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> <!-- Select Created Product--> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> <click stepKey="openFirstProduct" selector="{{AdminProductGridSection.productRowBySku($$createSimpleProduct.sku$$)}}"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductCustomAttributeSet.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductCustomAttributeSet.xml index 713e1b7d6dfd1..7f6feaff3ed5d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductCustomAttributeSet.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductCustomAttributeSet.xml @@ -71,10 +71,10 @@ <dontSeeElementInDOM selector="{{AdminProductFormSection.divByDataIndex('meta_keyword')}}" stepKey="dontSeeMetaKeyword"/> <!-- Finish filling the new product page --> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillSimpleProductMain"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillSimpleProductMain"> <argument name="product" value="_defaultProduct"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveSimpleProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSimpleProduct"/> <!-- Check the storefront --> <amOnPage url="{{_defaultProduct.name}}.html" stepKey="goToProductPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml index 291b6985bd3e5..f5e42bb84549c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductDuplicateUrlkeyTest.xml @@ -58,7 +58,7 @@ </before> <after> <!--Delete all products by filtering grid and using mass delete action--> - <actionGroup ref="deleteAllDuplicateProductUsingProductGrid" stepKey="deleteAllDuplicateProducts"> + <actionGroup ref="DeleteAllDuplicateProductUsingProductGridActionGroup" stepKey="deleteAllDuplicateProducts"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> <deleteData createDataKey="createCategory" stepKey="deletePreReqCatalog" /> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml index 11d919ddefa2c..7a99750c00e53 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryAndSubcategoriesTest.xml @@ -33,7 +33,7 @@ <selectOption userInput="Default Category" selector="{{AdminNewStoreGroupSection.storeRootCategoryDropdown}}" stepKey="selectOptionDefaultCategory"/> <click selector="{{AdminStoreGroupActionsSection.saveButton}}" stepKey="clickSaveStoreButton"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="clickOkOnModalDialog2"/> - <actionGroup ref="DeleteCategory" stepKey="deleteCreatedNewRootCategory"> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCreatedNewRootCategory"> <argument name="categoryEntity" value="NewRootCategory"/> </actionGroup> <actionGroup ref="logout" stepKey="logout2"/> @@ -48,13 +48,13 @@ </actionGroup> <scrollToTopOfPage stepKey="scrollToTopOfPage2"/> <!--Create subcategory--> - <actionGroup ref="CreateCategory" stepKey="createSubcategory1"> + <actionGroup ref="CreateCategoryActionGroup" stepKey="createSubcategory1"> <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(NewRootCategory.name)}}" stepKey="clickOnCreatedNewRootCategory1"/> <scrollToTopOfPage stepKey="scrollToTopOfPage3"/> <!--Create another subcategory--> - <actionGroup ref="CreateCategory" stepKey="createSubcategory2"> + <actionGroup ref="CreateCategoryActionGroup" stepKey="createSubcategory2"> <argument name="categoryEntity" value="SubCategoryWithParent"/> </actionGroup> <!--Assign new created root category to store--> @@ -74,11 +74,11 @@ <!--Go to storefront and verify created subcategory on frontend--> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> <waitForPageLoad stepKey="waitForPageAdminSystemStoreLoad2"/> - <actionGroup ref="CheckCategoryOnStorefront" stepKey="checkCreatedSubcategory1OnFrontend"> + <actionGroup ref="CheckCategoryOnStorefrontActionGroup" stepKey="checkCreatedSubcategory1OnFrontend"> <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> - <actionGroup ref="CheckCategoryOnStorefront" stepKey="checkCreatedSubcategory2OnFrontend"> + <actionGroup ref="CheckCategoryOnStorefrontActionGroup" stepKey="checkCreatedSubcategory2OnFrontend"> <argument name="categoryEntity" value="SubCategoryWithParent"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryRequiredFieldsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryRequiredFieldsTest.xml index f98f9acc46961..2b824554b9bd4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryRequiredFieldsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateRootCategoryRequiredFieldsTest.xml @@ -23,7 +23,7 @@ <actionGroup ref = "LoginAsAdmin" stepKey="LoginToAdminPanel"/> </before> <after> - <actionGroup ref="DeleteCategory" stepKey="deleteCategory"> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"> <argument name="categoryEntity" value="_defaultCategory" /> </actionGroup> <actionGroup ref="logout" stepKey="logout" /> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest.xml index a7587a5ed31fe..052f6b1924e89 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductTest.xml @@ -26,7 +26,7 @@ </after> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <actionGroup ref="FillAdminSimpleProductForm" stepKey="fillProductFieldsInAdmin"> + <actionGroup ref="FillAdminSimpleProductFormActionGroup" stepKey="fillProductFieldsInAdmin"> <argument name="category" value="$$createPreReqCategory$$"/> <argument name="simpleProduct" value="_defaultProduct"/> </actionGroup> @@ -34,7 +34,7 @@ <argument name="category" value="$$createPreReqCategory$$"/> <argument name="product" value="_defaultProduct"/> </actionGroup> - <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="assertProductInStorefront2"> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="assertProductInStorefront2"> <argument name="product" value="_defaultProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithCountryOfManufactureAttributeSKUMaskTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithCountryOfManufactureAttributeSKUMaskTest.xml index 3487de656173f..bbaabffcc5ecd 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithCountryOfManufactureAttributeSKUMaskTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithCountryOfManufactureAttributeSKUMaskTest.xml @@ -25,7 +25,7 @@ </before> <after> <magentoCLI stepKey="setName" command="config:set catalog/fields_masks/sku" arguments="{{name}}"/> - <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{nameAndAttributeSkuMaskSimpleProduct.name}}-{{nameAndAttributeSkuMaskSimpleProduct.country_of_manufacture_label}}" /> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml new file mode 100644 index 0000000000000..0f88fa9d6abf4 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithDatetimeAttributeTest.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateSimpleProductWithDatetimeAttributeTest"> + <annotations> + <features value="Catalog"/> + <stories value="Datetime product attributes support"/> + <title value="Set datetime attribute to product"/> + <description value="Admin should be able to specify datetime attribute to product and find by them in product grid"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-21461"/> + <group value="catalog"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="createDatetimeAttribute" stepKey="deleteDatetimeAttribute"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFiltersOnProductIndexPage"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Generate default value --> + <generateDate date="now" format="m/j/Y g:i A" stepKey="generateDefaultValue"/> + <generateDate date="now" format="M j, Y g:i:00 A" stepKey="generateDefaultGridValue"/> + <generateDate date="+1 minute" format="m/j/Y g:i A" stepKey="generateFilterToDate"/> + <!-- Create new datetime product attribute --> + <createData entity="DatetimeProductAttribute" stepKey="createDatetimeAttribute"> + <field key="default_value">{$generateDefaultValue}</field> + </createData> + <!-- Open the new simple product page --> + <actionGroup ref="AdminOpenNewProductFormPageActionGroup" stepKey="openNewProductPage"/> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillDefaultProductFields"/> + <!-- Add datetime attribute --> + <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addDatetimeAttribute"> + <argument name="attributeCode" value="$createDatetimeAttribute.attribute_code$"/> + </actionGroup> + <!-- Flush config cache to reset product attributes in attribute set --> + <magentoCLI command="cache:flush" arguments="config" stepKey="flushConfigCache"/> + <!-- Save the product --> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + <!-- Check default value --> + <scrollTo selector="{{AdminProductAttributesSection.sectionHeader}}" stepKey="goToAttributesSection"/> + <click selector="{{AdminProductAttributesSection.sectionHeader}}" stepKey="openAttributesSection"/> + <waitForElementVisible selector="{{AdminProductAttributesSection.attributeTextInputByCode($createDatetimeAttribute.attribute_code$)}}" stepKey="waitForSlideOutAttributes"/> + <seeInField selector="{{AdminProductAttributesSection.attributeTextInputByCode($createDatetimeAttribute.attribute_code$)}}" userInput="$generateDefaultValue" stepKey="checkDefaultValue"/> + <!-- Check datetime grid filter --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToAdminProductIndexPage"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.inputByCodeRangeFrom($createDatetimeAttribute.attribute_code$)}}" userInput="{$generateDefaultValue}" stepKey="fillProductDatetimeFromFilter"/> + <fillField selector="{{AdminProductGridFilterSection.inputByCodeRangeTo($createDatetimeAttribute.attribute_code$)}}" userInput="{$generateFilterToDate}" stepKey="fillProductDatetimeToFilter"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFilters"/> + <see selector="{{AdminDataGridTableSection.gridCell('1', 'Name')}}" userInput="{{_defaultProduct.name}}" stepKey="checkAppliedDatetimeFilter"/> + <see selector="{{AdminDataGridTableSection.rowTemplateStrict(_defaultProduct.name)}}" userInput="{$generateDefaultGridValue}" stepKey="checkDefaultValueInGrid"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml index 94d488f216b49..5dcc23a725b84 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateSimpleProductWithUnicodeTest.xml @@ -27,15 +27,17 @@ </after> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <actionGroup ref="FillAdminSimpleProductForm" stepKey="fillProductFieldsInAdmin"> + <actionGroup ref="FillAdminSimpleProductFormActionGroup" stepKey="fillProductFieldsInAdmin"> <argument name="category" value="$$createPreReqCategory$$"/> <argument name="simpleProduct" value="ProductWithUnicode"/> </actionGroup> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> <actionGroup ref="AssertProductInStorefrontCategoryPage" stepKey="assertProductInStorefront1"> <argument name="category" value="$$createPreReqCategory$$"/> <argument name="product" value="ProductWithUnicode"/> </actionGroup> - <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="assertProductInStorefront2"> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="assertProductInStorefront2"> <argument name="product" value="ProductWithUnicode"/> </actionGroup> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateTextEditorProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateTextEditorProductAttributeTest.xml index fc7482c353136..848e765d34d70 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateTextEditorProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateTextEditorProductAttributeTest.xml @@ -30,12 +30,12 @@ </before> <after> <!-- Delete attribute --> - <actionGroup ref="deleteProductAttribute" stepKey="deleteAttribute"> + <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteAttribute"> <argument name="ProductAttribute" value="productTextEditorAttribute"/> </actionGroup> <!-- Delete product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="_defaultProduct"/> </actionGroup> @@ -71,7 +71,7 @@ <selectOption selector="{{AttributePropertiesSection.InputType}}" userInput="{{productTextEditorAttribute.frontend_input}}" stepKey="returnInputType"/> <!-- Save Product Attribute --> - <actionGroup ref="saveProductAttribute" stepKey="saveAttribute"/> + <actionGroup ref="SaveProductAttributeActionGroup" stepKey="saveAttribute"/> <!-- Go to Store > Attribute Set --> <actionGroup ref="AdminOpenAttributeSetGridPageActionGroup" stepKey="openAttributeSetPage"/> @@ -80,20 +80,20 @@ <actionGroup ref="AdminOpenAttributeSetByNameActionGroup" stepKey="openDefaultAttributeSet"/> <!-- Add Product Attribute to Default attribute by dragging and dropping this to the 'Project Details' folder. Then Save. --> - <actionGroup ref="AssignAttributeToGroup" stepKey="assignAttributeToGroup"> + <actionGroup ref="AssignAttributeToGroupActionGroup" stepKey="assignAttributeToGroup"> <argument name="group" value="Product Details"/> <argument name="attribute" value="{{productTextEditorAttribute.attribute_code}}"/> </actionGroup> - <actionGroup ref="SaveAttributeSet" stepKey="saveAttributeSet"/> + <actionGroup ref="SaveAttributeSetActionGroup" stepKey="saveAttributeSet"/> <!-- Go Catalog > Product to create new product page --> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="goToProductIndexPage"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="_defaultProduct"/> </actionGroup> <!-- On product page, select Attribute Set: "Default" --> - <actionGroup ref="AdminProductPageSelectAttributeSet" stepKey="selectAttributeSet"> + <actionGroup ref="AdminProductPageSelectAttributeSetActionGroup" stepKey="selectAttributeSet"> <argument name="attributeSetName" value="Default"/> </actionGroup> @@ -115,8 +115,8 @@ <fillField selector="{{ProductDescriptionWysiwygSection.attributeEditArea(productTextEditorAttribute.attribute_code)}}" userInput="This content from product page" stepKey="setContent"/> <!-- Fill up all required fields for product form --> - <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"/> - <actionGroup ref="saveProductForm" stepKey="saveProductForm"/> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> <!-- Assert product attribute on Storefront --> <actionGroup ref="OpenStorefrontProductPageByProductNameActionGroup" stepKey="openProductPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductOutOfStockWithTierPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductOutOfStockWithTierPriceTest.xml index 0b929eaddc96e..9db9f64396826 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductOutOfStockWithTierPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductOutOfStockWithTierPriceTest.xml @@ -24,10 +24,10 @@ </before> <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="categoryEntity"/> - <actionGroup ref="deleteProductBySku" stepKey="deleteVirtualProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteVirtualProduct"> <argument name="sku" value="{{virtualProductOutOfStock.sku}}"/> </actionGroup> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilter"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilter"/> <actionGroup ref="logout" stepKey="logout"/> </after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml index 23f772a395a7d..fe39bb6ada2bd 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml @@ -24,10 +24,10 @@ </before> <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="categoryEntity"/> - <actionGroup ref="deleteProductBySku" stepKey="deleteVirtualProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteVirtualProduct"> <argument name="sku" value="{{virtualProductCustomImportOptions.sku}}"/> </actionGroup> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="resetOrderFilter"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="resetOrderFilter"/> <actionGroup ref="logout" stepKey="logout"/> </after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml index 9055e961f889f..976f714d7b3e1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml @@ -24,7 +24,7 @@ <createData entity="Simple_US_CA_Customer" stepKey="customer" /> </before> <after> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteVirtualProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteVirtualProduct"> <argument name="product" value="virtualProductGeneralGroup"/> </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="categoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteAttributeSetTest.xml index cbe2f40e0dd25..973ff0381584a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteAttributeSetTest.xml @@ -47,7 +47,7 @@ <!-- Search for the product by sku and name on the product page --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToAdminProductIndex"/> <waitForPageLoad stepKey="waitForAdminProductIndex"/> - <actionGroup ref="filterProductGridBySkuAndName" stepKey="filerProductsBySkuAndName"> + <actionGroup ref="FilterProductGridBySkuAndNameActionGroup" stepKey="filerProductsBySkuAndName"> <argument name="product" value="SimpleProductWithCustomAttributeSet"/> </actionGroup> <!-- Should not see the product --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteConfigurableChildProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteConfigurableChildProductsTest.xml index 0df9dd0b57545..4a305b8dfec75 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteConfigurableChildProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteConfigurableChildProductsTest.xml @@ -95,10 +95,10 @@ <see selector="{{StorefrontProductInfoMainSection.productAttributeTitle1}}" userInput="$$createConfigProductAttribute.default_value$$" stepKey="seeProductAttributeLabel"/> <seeElement selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" stepKey="seeProductAttributeOptions"/> <!-- Delete Child products --> - <actionGroup ref="deleteProductBySku" stepKey="deleteFirstChildProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteFirstChildProduct"> <argument name="sku" value="$$createConfigChildProduct1.sku$$"/> </actionGroup> - <actionGroup ref="deleteProductBySku" stepKey="deleteSecondChildProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteSecondChildProduct"> <argument name="sku" value="$$createConfigChildProduct2.sku$$"/> </actionGroup> <!--Verify product is not visible in category store front page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml index 3841c061c2629..3abe68a503b57 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml @@ -41,7 +41,7 @@ <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"> + <actionGroup ref="AssignAttributeToGroupActionGroup" stepKey="assignAttribute"> <argument name="group" value="Product Details"/> <argument name="attribute" value="$$attribute.attribute_code$$"/> </actionGroup> @@ -49,11 +49,15 @@ <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 ref="OpenProductAttributeFromSearchResultInGridActionGroup" stepKey="openProductAttributeFromSearchResultInGrid"> + <argument name="productAttributeCode" value="$$attribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="DeleteProductAttributeByAttributeCodeActionGroup" stepKey="deleteProductAttributeByAttributeCode"> + <argument name="productAttributeCode" value="$$attribute.attribute_code$$"/> </actionGroup> + <actionGroup ref="AssertProductAttributeRemovedSuccessfullyActionGroup" stepKey="deleteProductAttributeSuccess"/> <!--Confirm Attribute is not present in Product Attribute Grid --> - <actionGroup ref="filterProductAttributeByAttributeCode" stepKey="filterAttribute"> + <actionGroup ref="FilterProductAttributeByAttributeCodeActionGroup" stepKey="filterAttribute"> <argument name="ProductAttributeCode" value="$$attribute.attribute_code$$"/> </actionGroup> <see selector="{{AdminProductAttributeGridSection.FirstRow}}" userInput="We couldn't find any records." stepKey="seeEmptyRow"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductAttributeTest.xml index d0036a2adea5a..4060182a9bace 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductAttributeTest.xml @@ -24,16 +24,20 @@ <after> <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="deleteProductAttributeByAttributeCode" stepKey="deleteProductAttribute"> - <argument name="ProductAttributeCode" value="$$createProductAttribute.attribute_code$$"/> + <actionGroup ref="OpenProductAttributeFromSearchResultInGridActionGroup" stepKey="openProductAttributeFromSearchResultInGrid"> + <argument name="productAttributeCode" value="$$createProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="DeleteProductAttributeByAttributeCodeActionGroup" stepKey="deleteProductAttributeByAttributeCode"> + <argument name="productAttributeCode" value="$$createProductAttribute.attribute_code$$"/> </actionGroup> + <actionGroup ref="AssertProductAttributeRemovedSuccessfullyActionGroup" stepKey="deleteProductAttributeSuccess"/> <!-- Assert the product attribute is not in the grid by Attribute code --> - <actionGroup ref="filterProductAttributeByAttributeCode" stepKey="filterByAttributeCode"> + <actionGroup ref="FilterProductAttributeByAttributeCodeActionGroup" stepKey="filterByAttributeCode"> <argument name="ProductAttributeCode" value="$$createProductAttribute.attribute_code$$"/> </actionGroup> <see selector="{{AdminDataGridTableSection.dataGridEmpty}}" userInput="We couldn't find any records." stepKey="assertDataGridEmptyMessage"/> <!--Assert the product attribute is not in the grid by Default Label --> - <actionGroup ref="filterProductAttributeByDefaultLabel" stepKey="filterByDefaultLabel"> + <actionGroup ref="FilterProductAttributeByDefaultLabelActionGroup" stepKey="filterByDefaultLabel"> <argument name="productAttributeLabel" value="$$createProductAttribute.default_frontend_label$$"/> </actionGroup> <see selector="{{AdminDataGridTableSection.dataGridEmpty}}" userInput="We couldn't find any records." stepKey="assertDataGridEmptyMessage2"/> @@ -48,7 +52,7 @@ <waitForPageLoad stepKey="waitForAttributeAdded"/> <!-- Filter By Attribute Label on Add Attribute Page --> <click selector="{{AdminProductFiltersSection.filter}}" stepKey="clickOnFilter"/> - <actionGroup ref="filterProductAttributeByAttributeLabel" stepKey="filterByAttributeLabel"> + <actionGroup ref="FilterProductAttributeByAttributeLabelActionGroup" stepKey="filterByAttributeLabel"> <argument name="productAttributeLabel" value="$$createProductAttribute.default_frontend_label$$"/> </actionGroup> <see selector="{{AdminDataGridTableSection.dataGridEmpty}}" userInput="We couldn't find any records." stepKey="assertDataGridEmptyMessage3"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductWithCustomOptionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductWithCustomOptionTest.xml index 7f6a1333b721a..dec911ec84a8d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductWithCustomOptionTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductWithCustomOptionTest.xml @@ -30,7 +30,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteSimpleProductFilteredBySkuAndName"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteSimpleProductFilteredBySkuAndName"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="A total of 1 record(s) have been deleted." stepKey="deleteMessage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductsImageInCaseOfMultipleStoresTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductsImageInCaseOfMultipleStoresTest.xml index f334cbc218b7c..55c98bcc13d34 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductsImageInCaseOfMultipleStoresTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductsImageInCaseOfMultipleStoresTest.xml @@ -43,17 +43,17 @@ <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="visitAdminProductPage"/> <waitForPageLoad stepKey="waitForProductPageLoad0"/> <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="['Default Category', $$createRootCategory.name$$, $$createSubCategory.name$$]" stepKey="fillCategory"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!--Add images to the product--> <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="visitAdminProductPage2"/> <waitForPageLoad stepKey="waitForProductPageLoad1"/> - <actionGroup ref="addProductImage" stepKey="addImageToProduct"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImageToProduct"> <argument name="image" value="ProductImage"/> </actionGroup> - <actionGroup ref="addProductImage" stepKey="addImage1ToProduct"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImage1ToProduct"> <argument name="image" value="TestImageNew"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct1"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1"/> <!--Enable config to view created store view on store front--> <createData entity="EnableWebUrlOptionsConfig" stepKey="enableWebUrlOptionsConfig"/> </before> @@ -86,12 +86,12 @@ <actionGroup ref="SelectProductInWebsitesActionGroup" stepKey="selectWebsiteInProduct2"> <argument name="website" value="{{NewWebSiteData.name}}"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct2"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct2"/> <!--Reindex and flush cache--> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> <!--Switch to 'Default Store View' scope and open product page--> - <actionGroup ref="SwitchToTheNewStoreView" stepKey="SwitchDefaultStoreView"> + <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="SwitchDefaultStoreView"> <argument name="storeViewName" value="'Default Store View'"/> </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad3"/> @@ -99,9 +99,9 @@ <actionGroup ref="AdminAssignImageRolesIfUnassignedActionGroup" stepKey="assignAllRolesToFirstImage"> <argument name="image" value="ProductImage"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct3"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct3"/> <!--Switch to newly created Store View scope and open product page--> - <actionGroup ref="SwitchToTheNewStoreView" stepKey="SwitchNewStoreView"> + <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="SwitchNewStoreView"> <argument name="storeViewName" value="{{NewStoreViewData.name}}"/> </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad4"/> @@ -109,17 +109,17 @@ <actionGroup ref="AdminAssignImageRolesIfUnassignedActionGroup" stepKey="assignAllRolesToFirstImage2"> <argument name="image" value="ProductImage"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct4"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct4"/> <!--Switch to 'All Store Views' scope and open product page--> - <actionGroup ref="SwitchToTheNewStoreView" stepKey="SwitchAllStoreView"> + <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="SwitchAllStoreView"> <argument name="storeViewName" value="'All Store Views'"/> </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad5"/> <!--Remove product image and save--> - <actionGroup ref="RemoveProductImageByName" stepKey="removeProductImage"> + <actionGroup ref="RemoveProductImageByNameActionGroup" stepKey="removeProductImage"> <argument name="image" value="ProductImage"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct5"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct5"/> <!--Assert notification and success messages--> <see selector="{{StorefrontMessagesSection.success}}" userInput="{{ProductFormMessages.save_success}}" stepKey="seeSuccessMessage"/> <see selector="{{StorefrontMessagesSection.noticeMessage}}" userInput="{{ProductFormMessages.remove_image_notice}}" stepKey="seeNotification"/> @@ -128,7 +128,7 @@ <waitForPageLoad stepKey="waitForImagesLoad"/> <seeElement selector="{{AdminProductImagesSection.imageFile(ProductImage.fileName)}}" stepKey="seeImageIsNotDeleted"/> <!--Switch to newly created Store View scope and open product page--> - <actionGroup ref="SwitchToTheNewStoreView" stepKey="SwitchNewStoreView2"> + <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="SwitchNewStoreView2"> <argument name="storeViewName" value="{{NewStoreViewData.name}}"/> </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad6"/> @@ -136,17 +136,17 @@ <actionGroup ref="AdminAssignImageRolesIfUnassignedActionGroup" stepKey="assignAllRolesToSecondImage"> <argument name="image" value="TestImageNew"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct6"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct6"/> <!--Switch to 'All Store Views' scope and open product page--> - <actionGroup ref="SwitchToTheNewStoreView" stepKey="SwitchAllStoreView2"> + <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="SwitchAllStoreView2"> <argument name="storeViewName" value="'All Store Views'"/> </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad7"/> <!--Remove product image and save--> - <actionGroup ref="RemoveProductImageByName" stepKey="removeProductFirstImage"> + <actionGroup ref="RemoveProductImageByNameActionGroup" stepKey="removeProductFirstImage"> <argument name="image" value="ProductImage"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct7"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct7"/> <!--Assert notification and success messages--> <see selector="{{StorefrontMessagesSection.success}}" userInput="{{ProductFormMessages.save_success}}" stepKey="seeSuccessMessage2"/> <see selector="{{StorefrontMessagesSection.noticeMessage}}" userInput="{{ProductFormMessages.remove_image_notice}}" stepKey="seeNotification2"/> @@ -155,15 +155,15 @@ <waitForPageLoad stepKey="waitForImagesLoad2"/> <seeElement selector="{{AdminProductImagesSection.imageFile(ProductImage.fileName)}}" stepKey="seeImageIsNotDeleted2"/> <!--Switch to newly created Store View scope and open product page--> - <actionGroup ref="SwitchToTheNewStoreView" stepKey="SwitchNewStoreView3"> + <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="SwitchNewStoreView3"> <argument name="storeViewName" value="{{NewStoreViewData.name}}"/> </actionGroup> <waitForPageLoad stepKey="waitForProductPageLoad8"/> <!--Remove second image and save--> - <actionGroup ref="RemoveProductImageByName" stepKey="removeProductSecondImage"> + <actionGroup ref="RemoveProductImageByNameActionGroup" stepKey="removeProductSecondImage"> <argument name="image" value="TestImageNew"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct8"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct8"/> <!--Assert success messages--> <see selector="{{StorefrontMessagesSection.success}}" userInput="{{ProductFormMessages.save_success}}" stepKey="seeSuccessMessage3"/> <!--Reopen image tab and see the image is deleted--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSimpleProductTest.xml index 7c460a3dfc51e..5b8ac5157514d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteSimpleProductTest.xml @@ -29,7 +29,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteSimpleProductFilteredBySkuAndName"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteSimpleProductFilteredBySkuAndName"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="A total of 1 record(s) have been deleted." stepKey="deleteMessage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml index c3cafb17c5eac..4f05c364fda0e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml @@ -47,7 +47,7 @@ <!--Open Product Index Page and filter the product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProduct"> <argument name="product" value="SimpleProduct2"/> </actionGroup> <!--Verify Created Product Attribute displayed in Product page --> @@ -55,11 +55,15 @@ <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 ref="OpenProductAttributeFromSearchResultInGridActionGroup" stepKey="openProductAttributeFromSearchResultInGrid"> + <argument name="productAttributeCode" value="$$attribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="DeleteProductAttributeByAttributeCodeActionGroup" stepKey="deleteProductAttributeByAttributeCode"> + <argument name="productAttributeCode" value="$$attribute.attribute_code$$"/> </actionGroup> + <actionGroup ref="AssertProductAttributeRemovedSuccessfullyActionGroup" stepKey="deleteProductAttributeSuccess"/> <!-- Confirm attribute is not present in product attribute grid --> - <actionGroup ref="filterProductAttributeByAttributeCode" stepKey="filterAttribute"> + <actionGroup ref="FilterProductAttributeByAttributeCodeActionGroup" stepKey="filterAttribute"> <argument name="ProductAttributeCode" value="$$attribute.attribute_code$$"/> </actionGroup> <see stepKey="seeEmptyRow" selector="{{AdminProductAttributeGridSection.FirstRow}}" userInput="We couldn't find any records."/> @@ -77,7 +81,7 @@ <!--Verify Product Attribute is not present in Product Index Page --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="openProductIndexPage"/> <waitForPageLoad stepKey="waitForProductIndexPageToLoad1"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProduct1"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProduct1"> <argument name="product" value="SimpleProduct2"/> </actionGroup> <!--Verify Product Attribute is not present in Product page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteVirtualProductTest.xml index 413d53d1c3746..86f253f358532 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteVirtualProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteVirtualProductTest.xml @@ -30,7 +30,7 @@ <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteVirtualProductFilteredBySkuAndName"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteVirtualProductFilteredBySkuAndName"> <argument name="product" value="$$createVirtualProduct$$"/> </actionGroup> <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="A total of 1 record(s) have been deleted." stepKey="deleteMessage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDisableProductOnChangingAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDisableProductOnChangingAttributeSetTest.xml index dab1704d50bf3..0fc2c022b81e9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDisableProductOnChangingAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDisableProductOnChangingAttributeSetTest.xml @@ -33,7 +33,7 @@ </after> <actionGroup ref="LoginAsAdmin" stepKey="login"/> <amOnPage url="{{AdminProductAttributeSetEditPage.url}}/$$createAttributeSet.attribute_set_id$$/" stepKey="onAttributeSetEdit"/> - <actionGroup ref="SaveAttributeSet" stepKey="SaveAttributeSet"/> + <actionGroup ref="SaveAttributeSetActionGroup" stepKey="SaveAttributeSet"/> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminEditTextEditorProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminEditTextEditorProductAttributeTest.xml index 53040993beb8f..feea0930390b7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminEditTextEditorProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminEditTextEditorProductAttributeTest.xml @@ -19,14 +19,14 @@ </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> <createData stepKey="myProductAttributeCreation" entity="productAttributeWysiwyg"/> <createData stepKey="myProductAttributeSetAssign" entity="AddToDefaultSet"> <requiredEntity createDataKey="myProductAttributeCreation"/> </createData> </before> - <actionGroup ref="navigateToCreatedProductAttribute" stepKey="navigateToAttribute"> + <actionGroup ref="NavigateToCreatedProductAttributeActionGroup" stepKey="navigateToAttribute"> <argument name="ProductAttribute" value="productAttributeWysiwyg"/> </actionGroup> <seeOptionIsSelected selector="{{AttributePropertiesSection.InputType}}" userInput="Text Editor" stepKey="seeTextEditorSelected" /> @@ -75,7 +75,7 @@ <seeElement selector="{{ProductAttributeWYSIWYGSection.TinyMCE4($$myProductAttributeCreation.attribute_code$$)}}" stepKey="seePoweredBy"/> <after> <deleteData createDataKey="myProductAttributeCreation" stepKey="deletePreReqProductAttribute" /> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml index f3ec225540c75..0b230b0b8e002 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilterByNameByStoreViewOnProductGridTest.xml @@ -35,11 +35,11 @@ <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> <click selector="{{AdminProductFormSection.productNameUseDefault}}" stepKey="uncheckUseDefault"/> <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{SimpleProduct.name}}" stepKey="fillNewName"/> - <actionGroup ref="saveProductForm" stepKey="saveSimpleProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSimpleProduct"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <actionGroup ref="filterProductGridByName" stepKey="filterGridByName"> + <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="filterGridByName"> <argument name="product" value="SimpleProduct"/> </actionGroup> <see selector="{{AdminProductGridSection.firstProductRow}}" userInput="{{SimpleProduct2.name}}" stepKey="seeProductNameInGrid"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml index 5c434ecabf80d..77b719c03091e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml @@ -60,7 +60,7 @@ <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="clickToOpenWebsiteSection"/> <waitForPageLoad stepKey="waitForToOpenedWebsiteSection"/> <uncheckOption selector="{{ProductInWebsitesSection.website('Main Website')}}" stepKey="uncheckWebsite"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct1"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1"/> <!-- Set filter to product name and product2 in website 2 only --> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForProduct2"> @@ -73,7 +73,7 @@ <argument name="website" value="{{secondCustomWebsite.name}}"/> </actionGroup> <uncheckOption selector="{{ProductInWebsitesSection.website('Main Website')}}" stepKey="uncheckWebsite1"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct2"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct2"/> <!-- Set filter to product name and product12 assigned to both websites 1 and 2 --> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForProduct12"> @@ -85,7 +85,7 @@ <actionGroup ref="SelectProductInWebsitesActionGroup" stepKey="selectProductInWebsites1"> <argument name="website" value="{{secondCustomWebsite.name}}"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct3"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct3"/> </before> <after> <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> @@ -131,7 +131,6 @@ userInput="$$createProduct1.name$$" stepKey="seeProductName4"/> <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct12.name$$)}}" userInput="$$createProduct12.name$$" stepKey="seeProductName5"/> - <waitForText userInput="$$createCategory.name$$ (2)" stepKey="seeCorrectProductCount"/> <dontSee selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct0.name$$)}}" userInput="$$createProduct0.name$$" stepKey="dontSeeProductName"/> <dontSee selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct2.name$$)}}" @@ -151,7 +150,6 @@ userInput="$$createProduct2.name$$" stepKey="seeProductName6"/> <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct12.name$$)}}" userInput="$$createProduct12.name$$" stepKey="seeProductName7"/> - <waitForText userInput="$$createCategory.name$$ (2)" stepKey="seeCorrectProductCount2"/> <dontSee selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct0.name$$)}}" userInput="$$createProduct0.name$$" stepKey="dontSeeProductName2"/> <dontSee selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct2.name$$)}}" diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberAfterSaveAndCloseActionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberAfterSaveAndCloseActionTest.xml index eb4a561760070..fbc0354482a32 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberAfterSaveAndCloseActionTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberAfterSaveAndCloseActionTest.xml @@ -25,8 +25,8 @@ <comment userInput="Clear product grid" stepKey="commentClearProductGrid"/> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="goToProductCatalog"/> <waitForPageLoad stepKey="waitForProductIndexPage"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridToDefaultView"/> - <actionGroup ref="deleteProductsIfTheyExist" stepKey="deleteProductIfTheyExist"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridToDefaultView"/> + <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteProductIfTheyExist"/> <createData stepKey="category1" entity="SimpleSubCategory"/> <createData stepKey="product1" entity="SimpleProduct"> <requiredEntity createDataKey="category1"/> @@ -40,7 +40,7 @@ <amOnPage url="{{ProductCatalogPage.url}}" stepKey="goToProductCatalog"/> <waitForPageLoad stepKey="waitForProductIndexPage"/> <click selector="{{AdminDataGridPaginationSection.previousPage}}" stepKey="clickPrevPageOrderGrid"/> - <actionGroup ref="adminDataGridDeleteCustomPerPage" stepKey="deleteCustomAddedPerPage"> + <actionGroup ref="AdminDataGridDeleteCustomPerPageActionGroup" stepKey="deleteCustomAddedPerPage"> <argument name="perPage" value="ProductPerPage.productCount"/> </actionGroup> <deleteData stepKey="deleteCategory1" createDataKey="category1"/> @@ -51,7 +51,7 @@ </after> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="goToProductCatalog"/> <waitForPageLoad stepKey="waitForProductIndexPage"/> - <actionGroup ref="adminDataGridSelectCustomPerPage" stepKey="select1OrderPerPage"> + <actionGroup ref="AdminDataGridSelectCustomPerPageActionGroup" stepKey="select1OrderPerPage"> <argument name="perPage" value="ProductPerPage.productCount"/> </actionGroup> <!--Go to the next page and edit the product--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml index b1f00a2f51a95..76c0d7f7b931c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml @@ -15,70 +15,58 @@ <title value="Import customizable options to a product with existing SKU"/> <description value="Import customizable options to a product with existing SKU"/> <severity value="MAJOR"/> - <testCaseId value="MAGETWO-98211"/> + <testCaseId value="MC-16471"/> <useCaseId value="MAGETWO-70232"/> <group value="catalog"/> - <skip> - <issueId value="MC-17140"/> - </skip> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!--Create category--> - <comment userInput="Create category" stepKey="commentCreateCategory"/> <createData entity="ApiCategory" stepKey="createCategory"/> - <!-- Create two product --> - <comment userInput="Create two product" stepKey="commentCreateTwoProduct"/> + <!-- Create two products --> <createData entity="SimpleProduct2" stepKey="createFirstProduct"/> + <updateData entity="ProductWithTwoTextFieldOptions" createDataKey="createFirstProduct" stepKey="updateFirstProductWithCustomOptions"> + <requiredEntity createDataKey="createFirstProduct"/> + </updateData> <createData entity="ApiSimpleProduct" stepKey="createSecondProduct"> <requiredEntity createDataKey="createCategory"/> </createData> + + <!-- TODO: REMOVE AFTER FIX MC-21717 --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> - <!--Delete second product with changed sku--> - <comment userInput="Delete second product with changed sku" stepKey="commentDeleteProduct"/> - <actionGroup ref="deleteProductBySku" stepKey="deleteSecondProduct"> - <argument name="sku" value="$$createFirstProduct.sku$$-1"/> - </actionGroup> <!--Delete created data--> - <comment userInput="Delete created data" stepKey="commentDeleteCreatedData"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createFirstProduct" stepKey="deleteFirstProduct"/> + <!--Delete second product with changed sku--> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteSecondProduct"> + <argument name="sku" value="$$createFirstProduct.sku$$-1"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductGridFilter"/> <actionGroup ref="logout" stepKey="logoutOfAdmin"/> </after> - <!--Go to product page --> - <comment userInput="Go to product page" stepKey="commentGoToProductPage"/> - <amOnPage url="{{AdminProductEditPage.url($$createFirstProduct.id$$)}}" stepKey="goToProductEditPage"/> - <waitForPageLoad stepKey="waitForProductEditPageLoad"/> - <actionGroup ref="AddProductCustomOptionField" stepKey="addCutomOption1"> - <argument name="option" value="ProductOptionField"/> - </actionGroup> - <actionGroup ref="AddProductCustomOptionField" stepKey="addCutomOption2"> - <argument name="option" value="ProductOptionField2"/> - </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> <!--Change second product sku to first product sku--> - <comment userInput="Change second product sku to first product sku" stepKey="commentChangeSecondProduct"/> <amOnPage url="{{AdminProductEditPage.url($$createSecondProduct.id$$)}}" stepKey="goToProductEditPage1"/> <waitForPageLoad stepKey="waitForProductEditPageLoad1"/> <fillField selector="{{AdminProductFormSection.productSku}}" userInput="$$createFirstProduct.sku$$" stepKey="fillProductSku1"/> <!--Import customizable options and check--> - <comment userInput="Import customizable options and check" stepKey="commentImportOptions"/> <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" visible="false" stepKey="openCustomOptionSection"/> - <actionGroup ref="importProductCustomizableOptions" stepKey="importOptions"> + <actionGroup ref="ImportProductCustomizableOptionsActionGroup" stepKey="importOptions"> <argument name="productName" value="$$createFirstProduct.name$$"/> </actionGroup> - <actionGroup ref="checkCustomizableOptionImport" stepKey="checkFirstOptionImport"> + <actionGroup ref="CheckCustomizableOptionImportActionGroup" stepKey="checkFirstOptionImport"> <argument name="option" value="ProductOptionField"/> <argument name="optionIndex" value="0"/> </actionGroup> - <actionGroup ref="checkCustomizableOptionImport" stepKey="checkSecondOptionImport"> + <actionGroup ref="CheckCustomizableOptionImportActionGroup" stepKey="checkSecondOptionImport"> <argument name="option" value="ProductOptionField2"/> <argument name="optionIndex" value="1"/> </actionGroup> <!--Save product and check sku changed message--> - <comment userInput="Save product and check sku changed message" stepKey="commentSAveProductAndCheck"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct1"/> - <see userInput="SKU for product $$createSecondProduct.name$$ has been changed to $$createFirstProduct.sku$$-1." stepKey="seeSkuChangedMessage"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSecondProduct"/> + <see selector="{{AdminMessagesSection.notice}}" userInput="SKU for product $$createSecondProduct.name$$ has been changed to $$createFirstProduct.sku$$-1." stepKey="seeSkuChangedMessage"/> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassChangeProductsStatusTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassChangeProductsStatusTest.xml index 8d5121cf21461..6896b11196cf2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassChangeProductsStatusTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassChangeProductsStatusTest.xml @@ -39,10 +39,10 @@ <!-- Search and select products --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="searchProductGridByKeyword2" stepKey="searchByKeyword"> + <actionGroup ref="SearchProductGridByKeyword2ActionGroup" stepKey="searchByKeyword"> <argument name="keyword" value="api-simple-product"/> </actionGroup> - <actionGroup ref="sortProductsByIdDescending" stepKey="sortProductsByIdDescending"/> + <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckbox1"/> <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckbox2"/> <!-- Mass change status --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml index f5ad5b8079d1f..e98b145f01401 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassProductPriceUpdateTest.xml @@ -34,12 +34,12 @@ <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> <!--Search products using keyword --> - <actionGroup ref="searchProductGridByKeyword2" stepKey="searchByKeyword"> + <actionGroup ref="SearchProductGridByKeyword2ActionGroup" stepKey="searchByKeyword"> <argument name="keyword" value="Testp"/> </actionGroup> <!--Sort Products by ID in descending order--> - <actionGroup ref="sortProductsByIdDescending" stepKey="sortProductsByIdDescending"/> + <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> <!--Select products--> <checkOption selector="{{AdminProductGridSection.productRowCheckboxBySku($$simpleProduct1.sku$$)}}" stepKey="selectFirstProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml index 87e0bf3d2e9a0..71873fe5b0960 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml @@ -16,14 +16,12 @@ <description value="Admin should be able to mass update product attributes in global scope"/> <severity value="AVERAGE"/> <testCaseId value="MC-56"/> - <group value="Catalog"/> - <group value="Product Attributes"/> - <skip> - <issueId value="MC-17140"/> - </skip> + <group value="catalog"/> + <group value="product_attributes"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView" /> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="ApiSimpleProduct" stepKey="createProductOne"> @@ -38,16 +36,18 @@ <deleteData createDataKey="createProductTwo" stepKey="deleteProductTwo"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="AdminDeleteStoreViewActionGroup"/> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> + <actionGroup ref="ClearProductsFilterActionGroup" stepKey="clearProductFilter"/> + <actionGroup ref="logout" stepKey="amOnLogoutPage"/> </after> <!-- Search and select products --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="searchProductGridByKeyword2" stepKey="searchByKeyword"> + <actionGroup ref="SearchProductGridByKeyword2ActionGroup" stepKey="searchByKeyword"> <argument name="keyword" value="api-simple-product"/> </actionGroup> - <actionGroup ref="sortProductsByIdDescending" stepKey="sortProductsByIdDescending"/> + <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckbox1"/> <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckbox2"/> <!-- Mass update attributes --> @@ -58,36 +58,38 @@ <!-- Switch store view --> <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="AdminSwitchStoreViewActionGroup"/> <!-- Update attribute --> - <click selector="{{AdminEditProductAttributesSection.ChangeAttributePriceToggle}}" stepKey="toggleToChangePrice"/> + <checkOption selector="{{AdminEditProductAttributesSection.ChangeAttributePriceToggle}}" stepKey="toggleToChangePrice"/> <fillField selector="{{AdminEditProductAttributesSection.AttributePrice}}" userInput="$$createProductOne.price$$0" stepKey="fillAttributeNameField"/> <click selector="{{AdminEditProductAttributesSection.Save}}" stepKey="save"/> - <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpateSuccessMsg"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" time="60" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="Message is added to queue" stepKey="seeAttributeUpdateSuccessMsg"/> <!-- Run cron twice --> - <magentoCLI command="cron:run" stepKey="runCron1"/> - <magentoCLI command="cron:run" stepKey="runCron2"/> - <reloadPage stepKey="refreshPage"/> - <waitForPageLoad stepKey="waitFormToReload1"/> + <magentoCLI command="cron:run" arguments="--group=consumers" stepKey="runCron1"/> + <magentoCLI command="cron:run" arguments="--group=consumers" stepKey="runCron2"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> <!-- Assert on storefront default view --> <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupDefault"/> <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndPriceActionGroup" stepKey="searchByNameDefault"> - <argument name="name" value="$$createProductOne.name$$"/> + <argument name="name" value=""$$createProductOne.name$$""/> <argument name="priceFrom" value="$$createProductOne.price$$0"/> <argument name="priceTo" value="$$createProductOne.price$$0"/> </actionGroup> <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault"/> + <waitForElementVisible selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="waitForSearchResultInDefaultView"/> <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInDefault"/> <!-- Assert on storefront custom view --> <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroupCustom"/> <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="StorefrontSwitchStoreViewActionGroup"/> <actionGroup ref="StorefrontAdvancedCatalogSearchByProductNameAndPriceActionGroup" stepKey="searchByNameCustom"> - <argument name="name" value="$$createProductOne.name$$"/> + <argument name="name" value=""$$createProductOne.name$$""/> <argument name="priceFrom" value="$$createProductOne.price$$0"/> <argument name="priceTo" value="$$createProductOne.price$$0"/> </actionGroup> <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultCustom"/> + <waitForElementVisible selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="waitForSearchResultInCustomView"/> <see userInput="1 item" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="seeInCustom"/> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesMissingRequiredFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesMissingRequiredFieldTest.xml index fe0e46369c5e6..a4c8f5e8cff6c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesMissingRequiredFieldTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesMissingRequiredFieldTest.xml @@ -39,7 +39,7 @@ <!-- Search and select products --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="searchProductGridByKeyword2" stepKey="searchByKeyword"> + <actionGroup ref="SearchProductGridByKeyword2ActionGroup" stepKey="searchByKeyword"> <argument name="keyword" value="api-simple-product"/> </actionGroup> <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckbox1"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml index 18d4b9e341cc6..c9840fe455f84 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesStoreViewScopeTest.xml @@ -38,10 +38,10 @@ <!-- Search and select products --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="searchProductGridByKeyword2" stepKey="searchByKeyword"> + <actionGroup ref="SearchProductGridByKeyword2ActionGroup" stepKey="searchByKeyword"> <argument name="keyword" value="api-simple-product"/> </actionGroup> - <actionGroup ref="sortProductsByIdDescending" stepKey="sortProductsByIdDescending"/> + <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckbox1"/> <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckbox2"/> <!-- Mass update attributes --> @@ -114,10 +114,10 @@ <!-- Search and select products --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="searchProductGridByKeyword2" stepKey="searchByKeyword"> + <actionGroup ref="SearchProductGridByKeyword2ActionGroup" stepKey="searchByKeyword"> <argument name="keyword" value="api-simple-product"/> </actionGroup> - <actionGroup ref="sortProductsByIdDescending" stepKey="sortProductsByIdDescending"/> + <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('1')}}" stepKey="clickCheckbox1"/> <click selector="{{AdminProductGridSection.productGridCheckboxOnRow('2')}}" stepKey="clickCheckbox2"/> <!-- Mass update attributes --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml index 02e8157282dee..21c6c56adfd96 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductStatusStoreViewScopeTest.xml @@ -55,13 +55,13 @@ <see userInput="You saved the store view." stepKey="seeSavedMessage" /> <!--Create a Simple Product 1 --> - <actionGroup ref="createSimpleProductAndAddToWebsite" stepKey="createSimpleProduct1"> + <actionGroup ref="CreateSimpleProductAndAddToWebsiteActionGroup" stepKey="createSimpleProduct1"> <argument name="product" value="simpleProductForMassUpdate"/> <argument name="website" value="Second Website"/> </actionGroup> <!--Create a Simple Product 2 --> - <actionGroup ref="createSimpleProductAndAddToWebsite" stepKey="createSimpleProduct2"> + <actionGroup ref="CreateSimpleProductAndAddToWebsiteActionGroup" stepKey="createSimpleProduct2"> <argument name="product" value="simpleProductForMassUpdate2"/> <argument name="website" value="Second Website"/> </actionGroup> @@ -86,10 +86,10 @@ <!-- Search and select products --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="searchProductGridByKeyword2" stepKey="searchByKeyword"> + <actionGroup ref="SearchProductGridByKeyword2ActionGroup" stepKey="searchByKeyword"> <argument name="keyword" value="{{simpleProductForMassUpdate.keyword}}"/> </actionGroup> - <actionGroup ref="sortProductsByIdDescending" stepKey="sortProductsByIdDescending"/> + <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> <!-- Filter to Second Store View --> <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterStoreView" > @@ -205,13 +205,13 @@ <see userInput="You saved the store view." stepKey="seeSavedMessage" /> <!--Create a Simple Product 1 --> - <actionGroup ref="createSimpleProductAndAddToWebsite" stepKey="createSimpleProduct1"> + <actionGroup ref="CreateSimpleProductAndAddToWebsiteActionGroup" stepKey="createSimpleProduct1"> <argument name="product" value="simpleProductForMassUpdate"/> <argument name="website" value="Second Website"/> </actionGroup> <!--Create a Simple Product 2 --> - <actionGroup ref="createSimpleProductAndAddToWebsite" stepKey="createSimpleProduct2"> + <actionGroup ref="CreateSimpleProductAndAddToWebsiteActionGroup" stepKey="createSimpleProduct2"> <argument name="product" value="simpleProductForMassUpdate2"/> <argument name="website" value="Second Website"/> </actionGroup> @@ -236,10 +236,10 @@ <!-- Search and select products --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="searchProductGridByKeyword2" stepKey="searchByKeyword"> + <actionGroup ref="SearchProductGridByKeyword2ActionGroup" stepKey="searchByKeyword"> <argument name="keyword" value="{{simpleProductForMassUpdate.keyword}}"/> </actionGroup> - <actionGroup ref="sortProductsByIdDescending" stepKey="sortProductsByIdDescending"/> + <actionGroup ref="SortProductsByIdDescendingActionGroup" stepKey="sortProductsByIdDescending"/> <!-- Filter to Second Store View --> <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterStoreView" > @@ -299,4 +299,4 @@ <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="StorefrontCheckAdvancedSearchResultDefault2"/> <see userInput="We can't find any items matching these search criteria." selector="{{StorefrontCatalogSearchAdvancedResultMainSection.message}}" stepKey="seeInDefault2"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryToDefaultCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryToDefaultCategoryTest.xml index 247711295a555..a8a8ede297b44 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryToDefaultCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryToDefaultCategoryTest.xml @@ -69,6 +69,10 @@ <waitForPageLoad stepKey="waitForSecondCategoryToSave2"/> <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="seeSuccessMessage2"/> + <!-- TODO: REMOVE AFTER FIX MC-21717 --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + <!--Open Category in store front page--> <amOnPage url="/$$createDefaultCategory.name$$/{{FirstLevelSubCat.name}}/{{SimpleSubCategory.name}}.html" stepKey="seeTheCategoryInStoreFrontPage"/> <waitForPageLoad stepKey="waitForStoreFrontPageLoad"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryFromParentAnchoredCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryFromParentAnchoredCategoryTest.xml index b613068893b0e..271d78ab9cdb0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryFromParentAnchoredCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryFromParentAnchoredCategoryTest.xml @@ -25,7 +25,7 @@ <after> <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> <deleteData createDataKey="createDefaultCategory" stepKey="deleteDefaultCategory"/> - <actionGroup ref="DeleteCategory" stepKey="deleteCategory"> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"> <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryToAnotherPositionInCategoryTreeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryToAnotherPositionInCategoryTreeTest.xml index 9831f73e07877..6c403fc7714eb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryToAnotherPositionInCategoryTreeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveCategoryToAnotherPositionInCategoryTreeTest.xml @@ -25,7 +25,7 @@ </before> <after> <deleteData createDataKey="createDefaultCategory" stepKey="deleteDefaultCategory"/> - <actionGroup ref="DeleteCategory" stepKey="SecondLevelSubCat"> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="SecondLevelSubCat"> <argument name="categoryEntity" value="SecondLevelSubCat"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml index da985fc2ce34d..7e6e79cd08c26 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml @@ -94,7 +94,7 @@ <waitForPageLoad stepKey="waitForProductPage"/> <!-- Click on <product1>: Product page opens--> - <actionGroup ref="filterProductGridByName" stepKey="filterProduct"> + <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="filterProduct"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> <click selector="{{AdminProductGridSection.productGridNameProduct($$simpleProduct.name$$)}}" stepKey="clickProduct1"/> @@ -159,16 +159,16 @@ <see userInput="$$createAnchoredCategory1.name$$" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="seeCategory1Name"/> <see userInput="We can't find products matching the selection." stepKey="seeEmptyNotice"/> <dontSee userInput="$$simpleProduct.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="dontseeProduct"/> - + <!-- Log in to the backend: Admin user is logged in--> <actionGroup ref="LoginAsAdmin" stepKey="LoginAdmin"/> - + <!-- Navigate to the Catalog > Products: Navigate to the Catalog>Products --> <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="amOnProductPage"/> <waitForPageLoad stepKey="waitForProductsPage"/> <!-- Click on <product1> --> - <actionGroup ref="filterAndSelectProduct" stepKey="openSimpleProduct"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="openSimpleProduct"> <argument name="productSku" value="$$simpleProduct.sku$$"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminNavigateMultipleUpSellProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminNavigateMultipleUpSellProductsTest.xml index bcd4ca8531203..5dd8b2e430941 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminNavigateMultipleUpSellProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminNavigateMultipleUpSellProductsTest.xml @@ -97,7 +97,7 @@ <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> <!--Select SimpleProduct --> - <actionGroup ref="filterProductGridBySku" stepKey="findCreatedProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> <click stepKey="openFirstProduct" selector="{{AdminProductGridSection.productRowBySku($$createSimpleProduct.sku$$)}}"/> @@ -106,7 +106,7 @@ <!--Add SimpleProduct1 and ConfigProduct as Up sell products--> <click stepKey="clickOnRelatedProducts" selector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedProductsHeader}}"/> <click stepKey="clickOnAddUpSellProducts" selector="{{AdminProductFormRelatedUpSellCrossSellSection.addUpSellProduct}}"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterProduct"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProduct"> <argument name="sku" value="$$createSimpleProduct1.sku$$"/> </actionGroup> <waitForPageLoad stepKey="waitForTheProductToLoad"/> @@ -114,7 +114,7 @@ <click stepKey="addSelectedProduct" selector="{{AdminAddRelatedProductsModalSection.AddUpSellProductsButton}}"/> <waitForPageLoad stepKey="waitForProductToBeAdded"/> <click stepKey="clickOnAddUpSellProductsButton" selector="{{AdminProductFormRelatedUpSellCrossSellSection.addUpSellProduct}}"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterConfigurableProduct"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterConfigurableProduct"> <argument name="sku" value="$$createConfigProduct.sku$$"/> </actionGroup> <waitForPageLoad stepKey="waitForTheConfigProductToLoad"/> @@ -131,7 +131,7 @@ <waitForPageLoad stepKey="waitForProductsToBeLoaded"/> <!--Select Configurable Product--> - <actionGroup ref="filterProductGridBySku" stepKey="findConfigProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findConfigProduct"> <argument name="product" value="$$createConfigProduct$$"/> </actionGroup> <click stepKey="openConfigProduct" selector="{{AdminProductGridSection.productRowBySku($$createConfigProduct.sku$$)}}"/> @@ -140,7 +140,7 @@ <!--Add SimpleProduct1 as Up Sell Product--> <click stepKey="clickOnRelatedProductsHeader" selector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedProductsHeader}}"/> <click stepKey="clickOnAddUpSellProductsButton1" selector="{{AdminProductFormRelatedUpSellCrossSellSection.addUpSellProduct}}"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterSimpleProduct2"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterSimpleProduct2"> <argument name="sku" value="$$createSimpleProduct1.sku$$"/> </actionGroup> <waitForPageLoad stepKey="waitForTheSimpleProduct2ToBeLoaded"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml index 41d3ca3020c98..2283a0e4d6158 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml @@ -148,7 +148,7 @@ <waitForPageLoad stepKey="waitForProductC1PageLoad"/> <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="clickOffEnableToggleAgain"/> <!-- Saved successfully --> - <actionGroup ref="saveProductForm" stepKey="saveProductC1"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductC1"/> <!-- 12. Open category B on Storefront --> <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="toCategoryBStorefront"> @@ -205,7 +205,7 @@ <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="clickOnEnableToggleAgain"/> <!-- Saved successfully --> - <actionGroup ref="saveProductForm" stepKey="saveChangedProductC1"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveChangedProductC1"/> <!-- 17.12. Open category B on Storefront --> <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="openCategoryB"> @@ -264,7 +264,7 @@ stepKey="changeVisibility"/> <!-- Saved successfully --> - <actionGroup ref="saveProductForm" stepKey="productC1Saved"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="productC1Saved"/> <!-- 18.12. Open category B on Storefront --> <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="goPageCategoryB"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKey.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKey.xml index bae81513de632..df09768139533 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKey.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCustomURLKeyPreservedWhenAssignedToCategoryWithoutCustomURLKey.xml @@ -61,20 +61,20 @@ <conditionalClick selector="{{AdminProductSEOSection.sectionHeader}}" dependentSelector="{{AdminProductSEOSection.urlKeyInput}}" visible="false" stepKey="openSeoSection"/> <uncheckOption selector="{{AdminProductSEOSection.useDefaultUrl}}" stepKey="uncheckUseDefaultUrlKey"/> <fillField userInput="U2" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> - <actionGroup ref="goToAdminCategoryPageById" stepKey="openCategory"> + <actionGroup ref="GoToAdminCategoryPageByIdActionGroup" stepKey="openCategory"> <argument name="id" value="$createCategory.id$"/> </actionGroup> - <actionGroup ref="AdminCategoryAssignProduct" stepKey="assignSimpleProductFirst"> + <actionGroup ref="AdminCategoryAssignProductActionGroup" stepKey="assignSimpleProductFirst"> <argument name="productSku" value="$createSimpleProductFirst.sku$"/> </actionGroup> - <actionGroup ref="AdminCategoryAssignProduct" stepKey="assignSimpleProductSecond"> + <actionGroup ref="AdminCategoryAssignProductActionGroup" stepKey="assignSimpleProductSecond"> <argument name="productSku" value="$createSimpleProductSecond.sku$"/> </actionGroup> - <actionGroup ref="saveCategoryForm" stepKey="saveCategory"/> + <actionGroup ref="SaveCategoryFormActionGroup" stepKey="saveCategory"/> <executeJS function="return '$createCategory.name$'.toLowerCase();" stepKey="categoryNameLower" /> <executeJS function="return '$createSimpleProductFirst.name$'.toLowerCase();" stepKey="simpleProductFirstNameLower" /> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml new file mode 100644 index 0000000000000..6f65865924bad --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml @@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminProductGridFilteringByCustomAttributeTest"> + <annotations> + <features value="Catalog"/> + <stories value="Product grid"/> + <title value="Sorting the product grid by custom product attribute"/> + <description value="Sorting the product grid by custom product attribute should sort Alphabetically instead of value id"/> + <severity value="MAJOR"/> + <useCaseId value="MC-19031"/> + <testCaseId value="MC-20329"/> + <group value="catalog"/> + </annotations> + <before> + <!--Login as admin and delete all products --> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> + <!--Create dropdown product attribute--> + <createData entity="productDropDownAttribute" stepKey="createDropdownAttribute"/> + <!--Create attribute options--> + <createData entity="ProductAttributeOption7" stepKey="createFirstProductAttributeOption"> + <requiredEntity createDataKey="createDropdownAttribute"/> + </createData> + <createData entity="ProductAttributeOption8" stepKey="createSecondProductAttributeOption"> + <requiredEntity createDataKey="createDropdownAttribute"/> + </createData> + <createData entity="ProductAttributeOption9" stepKey="createThirdProductAttributeOption"> + <requiredEntity createDataKey="createDropdownAttribute"/> + </createData> + <!--Add attribute to default attribute set--> + <createData entity="AddToDefaultSet" stepKey="addAttributeToDefaultSet"> + <requiredEntity createDataKey="createDropdownAttribute"/> + </createData> + <!--Create category--> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <!--Create 3 products--> + <createData entity="ApiSimpleProduct" stepKey="createFirstProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createSecondProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createThirdProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!--Update first product--> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForFirstProduct"> + <argument name="product" value="$$createFirstProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="editFirstProduct"> + <argument name="product" value="$$createFirstProduct$$"/> + </actionGroup> + <selectOption selector="{{AdminProductFormSection.customSelectField($$createDropdownAttribute.attribute[attribute_code]$$)}}" userInput="$$createFirstProductAttributeOption.option[store_labels][0][label]$$" stepKey="setFirstAttributeValue"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveFirstProduct"/> + <!--Update second product--> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSecondProduct"> + <argument name="product" value="$$createSecondProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="editSecondProduct"> + <argument name="product" value="$$createSecondProduct$$"/> + </actionGroup> + <selectOption selector="{{AdminProductFormSection.customSelectField($$createDropdownAttribute.attribute[attribute_code]$$)}}" userInput="$$createSecondProductAttributeOption.option[store_labels][0][label]$$" stepKey="setSecondAttributeValue"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSecondProduct"/> + <!--Update third product--> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForThirdProduct"> + <argument name="product" value="$$createThirdProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="editThirdProduct"> + <argument name="product" value="$$createThirdProduct$$"/> + </actionGroup> + <selectOption selector="{{AdminProductFormSection.customSelectField($$createDropdownAttribute.attribute[attribute_code]$$)}}" userInput="$$createThirdProductAttributeOption.option[store_labels][0][label]$$" stepKey="setThirdAttributeValue"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveThirdProduct"/> + </before> + <after> + <!--Delete products--> + <deleteData createDataKey="createFirstProduct" stepKey="deleteFirstProduct"/> + <deleteData createDataKey="createSecondProduct" stepKey="deleteSecondProduct"/> + <deleteData createDataKey="createThirdProduct" stepKey="deleteThirdProduct"/> + <!--Delete attribute--> + <deleteData createDataKey="createDropdownAttribute" stepKey="deleteDropdownAttribute"/> + <!--Delete category--> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="NavigateToAndResetProductGridToDefaultViewActionGroup" stepKey="NavigateToAndResetProductGridToDefaultViewAfterTest"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductGridPageLoad"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductGridFilters"/> + <!--Sort by custom attribute DESC using grabbed value--> + <conditionalClick selector="{{AdminProductGridSection.columnHeader($$createDropdownAttribute.attribute[frontend_labels][0][label]$$)}}" dependentSelector="{{AdminProductGridSection.columnHeader($$createDropdownAttribute.attribute[frontend_labels][0][label]$$)}}" visible="true" stepKey="ascendSortByCustomAttribute"/> + <waitForPageLoad stepKey="waitForProductGridLoad"/> + <!--Check products sorting. Expected result => Blue-Green-Red --> + <see selector="{{AdminProductGridSection.productGridNameProduct($$createSecondProduct.name$$)}}" userInput="$$createSecondProduct.name$$" stepKey="seeSecondProductName"/> + <see selector="{{AdminProductGridSection.productGridNameProduct($$createFirstProduct.name$$)}}" userInput="$$createFirstProduct.name$$" stepKey="seeFirstProductName"/> + <see selector="{{AdminProductGridSection.productGridNameProduct($$createThirdProduct.name$$)}}" userInput="$$createThirdProduct.name$$" stepKey="seeThirdProductName"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByDateAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByDateAttributeTest.xml index 2884cb26cf813..df2b525a56086 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByDateAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByDateAttributeTest.xml @@ -45,7 +45,7 @@ <click selector="{{AdminProductGridFilterSection.columnsDropdown}}" stepKey="closeColumnsDropdown1"/> <seeElement selector="{{AdminProductGridSection.columnHeader('Set Product as New from Date')}}" stepKey="seeNewFromDateColumn"/> <waitForPageLoad stepKey="waitforFiltersToApply"/> - <actionGroup ref="filterProductGridBySetNewFromDate" stepKey="filterProductGridToCheckSetAsNewColumn"/> + <actionGroup ref="FilterProductGridBySetNewFromDateActionGroup" stepKey="filterProductGridToCheckSetAsNewColumn"/> <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnFirstRowProductGrid"/> <waitForPageLoad stepKey="waitForProductEditPageToLoad"/> <actionGroup ref="AdminFormSaveAndClose" stepKey="saveAndCloseProductForm"/> @@ -57,4 +57,4 @@ <click selector="{{AdminProductGridFilterSection.columnsDropdown}}" stepKey="closeColumnsDropdown2"/> <click selector="{{AdminProductGridFilterSection.clearAll}}" stepKey="clearGridFilters"/> </test> - </tests> \ No newline at end of file + </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductImageAssignmentForMultipleStoresTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductImageAssignmentForMultipleStoresTest.xml index 8149bc34087fb..455e77666c3a2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductImageAssignmentForMultipleStoresTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductImageAssignmentForMultipleStoresTest.xml @@ -50,7 +50,7 @@ <argument name="customStore" value="customStoreFR"/> </actionGroup> <!-- Clear Filter Store --> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="resetFiltersOnStorePage"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="resetFiltersOnStorePage"/> <!-- Delete Category and Simple Product --> <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> @@ -73,8 +73,8 @@ </actionGroup> <!-- Upload Image English --> - <actionGroup ref="addProductImage" stepKey="uploadImageEnglish"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct1"/> + <actionGroup ref="AddProductImageActionGroup" stepKey="uploadImageEnglish"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1"/> <!-- Switch to the French store view --> <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="switchStoreViewFrenchProduct"> @@ -82,19 +82,19 @@ </actionGroup> <!-- Upload Image French --> - <actionGroup ref="addProductImage" stepKey="uploadImageFrench"> + <actionGroup ref="AddProductImageActionGroup" stepKey="uploadImageFrench"> <argument name="image" value="Magento3"/> </actionGroup> <actionGroup ref="AdminAssignImageRolesActionGroup" stepKey="assignImageRole1"> <argument name="image" value="Magento3"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct2"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct2"/> <!-- Switch to the All store view --> <actionGroup ref="AdminSwitchToAllStoreViewActionGroup" stepKey="switchAllStoreViewProduct"/> <!-- Upload Image All Store View --> - <actionGroup ref="addProductImage" stepKey="uploadImageAllStoreView"> + <actionGroup ref="AddProductImageActionGroup" stepKey="uploadImageAllStoreView"> <argument name="image" value="TestImageNew"/> </actionGroup> <actionGroup ref="AdminAssignImageRolesActionGroup" stepKey="assignImageRole"> @@ -105,7 +105,7 @@ <click selector="{{AdminProductContentSection.sectionHeader}}" stepKey="openDescriptionDropDown"/> <fillField selector="{{AdminProductContentSection.descriptionTextArea}}" userInput="This is the long description" stepKey="fillLongDescription"/> <fillField selector="{{AdminProductContentSection.shortDescriptionTextArea}}" userInput="This is the short description" stepKey="fillShortDescription"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Go to Product Page and see Default Store View--> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.custom_attributes[url_key]$$)}}" stepKey="goToDefaultStorefrontProductPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml index de065d2d930cb..0b7e2a70735c3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml @@ -42,14 +42,14 @@ <waitForPageLoad stepKey="waitForDownloadableProductPageLoad"/> <actionGroup ref="AdminAddDownloadableLinkInformationActionGroup" stepKey="addDownloadableLinkInformation"/> <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately"/> - <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addDownloadableProductLink"> <argument name="link" value="downloadableLinkWithMaxDownloads"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveDownloadableProductForm"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveDownloadableProductForm"/> <!--Assert downloadable product on Admin product page grid--> <comment userInput="Assert configurable product in Admin product page grid" stepKey="commentAssertDownloadableProductOnAdmin"/> <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPage"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGridBySku"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySku"> <argument name="sku" value="$$createProduct.sku$$"/> </actionGroup> <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeDownloadableProductNameInGrid"/> @@ -80,11 +80,11 @@ <waitForPageLoad stepKey="waitForProductPageLoad"/> <actionGroup ref="AdminAddDownloadableLinkInformationActionGroup" stepKey="addDownloadableLinkInformation"/> <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeightForProduct"/> - <actionGroup ref="saveProductForm" stepKey="saveProductForm"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> <!--Assert simple product on Admin product page grid--> <comment userInput="Assert simple product in Admin product page grid" stepKey="commentAssertProductOnAdmin"/> <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogSimpleProductPage"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterSimpleProductGridBySku"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterSimpleProductGridBySku"> <argument name="sku" value="$$createProduct.sku$$"/> </actionGroup> <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeSimpleProductNameInGrid"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveCustomOptionsFromProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveCustomOptionsFromProductTest.xml index b5f212d1144be..fb54b0b601d85 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveCustomOptionsFromProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveCustomOptionsFromProductTest.xml @@ -27,66 +27,66 @@ <after> <deleteData createDataKey="createProduct" stepKey="deleteProductWithOptions"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearProductFilter"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductFilter"/> <actionGroup ref="logout" stepKey="logout"/> </after> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!-- Edit Simple Product --> <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="goToProduct"/> <!-- See 3 options are present --> - <actionGroup ref="AdminAssertProductCustomOptionVisible" stepKey="assertCustomOptionsField"> + <actionGroup ref="AdminAssertProductCustomOptionVisibleActionGroup" stepKey="assertCustomOptionsField"> <argument name="option" value="ProductOptionField"/> </actionGroup> - <actionGroup ref="AdminAssertProductCustomOptionVisible" stepKey="assertCustomOptionsArea"> + <actionGroup ref="AdminAssertProductCustomOptionVisibleActionGroup" stepKey="assertCustomOptionsArea"> <argument name="option" value="ProductOptionArea"/> </actionGroup> - <actionGroup ref="AdminAssertProductCustomOptionVisible" stepKey="assertCustomOptionsFile"> + <actionGroup ref="AdminAssertProductCustomOptionVisibleActionGroup" stepKey="assertCustomOptionsFile"> <argument name="option" value="ProductOptionFile"/> </actionGroup> <!-- Click delete "Area" and "File" options --> - <actionGroup ref="AdminDeleteProductCustomOption" stepKey="deleteCustomOptionArea"> + <actionGroup ref="AdminDeleteProductCustomOptionActionGroup" stepKey="deleteCustomOptionArea"> <argument name="option" value="ProductOptionArea"/> </actionGroup> - <actionGroup ref="AdminDeleteProductCustomOption" stepKey="deleteCustomOptionFile"> + <actionGroup ref="AdminDeleteProductCustomOptionActionGroup" stepKey="deleteCustomOptionFile"> <argument name="option" value="ProductOptionFile"/> </actionGroup> - <actionGroup ref="AdminAssertProductCustomOptionVisible" stepKey="assertVisibleCustomOptionField"> + <actionGroup ref="AdminAssertProductCustomOptionVisibleActionGroup" stepKey="assertVisibleCustomOptionField"> <argument name="option" value="ProductOptionField"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- See only "Field option" left Also we shouldn't see any other options --> - <actionGroup ref="AdminAssertProductCustomOptionVisible" stepKey="assertVisibleSecondCustomOptionField"> + <actionGroup ref="AdminAssertProductCustomOptionVisibleActionGroup" stepKey="assertVisibleSecondCustomOptionField"> <argument name="option" value="ProductOptionField"/> </actionGroup> - <actionGroup ref="AdminAssertProductHasNoCustomOption" stepKey="assertNoCustomOptionsFile"> + <actionGroup ref="AdminAssertProductHasNoCustomOptionActionGroup" stepKey="assertNoCustomOptionsFile"> <argument name="option" value="ProductOptionFileSecond"/> </actionGroup> - <actionGroup ref="AdminAssertProductHasNoCustomOption" stepKey="assertNoCustomOptionsField"> + <actionGroup ref="AdminAssertProductHasNoCustomOptionActionGroup" stepKey="assertNoCustomOptionsField"> <argument name="option" value="ProductOptionFieldSecond"/> </actionGroup> <!-- Click Add option "File" --> - <actionGroup ref="AddProductCustomOptionFile" stepKey="createAddOptionFile"> + <actionGroup ref="AddProductCustomOptionFileActionGroup" stepKey="createAddOptionFile"> <argument name="option" value="ProductOptionFileSecond"/> </actionGroup> <!-- Click Add option "Field" --> - <actionGroup ref="AddProductCustomOptionField" stepKey="createCustomOptionField"> + <actionGroup ref="AddProductCustomOptionFieldActionGroup" stepKey="createCustomOptionField"> <argument name="option" value="ProductOptionFieldSecond"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProductWithNewlyAddedOptions"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductWithNewlyAddedOptions"/> <!-- See 3 options are present --> - <actionGroup ref="AdminAssertProductCustomOptionVisible" stepKey="assertPresentCustomOptionField"> + <actionGroup ref="AdminAssertProductCustomOptionVisibleActionGroup" stepKey="assertPresentCustomOptionField"> <argument name="option" value="ProductOptionField"/> </actionGroup> - <actionGroup ref="AdminAssertProductCustomOptionVisible" stepKey="assertPresenceOfFileOption"> + <actionGroup ref="AdminAssertProductCustomOptionVisibleActionGroup" stepKey="assertPresenceOfFileOption"> <argument name="option" value="ProductOptionFileSecond"/> </actionGroup> - <actionGroup ref="AdminAssertProductCustomOptionVisible" stepKey="assertPresenceOfFieldOption"> + <actionGroup ref="AdminAssertProductCustomOptionVisibleActionGroup" stepKey="assertPresenceOfFieldOption"> <argument name="option" value="ProductOptionFieldSecond"/> </actionGroup> <!-- Delete All options and See no more options present on the page --> - <actionGroup ref="AdminDeleteAllProductCustomOptions" stepKey="deleteAllCustomOptions"/> + <actionGroup ref="AdminDeleteAllProductCustomOptionsActionGroup" stepKey="deleteAllCustomOptions"/> <!-- Product successfully saved and it has no options --> - <actionGroup ref="saveProductForm" stepKey="saveProductWithoutCustomOptions"/> - <actionGroup ref="AdminAssertProductHasNoCustomOptions" stepKey="assertNoCustomOptions"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductWithoutCustomOptions"/> + <actionGroup ref="AdminAssertProductHasNoCustomOptionsActionGroup" stepKey="assertNoCustomOptions"/> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml index 9760dc579b10b..3b750c2cdb21c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageSimpleProductTest.xml @@ -28,35 +28,35 @@ <!--Create product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPage"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateSimpleProduct"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateSimpleProduct"> <argument name="product" value="SimpleProduct3"/> </actionGroup> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillSimpleProductMain"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillSimpleProductMain"> <argument name="product" value="SimpleProduct3"/> </actionGroup> <!-- Add image to product --> - <actionGroup ref="addProductImage" stepKey="addImageForProductSimple"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImageForProductSimple"> <argument name="image" value="MagentoLogo"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveSimpleProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSimpleProduct"/> <!-- Remove image from product --> - <actionGroup ref="removeProductImage" stepKey="removeProductImage"/> + <actionGroup ref="RemoveProductImageActionGroup" stepKey="removeProductImage"/> - <actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductFormAfterRemove"/> <!-- Assert product image not in admin product form --> - <actionGroup ref="assertProductImageNotInAdminProductPage" stepKey="assertProductImageNotInAdminProductPage"/> + <actionGroup ref="AssertProductImageNotInAdminProductPageActionGroup" stepKey="assertProductImageNotInAdminProductPage"/> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPageAfterRemove"> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPageAfterRemove"> <argument name="product" value="SimpleProduct3"/> </actionGroup> <!-- Assert product image not in storefront product page --> - <actionGroup ref="assertProductImageNotInStorefrontProductPage" stepKey="assertProductImageNotInStorefrontProductPage"> + <actionGroup ref="AssertProductImageNotInStorefrontProductPageActionGroup" stepKey="assertProductImageNotInStorefrontProductPage"> <argument name="product" value="SimpleProduct3"/> </actionGroup> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml index a740b700c3026..6a68928be8c70 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultImageVirtualProductTest.xml @@ -28,35 +28,35 @@ <!--Create product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPage"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="defaultVirtualProduct"/> </actionGroup> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductMain"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillProductMain"> <argument name="product" value="defaultVirtualProduct"/> </actionGroup> <!-- Add image to product --> - <actionGroup ref="addProductImage" stepKey="addImageForProduct"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImageForProduct"> <argument name="image" value="MagentoLogo"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Remove image from product --> - <actionGroup ref="removeProductImage" stepKey="removeProductImage"/> + <actionGroup ref="RemoveProductImageActionGroup" stepKey="removeProductImage"/> - <actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductFormAfterRemove"/> <!-- Assert product image not in admin product form --> - <actionGroup ref="assertProductImageNotInAdminProductPage" stepKey="assertProductImageNotInAdminProductPage"/> + <actionGroup ref="AssertProductImageNotInAdminProductPageActionGroup" stepKey="assertProductImageNotInAdminProductPage"/> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPageAfterRemove"> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPageAfterRemove"> <argument name="product" value="defaultVirtualProduct"/> </actionGroup> <!-- Assert product image not in storefront product page --> - <actionGroup ref="assertProductImageNotInStorefrontProductPage" stepKey="assertProductImageNotInStorefrontProductPage"> + <actionGroup ref="AssertProductImageNotInStorefrontProductPageActionGroup" stepKey="assertProductImageNotInStorefrontProductPage"> <argument name="product" value="defaultVirtualProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml index 876eedb9347c7..8c80a2bf9a851 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml @@ -30,21 +30,21 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="adminProductIndexPageAdd"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> <actionGroup ref="EnableAdminAccountSharingActionGroup" stepKey="enableAdminAccountSharing"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProductPage"> <argument name="product" value="ApiSimpleProduct"/> </actionGroup> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillMainProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillMainProductForm"> <argument name="product" value="ApiSimpleProduct"/> </actionGroup> <!-- Save product --> - <actionGroup ref="saveProductForm" stepKey="saveProductForm"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> <!-- Save product --> - <actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductFormAfterRemove"/> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="ApiSimpleProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoVirtualProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoVirtualProductTest.xml index 8b3b38d0ece31..8d89e0d9b535b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoVirtualProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveDefaultVideoVirtualProductTest.xml @@ -21,13 +21,13 @@ <!-- Replacing steps in base AdminRemoveDefaultVideoSimpleProductTest --> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProductPage"> <argument name="product" value="defaultVirtualProduct"/> </actionGroup> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillMainProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillMainProductForm"> <argument name="product" value="defaultVirtualProduct"/> </actionGroup> - <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="defaultVirtualProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml index 8316f54c15a52..47f497b381553 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageAffectsAllScopesTest.xml @@ -55,7 +55,7 @@ </before> <after> - <actionGroup ref="ResetWebUrlOptions" stepKey="resetUrlOption"/> + <actionGroup ref="ResetWebUrlOptionsActionGroup" stepKey="resetUrlOption"/> <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> <argument name="websiteName" value="FirstWebSite"/> </actionGroup> @@ -73,26 +73,26 @@ <!--Create product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPage"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/> <!--Open created product--> <click selector="{{AdminProductGridSection.productGridNameProduct($$product.name$$)}}" stepKey="createdProduct"/> <waitForPageLoad stepKey="waitForOpenedCreatedProduct"/> <!-- Add image to product --> - <actionGroup ref="addProductImage" stepKey="addFirstImageForProduct"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addFirstImageForProduct"> <argument name="image" value="TestImageNew"/> </actionGroup> <!-- Add second image to product --> - <actionGroup ref="addProductImage" stepKey="addSecondImageForProduct"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addSecondImageForProduct"> <argument name="image" value="MagentoLogo"/> </actionGroup> <!--"Product in Websites": select both Websites--> - <actionGroup ref="ProductSetWebsite" stepKey="ProductSetWebsite1"> + <actionGroup ref="ProductSetWebsiteActionGroup" stepKey="ProductSetWebsite1"> <argument name="website" value="FirstWebSite"/> </actionGroup> - <actionGroup ref="ProductSetWebsite" stepKey="ProductSetWebsite2"> + <actionGroup ref="ProductSetWebsiteActionGroup" stepKey="ProductSetWebsite2"> <argument name="website" value="SecondWebSite"/> </actionGroup> @@ -103,28 +103,28 @@ <waitForPageLoad stepKey="waitForCreatedProductOpened"/> <!--Delete Image 1--> - <actionGroup ref="removeProductImage" stepKey="removeProductImage"/> + <actionGroup ref="RemoveProductImageActionGroup" stepKey="removeProductImage"/> <!--Click "Save" in the upper right corner--> - <actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductFormAfterRemove"/> <!--Switch to "Store view 1"--> - <actionGroup ref="SwitchToTheNewStoreView" stepKey="selectStoreView"> + <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="selectStoreView"> <argument name="storeViewName" value="Store View"/> </actionGroup> <!-- Assert product first image not in admin product form --> - <actionGroup ref="assertProductImageNotInAdminProductPage" stepKey="assertProductImageNotInAdminProductPage"> + <actionGroup ref="AssertProductImageNotInAdminProductPageActionGroup" stepKey="assertProductImageNotInAdminProductPage"> <argument name="image" value="TestImageNew"/> </actionGroup> <!--Switch to "Store view 2"--> - <actionGroup ref="SwitchToTheNewStoreView" stepKey="selectSecondStoreView"> + <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="selectSecondStoreView"> <argument name="storeViewName" value="Second Store View"/> </actionGroup> <!-- Verify that Image 1 is deleted from the Second Store View list --> - <actionGroup ref="assertProductImageNotInAdminProductPage" stepKey="assertProductImageNotInSecondStoreViewPage"> + <actionGroup ref="AssertProductImageNotInAdminProductPageActionGroup" stepKey="assertProductImageNotInSecondStoreViewPage"> <argument name="image" value="TestImageNew"/> </actionGroup> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageFromCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageFromCategoryTest.xml index fb33e18379982..ef276114b4de5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageFromCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRemoveImageFromCategoryTest.xml @@ -22,30 +22,30 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="DeleteCategory" stepKey="DeleteCategory"> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="DeleteCategory"> <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> </after> <!-- Go to create a new category with image --> - <actionGroup ref="goToCreateCategoryPage" stepKey="goToCreateCategoryPage"/> - <actionGroup ref="fillCategoryForm" stepKey="fillCategoryForm"> + <actionGroup ref="GoToCreateCategoryPageActionGroup" stepKey="goToCreateCategoryPage"/> + <actionGroup ref="FillCategoryFormActionGroup" stepKey="fillCategoryForm"> <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> - <actionGroup ref="addCategoryImage" stepKey="addCategoryImage"/> - <actionGroup ref="saveCategoryForm" stepKey="saveCategoryForm"/> - <actionGroup ref="checkCategoryImageInAdmin" stepKey="checkCategoryImageInAdmin"/> + <actionGroup ref="AddCategoryImageActionGroup" stepKey="addCategoryImage"/> + <actionGroup ref="SaveCategoryFormActionGroup" stepKey="saveCategoryForm"/> + <actionGroup ref="CheckCategoryImageInAdminActionGroup" stepKey="checkCategoryImageInAdmin"/> <!-- Remove image from category --> - <actionGroup ref="removeCategoryImage" stepKey="removeCategoryImage"/> - <actionGroup ref="saveCategoryForm" stepKey="saveCategoryFormAfterRemove"/> + <actionGroup ref="RemoveCategoryImageActionGroup" stepKey="removeCategoryImage"/> + <actionGroup ref="SaveCategoryFormActionGroup" stepKey="saveCategoryFormAfterRemove"/> - <actionGroup ref="CheckCategoryOnStorefront" stepKey="CheckCategoryOnStorefront"> + <actionGroup ref="CheckCategoryOnStorefrontActionGroup" stepKey="CheckCategoryOnStorefront"> <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> <!-- Verify category with no image in storefront --> <dontSee selector="{{StorefrontCategoryMainSection.categoryImage}}" stepKey="dontSeeImage"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml index 7b5455951fb27..05008b32ed4fb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminRestrictedUserAddCategoryFromProductPageTest.xml @@ -28,10 +28,10 @@ <after> <!--Delete created product--> <comment userInput="Delete created product" stepKey="commentDeleteProduct"/> - <actionGroup ref="deleteProductBySku" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> <argument name="sku" value="{{_defaultProduct.sku}}"/> </actionGroup> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetFiltersIfExist"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetFiltersIfExist"/> <actionGroup ref="logout" stepKey="logoutOfUser"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!--Delete created data--> @@ -51,16 +51,16 @@ </after> <!--Create user role--> <comment userInput="Create user role" stepKey="commentCreateUserRole"/> - <actionGroup ref="AdminFillUserRoleRequiredData" stepKey="fillUserRoleRequiredData"> + <actionGroup ref="AdminFillUserRoleRequiredDataActionGroup" stepKey="fillUserRoleRequiredData"> <argument name="User" value="adminRole"/> <argument name="restrictedRole" value="Stores"/> </actionGroup> <click selector="{{AdminEditRoleInfoSection.roleResourcesTab}}" stepKey="clickRoleResourcesTab" /> - <actionGroup ref="AdminAddRestrictedRole" stepKey="addRestrictedRoleStores"> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="addRestrictedRoleStores"> <argument name="User" value="adminRole"/> <argument name="restrictedRole" value="Stores"/> </actionGroup> - <actionGroup ref="AdminAddRestrictedRole" stepKey="addRestrictedRoleProducts"> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="addRestrictedRoleProducts"> <argument name="User" value="adminRole"/> <argument name="restrictedRole" value="Products"/> </actionGroup> @@ -80,15 +80,15 @@ </actionGroup> <!--Go to create product page--> <comment userInput="Go to create product page" stepKey="commentGoCreateProductPage"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"/> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProductPage"/> <dontSeeElement selector="{{AdminProductFormSection.newCategoryButton}}" stepKey="dontSeeNewCategoryButton"/> <!--Fill product data and assign to category--> <comment userInput="Fill product data and assign to category" stepKey="commentFillProductData"/> - <actionGroup ref="fillMainProductForm" stepKey="fillMainProductForm"/> - <actionGroup ref="SetCategoryByName" stepKey="addCategoryToProduct"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillMainProductForm"/> + <actionGroup ref="SetCategoryByNameActionGroup" stepKey="addCategoryToProduct"> <argument name="categoryName" value="$$createCategory.name$$"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!--Assert that category exist in field--> <comment userInput="Assert that category exist in field" stepKey="commentAssertion"/> <grabTextFrom selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="grabCategoryName"/> @@ -98,10 +98,10 @@ </assertContains> <!--Remove the category from the product and assert that it removed--> <comment userInput="Remove the category from the product and assert that it removed" stepKey="assertCategoryRemoved"/> - <actionGroup ref="removeCategoryFromProduct" stepKey="removeCategoryFromProduct"> + <actionGroup ref="RemoveCategoryFromProductActionGroup" stepKey="removeCategoryFromProduct"> <argument name="categoryName" value="$$createCategory.name$$"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProductAfterRemovingCategory"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductAfterRemovingCategory"/> <grabTextFrom selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="grabCategoryFieldContent"/> <assertNotContains stepKey="assertThatCategoryRemoved"> <expectedResult type="variable">$$createCategory.name$$</expectedResult> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminShouldBeAbleToAssociateSimpleProductToWebsitesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminShouldBeAbleToAssociateSimpleProductToWebsitesTest.xml index 061c30b224828..9d19a1dedf7ef 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminShouldBeAbleToAssociateSimpleProductToWebsitesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminShouldBeAbleToAssociateSimpleProductToWebsitesTest.xml @@ -43,13 +43,13 @@ </actionGroup> <actionGroup ref="AdminGridFilterResetActionGroup" stepKey="resetFiltersOnStoresIndexPage"/> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPageToResetFilters"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFiltersOnProductIndexPage"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFiltersOnProductIndexPage"/> <actionGroup ref="logout" stepKey="logout"/> </after> <!-- 1. Go to product page in admin panel to edit --> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPageToAssociateToSecondWebsite"/> - <actionGroup ref="filterProductGridByName2" stepKey="filterProductInGrid"> + <actionGroup ref="FilterProductGridByName2ActionGroup" stepKey="filterProductInGrid"> <argument name="name" value="$$createSimpleProduct.name$$"/> </actionGroup> @@ -59,10 +59,10 @@ <argument name="websiteToUnassign" value="_defaultWebsite"/> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> - <actionGroup ref="AssertProductIsAssignedToWebsite" stepKey="seeCustomWebsiteIsChecked"> + <actionGroup ref="AssertProductIsAssignedToWebsiteActionGroup" stepKey="seeCustomWebsiteIsChecked"> <argument name="website" value="$createCustomWebsite.website[name]$"/> </actionGroup> - <actionGroup ref="AssertProductIsNotAssignedToWebsite" stepKey="seeMainWebsiteIsNotChecked"> + <actionGroup ref="AssertProductIsNotAssignedToWebsiteActionGroup" stepKey="seeMainWebsiteIsNotChecked"> <argument name="website" value="{{_defaultWebsite.name}}"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml index df50edd20410a..ff8240655ca03 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductImagesTest.xml @@ -40,11 +40,11 @@ <!-- Go to the first product edit page --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> <waitForPageLoad stepKey="wait1"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGrid"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku"> <argument name="product" value="$$firstProduct$$"/> </actionGroup> - <actionGroup ref="openProducForEditByClickingRowXColumnYInProductGrid" stepKey="openProducForEditByClickingRow1Column2InProductGrid"/> + <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProducForEditByClickingRow1Column2InProductGrid"/> <!-- Set url key --> <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> @@ -126,11 +126,11 @@ <!-- Go to the second product edit page --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex2"/> <waitForPageLoad stepKey="wait2"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGrid2"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku2"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid2"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku2"> <argument name="product" value="$$secondProduct$$"/> </actionGroup> - <actionGroup ref="openProducForEditByClickingRowXColumnYInProductGrid" stepKey="openProducForEditByClickingRow1Column2InProductGrid2"/> + <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProducForEditByClickingRow1Column2InProductGrid2"/> <!-- Upload an image --> <click selector="{{AdminProductImagesSection.productImagesToggle}}" stepKey="expandImages2"/> @@ -150,8 +150,8 @@ <!-- Go to the admin grid and see the uploaded image --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex3"/> <waitForPageLoad stepKey="wait3"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGrid3"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku3"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid3"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku3"> <argument name="product" value="$$secondProduct$$"/> </actionGroup> <seeElement selector="img.admin__control-thumbnail[src*='/large']" stepKey="seeImgInGrid"/> @@ -194,11 +194,11 @@ <!-- Go to the product edit page --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> <waitForPageLoad stepKey="wait1"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGrid"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku"> <argument name="product" value="$$product$$"/> </actionGroup> - <actionGroup ref="openProducForEditByClickingRowXColumnYInProductGrid" stepKey="openProduct"/> + <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProduct"/> <!-- Set url key --> <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> @@ -259,8 +259,8 @@ <!-- Go to the admin grid and see the Thumbnail image --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex2"/> <waitForPageLoad stepKey="wait2"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGrid2"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku2"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid2"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku2"> <argument name="product" value="$$product$$"/> </actionGroup> <seeElement selector="{{AdminProductGridSection.productThumbnailBySrc('/adobe-thumb')}}" stepKey="seeBaseInGrid"/> @@ -268,11 +268,11 @@ <!-- Go to the product edit page again --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex3"/> <waitForPageLoad stepKey="wait5"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGrid3"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku3"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid3"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku3"> <argument name="product" value="$$product$$"/> </actionGroup> - <actionGroup ref="openProducForEditByClickingRowXColumnYInProductGrid" stepKey="openProduct3"/> + <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProduct3"/> <click selector="{{AdminProductImagesSection.productImagesToggle}}" stepKey="expandImages2"/> <!-- Remove all images --> @@ -284,8 +284,8 @@ <!-- Check admin grid for placeholder --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex4"/> <waitForPageLoad stepKey="wait6"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGrid4"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku4"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid4"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku4"> <argument name="product" value="$$product$$"/> </actionGroup> <dontSeeElement selector="{{AdminProductGridSection.productThumbnailBySrc('/adobe-thumb')}}" stepKey="dontSeeBaseInGrid"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml index f5e5911352c86..1e1fe4572b28d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleProductSetEditContentTest.xml @@ -22,11 +22,11 @@ <before> <!--Admin Login--> <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> </before> <after> <!-- Delete simple product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="SimpleProduct"/> </actionGroup> <!--Admin Logout--> @@ -36,10 +36,10 @@ <!-- Create product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPage"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="SimpleProduct"/> </actionGroup> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillProductForm"> <argument name="product" value="SimpleProduct"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml index 4803c1be6c936..0c0b8751a732e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSimpleSetEditRelatedProductsTest.xml @@ -30,7 +30,7 @@ </before> <after> <!-- Delete simple product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="SimpleProduct3"/> </actionGroup> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> @@ -46,15 +46,15 @@ <!--Create product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPage"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="SimpleProduct3"/> </actionGroup> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillProductForm"> <argument name="product" value="SimpleProduct3"/> </actionGroup> <!--Add related product--> - <actionGroup ref="addRelatedProductBySku" stepKey="addRelatedProduct0"> + <actionGroup ref="AddRelatedProductBySkuActionGroup" stepKey="addRelatedProduct0"> <argument name="sku" value="$$simpleProduct0.sku$$"/> </actionGroup> @@ -63,7 +63,7 @@ <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="messageYouSavedTheProductIsShown"/> <!--Add another related product--> - <actionGroup ref="addRelatedProductBySku" stepKey="addRelatedProduct1"> + <actionGroup ref="AddRelatedProductBySkuActionGroup" stepKey="addRelatedProduct1"> <argument name="sku" value="$$simpleProduct1.sku$$"/> </actionGroup> @@ -80,19 +80,19 @@ <see selector="{{AdminProductFormRelatedUpSellCrossSellSection.selectedRelatedProduct}}" userInput="$$simpleProduct1.sku$$" stepKey="seeRelatedProduct"/> <!--See more related products in admin--> - <actionGroup ref="addRelatedProductBySku" stepKey="addRelatedProduct2"> + <actionGroup ref="AddRelatedProductBySkuActionGroup" stepKey="addRelatedProduct2"> <argument name="sku" value="$$simpleProduct2.sku$$"/> </actionGroup> - <actionGroup ref="addRelatedProductBySku" stepKey="addRelatedProduct3"> + <actionGroup ref="AddRelatedProductBySkuActionGroup" stepKey="addRelatedProduct3"> <argument name="sku" value="$$simpleProduct3.sku$$"/> </actionGroup> - <actionGroup ref="addRelatedProductBySku" stepKey="addRelatedProduct4"> + <actionGroup ref="AddRelatedProductBySkuActionGroup" stepKey="addRelatedProduct4"> <argument name="sku" value="$$simpleProduct4.sku$$"/> </actionGroup> - <actionGroup ref="addRelatedProductBySku" stepKey="addRelatedProduct5"> + <actionGroup ref="AddRelatedProductBySkuActionGroup" stepKey="addRelatedProduct5"> <argument name="sku" value="$$simpleProduct5.sku$$"/> </actionGroup> - <actionGroup ref="addRelatedProductBySku" stepKey="addRelatedProduct6"> + <actionGroup ref="AddRelatedProductBySkuActionGroup" stepKey="addRelatedProduct6"> <argument name="sku" value="$$simpleProduct6.sku$$"/> </actionGroup> <scrollTo selector="{{AdminProductFormRelatedUpSellCrossSellSection.relatedDropdown}}" stepKey="scrollTo2"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml index 5b6207e135796..b20a024a7c586 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminSortingByWebsitesTest.xml @@ -30,7 +30,7 @@ <argument name="newWebsiteName" value="{{customWebsite.name}}"/> <argument name="websiteCode" value="{{customWebsite.code}}"/> </actionGroup> - <actionGroup ref="EnableWebUrlOptions" stepKey="addStoreCodeToUrls"/> + <actionGroup ref="EnableWebUrlOptionsActionGroup" stepKey="addStoreCodeToUrls"/> <magentoCLI command="cache:flush" stepKey="flushCacheAfterEnableWebUrlOptions"/> </before> <after> @@ -40,9 +40,9 @@ <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteTestWebsite"> <argument name="websiteName" value="{{customWebsite.name}}"/> </actionGroup> - <actionGroup ref="GoToProductCatalogPage" stepKey="goToProductCatalogPage"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> - <actionGroup ref="ResetWebUrlOptions" stepKey="resetUrlOption"/> + <actionGroup ref="GoToProductCatalogPageActionGroup" stepKey="goToProductCatalogPage"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="ResetWebUrlOptionsActionGroup" stepKey="resetUrlOption"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> <actionGroup ref="logout" stepKey="logout"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml index bcc8636c65b1e..1c9cdad681d98 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml @@ -40,7 +40,7 @@ <!-- Assert attribute presence in storefront product additional information --> <amOnPage url="/$$product.custom_attributes[url_key]$$.html" stepKey="onProductPage1"/> <waitForPageLoad stepKey="wait1"/> - <actionGroup ref="checkAttributeInMoreInformationTab" stepKey="checkAttributeInMoreInformationTab"> + <actionGroup ref="CheckAttributeInMoreInformationTabActionGroup" stepKey="checkAttributeInMoreInformationTab"> <argument name="attributeLabel" value="$$attribute.attribute[frontend_labels][0][label]$$"/> <argument name="attributeValue" value="$$option2.option[store_labels][0][label]$$"/> </actionGroup> @@ -49,14 +49,14 @@ <!-- Assert created attribute in a group --> <see userInput="$$attribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.groupTree}}" stepKey="seeAttributeInGroup"/> <!-- Unassign attribute from group --> - <actionGroup ref="UnassignAttributeFromGroup" stepKey="UnassignAttributeFromGroup"> + <actionGroup ref="UnassignAttributeFromGroupActionGroup" stepKey="UnassignAttributeFromGroup"> <argument name="group" value="Product Details"/> <argument name="attribute" value="$$attribute.attribute_code$$"/> </actionGroup> <!-- Assert attribute in unassigned section --> <see userInput="$$attribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.unassignedAttributesTree}}" stepKey="seeAttributeInUnassigned"/> <!-- Save attribute set --> - <actionGroup ref="SaveAttributeSet" stepKey="SaveAttributeSet"/> + <actionGroup ref="SaveAttributeSetActionGroup" stepKey="SaveAttributeSet"/> <!-- Clear cache --> <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCacheActionGroup"/> <!-- Go to create new product page --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml index 0f63a72844452..01eba2976c3d6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryStoreUrlKeyTest.xml @@ -19,7 +19,7 @@ <group value="category"/> </annotations> <after> - <actionGroup ref="DeleteCategory" stepKey="deleteCategory"> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"> <argument name="categoryEntity" value="_defaultCategory"/> </actionGroup> <actionGroup ref="logout" stepKey="adminLogout"/> @@ -29,12 +29,12 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage"/> <waitForPageLoad stepKey="waitForPageLoad1"/> - <actionGroup ref="CreateCategory" stepKey="createCategory"> + <actionGroup ref="CreateCategoryActionGroup" stepKey="createCategory"> <argument name="categoryEntity" value="_defaultCategory"/> </actionGroup> <!--Switch to "Default Store View" scope--> - <actionGroup ref="switchCategoryStoreView" stepKey="SwitchStoreView"> + <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="SwitchStoreView"> <argument name="Store" value="_defaultStore.name"/> <argument name="CatName" value="_defaultCategory.name"/> </actionGroup> @@ -58,7 +58,7 @@ <!-- Update SEO key to original, uncheck "Create Redirect", confirm in frontend, delete category --> <!--Switch to "Default Store View" scope--> - <actionGroup ref="switchCategoryStoreView" stepKey="SwitchStoreView2"> + <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="SwitchStoreView2"> <argument name="Store" value="_defaultStore.name"/> <argument name="CatName" value="_defaultCategory.name"/> </actionGroup> @@ -74,4 +74,4 @@ <seeInTitle userInput="{{_defaultCategory.name}}" stepKey="seeCategoryNameInTitle2"/> <seeInCurrentUrl stepKey="verifyUrlKeyAfterOriginalSeoKey" url="{{_defaultCategory.name_lwr}}.html"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryAndAddProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryAndAddProductsTest.xml index 8872ea98eb504..cebf67ae2ebcf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryAndAddProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryAndAddProductsTest.xml @@ -24,11 +24,11 @@ <!-- Create category --> <createData entity="defaultSimpleProduct" stepKey="createSimpleProduct"/> <!-- Create First StoreView --> - <actionGroup ref="CreateStoreView" stepKey="createCustomStoreViewEn"> + <actionGroup ref="CreateStoreViewActionGroup" stepKey="createCustomStoreViewEn"> <argument name="storeView" value="customStoreEN"/> </actionGroup> <!-- Create Second StoreView --> - <actionGroup ref="CreateStoreView" stepKey="createCustomStoreViewFr"> + <actionGroup ref="CreateStoreViewActionGroup" stepKey="createCustomStoreViewFr"> <argument name="storeView" value="customStoreFR"/> </actionGroup> <!--Run full reindex and clear caches --> @@ -100,4 +100,4 @@ <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName($$category.name$$)}}" stepKey="seeCategoryOnNavigation1"/> <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{defaultSimpleProduct.name}}" stepKey="seeProductName"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryIncludeInNavigationTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryIncludeInNavigationTest.xml index 5527303370623..d7ce22bdc0097 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryIncludeInNavigationTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryIncludeInNavigationTest.xml @@ -22,11 +22,11 @@ <!--Create category--> <createData entity="CatNotIncludeInMenu" stepKey="createCategory"/> <!-- Create First StoreView --> - <actionGroup ref="CreateStoreView" stepKey="createCustomStoreViewEn"> + <actionGroup ref="CreateStoreViewActionGroup" stepKey="createCustomStoreViewEn"> <argument name="storeView" value="customStoreEN"/> </actionGroup> <!-- Create Second StoreView --> - <actionGroup ref="CreateStoreView" stepKey="createCustomStoreViewFr"> + <actionGroup ref="CreateStoreViewActionGroup" stepKey="createCustomStoreViewFr"> <argument name="storeView" value="customStoreFR"/> </actionGroup> <!--Run full reindex and clear caches --> @@ -88,4 +88,4 @@ <waitForPageLoad stepKey="waitForSecondstoreView"/> <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="seeCategoryOnNavigation1"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryNameAndDescriptionTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryNameAndDescriptionTest.xml index fcbc0cb205268..2b14973d6ce32 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryNameAndDescriptionTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateFlatCategoryNameAndDescriptionTest.xml @@ -23,11 +23,11 @@ <!--Create category--> <createData entity="_defaultCategory" stepKey="createCategory"/> <!-- Create First StoreView --> - <actionGroup ref="CreateStoreView" stepKey="createCustomStoreViewEn"> + <actionGroup ref="CreateStoreViewActionGroup" stepKey="createCustomStoreViewEn"> <argument name="storeView" value="customStoreEN"/> </actionGroup> <!-- Create Second StoreView --> - <actionGroup ref="CreateStoreView" stepKey="createCustomStoreViewFr"> + <actionGroup ref="CreateStoreViewActionGroup" stepKey="createCustomStoreViewFr"> <argument name="storeView" value="customStoreFR"/> </actionGroup> <!--Run full reindex and clear caches --> @@ -99,4 +99,4 @@ <click selector="{{AdminCategoryContentSection.sectionHeader}}" stepKey="selectContent1"/> <seeInField selector="{{AdminCategoryContentSection.description}}" userInput="Updated category Description Fields" stepKey="seeUpdatedDescription"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml index 80f0c8ad10ede..60e7f03824ab7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="CreateStoreView" stepKey="createCustomStoreViewFr"> + <actionGroup ref="CreateStoreViewActionGroup" stepKey="createCustomStoreViewFr"> <argument name="storeView" value="customStoreFR"/> </actionGroup> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> @@ -32,7 +32,7 @@ <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> - <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{defaultSimpleProduct.sku}}"/> </actionGroup> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> @@ -44,7 +44,7 @@ <!-- Search default simple product in grid --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGrid"> <argument name="sku" value="$$initialSimpleProduct.sku$$"/> </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml index f698b3d89ffe9..7624ad0557b47 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="CreateStoreView" stepKey="createCustomStoreViewFr"> + <actionGroup ref="CreateStoreViewActionGroup" stepKey="createCustomStoreViewFr"> <argument name="storeView" value="customStoreFR"/> </actionGroup> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> @@ -32,7 +32,7 @@ <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> - <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{defaultSimpleProduct.sku}}"/> </actionGroup> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> @@ -44,7 +44,7 @@ <!-- Search default simple product in grid --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGrid"> <argument name="sku" value="$$initialSimpleProduct.sku$$"/> </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml index f317c66e5366a..a848eb3d11e61 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml @@ -25,11 +25,15 @@ <requiredEntity createDataKey="initialCategoryEntity"/> </createData> <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> + + <!--TODO: REMOVE AFTER FIX MC-21717 --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush full_page" stepKey="flushCache"/> </before> <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> - <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{simpleProductTierPrice300InStock.sku}}"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> @@ -38,7 +42,7 @@ <!-- Search default simple product in the grid --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGrid"> <argument name="sku" value="$$initialSimpleProduct.sku$$"/> </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml index 6e8f1ba6f12a6..ff3f56b566b38 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml @@ -27,7 +27,7 @@ </before> <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> - <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{simpleProductDisabled.sku}}"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> @@ -36,7 +36,7 @@ <!-- Search default simple product in the grid page --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGrid"> <argument name="sku" value="$$initialSimpleProduct.sku$$"/> </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.xml index afb8b40a6dbd4..09ddc18aff4bd 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.xml @@ -30,7 +30,7 @@ <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> - <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{simpleProductEnabledFlat.sku}}"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> @@ -40,7 +40,7 @@ <!-- Search default simple product in the grid page --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGrid"> <argument name="sku" value="$$initialSimpleProduct.sku$$"/> </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml index 2436fc0fc7f12..94aec4cc95d1d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml @@ -29,7 +29,7 @@ <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> - <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{simpleProductNotVisibleIndividually.sku}}"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> @@ -38,7 +38,7 @@ <!-- Search default simple product in the grid page --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGrid"> <argument name="sku" value="$$initialSimpleProduct.sku$$"/> </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockUnassignFromCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockUnassignFromCategoryTest.xml index 3433a09117322..fa3317aa815d9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockUnassignFromCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockUnassignFromCategoryTest.xml @@ -27,7 +27,7 @@ </before> <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> - <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="$$initialSimpleProduct.sku$$"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> @@ -36,7 +36,7 @@ <!--Search default simple product in the grid page --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGrid"> <argument name="sku" value="$$initialSimpleProduct.sku$$"/> </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml index 82395e5d6e0eb..95b74f4d38b3f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml @@ -29,7 +29,7 @@ <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> - <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{simpleProductRegularPrice245InStock.sku}}"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> @@ -38,7 +38,7 @@ <!-- Search default simple product in the grid page --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGrid"> <argument name="sku" value="$$initialSimpleProduct.sku$$"/> </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml index 4817b3497c97e..af190890ac351 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml @@ -29,7 +29,7 @@ <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> - <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{simpleProductRegularPrice32501InStock.sku}}"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> @@ -38,7 +38,7 @@ <!-- Search default simple product in the grid --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGrid"> <argument name="sku" value="$$initialSimpleProduct.sku$$"/> </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml index 214f9b0273b6a..626c3d00a5caf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml @@ -29,7 +29,7 @@ <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> - <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{simpleProductRegularPrice325InStock.sku}}"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> @@ -38,7 +38,7 @@ <!-- Search default simple product in the grid --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGrid"> <argument name="sku" value="$$initialSimpleProduct.sku$$"/> </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml index b145328890a91..13782da076f69 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml @@ -29,7 +29,7 @@ <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> - <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{simpleProductRegularPriceCustomOptions.sku}}"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> @@ -38,7 +38,7 @@ <!-- Search default simple product in the grid --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGrid"> <argument name="sku" value="$$initialSimpleProduct.sku$$"/> </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml index 27c7e77a94ad1..285ceb3c4d722 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml @@ -29,7 +29,7 @@ <after> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> - <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProduct"> <argument name="sku" value="{{simpleProductRegularPrice32503OutOfStock.sku}}"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> @@ -38,7 +38,7 @@ <!-- Search default simple product in the grid --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGrid"> <argument name="sku" value="$$initialSimpleProduct.sku$$"/> </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockVisibleInCategoryOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockVisibleInCategoryOnlyTest.xml index 9fa0e155a4fe7..0e9b9431dcfa6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockVisibleInCategoryOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockVisibleInCategoryOnlyTest.xml @@ -28,7 +28,7 @@ <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> </before> <after> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteVirtualProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteVirtualProduct"> <argument name="product" value="updateVirtualProductRegularPrice"/> </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml index a2a4f65860254..021ed5593c738 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryAndSearchTest.xml @@ -28,6 +28,9 @@ <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> </before> <after> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteDefaultVirtualProduct"> + <argument name="sku" value="{{updateVirtualProductRegularPrice5OutOfStock.sku}}"/> + </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> <actionGroup ref="logout" stepKey="logout"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml index ffbad0752b73c..d74a6ce508b88 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml @@ -28,6 +28,9 @@ <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> </before> <after> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteDefaultVirtualProduct"> + <argument name="sku" value="{{updateVirtualProductRegularPrice5OutOfStock.sku}}"/> + </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> <actionGroup ref="logout" stepKey="logout"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInSearchOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInSearchOnlyTest.xml index aa3184994daff..aa90d018f7710 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInSearchOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInSearchOnlyTest.xml @@ -27,6 +27,9 @@ </createData> </before> <after> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteDefaultVirtualProduct"> + <argument name="sku" value="{{updateVirtualProductRegularPrice99OutOfStock.sku}}"/> + </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <actionGroup ref="logout" stepKey="logout"/> </after> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml index 3101c1e460322..94ddb3dc5e5da 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml @@ -28,6 +28,9 @@ <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> </before> <after> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteDefaultVirtualProduct"> + <argument name="sku" value="{{updateVirtualProductSpecialPrice.sku}}"/> + </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> <actionGroup ref="logout" stepKey="logout"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml index 58978c31b5b40..a6a629b35091e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml @@ -28,6 +28,9 @@ <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> </before> <after> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteDefaultVirtualProduct"> + <argument name="sku" value="{{updateVirtualProductSpecialPriceOutOfStock.sku}}"/> + </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> <actionGroup ref="logout" stepKey="logout"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml index e0e8360850983..10347584b4cda 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml @@ -28,7 +28,7 @@ <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> </before> <after> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteVirtualProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteVirtualProduct"> <argument name="product" value="updateVirtualProductTierPriceInStock"/> </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryOnlyTest.xml index 677cc4c65ce88..0edc487e71edf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryOnlyTest.xml @@ -28,7 +28,7 @@ <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> </before> <after> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteVirtualProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteVirtualProduct"> <argument name="product" value="updateVirtualProductWithTierPriceInStock"/> </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceOutOfStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceOutOfStockVisibleInCategoryAndSearchTest.xml index f0148f3d384c1..e513d007e6a09 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceOutOfStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceOutOfStockVisibleInCategoryAndSearchTest.xml @@ -28,7 +28,7 @@ <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> </before> <after> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteVirtualProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteVirtualProduct"> <argument name="product" value="updateVirtualTierPriceOutOfStock"/> </actionGroup> <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminVerifyProductOrderTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminVerifyProductOrderTest.xml index a81c26b6e6eaf..b61be7fd95a58 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminVerifyProductOrderTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminVerifyProductOrderTest.xml @@ -25,7 +25,7 @@ <after> <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="GoToProductCatalogPage" stepKey="goToProductCatalogPage"/> + <actionGroup ref="GoToProductCatalogPageActionGroup" stepKey="goToProductCatalogPage"/> <actionGroup ref="VerifyProductTypeOrder" stepKey="verifyProductTypeOrder"/> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualProductSetEditContentTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualProductSetEditContentTest.xml index c9932de808006..1e7b7cc14f9cc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualProductSetEditContentTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualProductSetEditContentTest.xml @@ -21,16 +21,16 @@ </annotations> <after> <!-- Delete virtual product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="defaultVirtualProduct"/> </actionGroup> </after> <!-- Create product --> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="defaultVirtualProduct"/> </actionGroup> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillProductForm"> <argument name="product" value="defaultVirtualProduct"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualSetEditRelatedProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualSetEditRelatedProductsTest.xml index e630545fff8fb..f70f5757ec5c2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualSetEditRelatedProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminVirtualSetEditRelatedProductsTest.xml @@ -17,20 +17,23 @@ <severity value="CRITICAL"/> <testCaseId value="MC-3415"/> <group value="Catalog"/> + <skip> + <issueId value="MC-194"/> + </skip> </annotations> <before></before> <after> <!-- Delete virtual product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="defaultVirtualProduct"/> </actionGroup> </after> <!-- Create product --> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="defaultVirtualProduct"/> </actionGroup> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillProductForm"> <argument name="product" value="defaultVirtualProduct"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml index 867f097042a17..3125ba3decce6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdvanceCatalogSearchSimpleProductTest.xml @@ -19,10 +19,13 @@ <group value="Catalog"/> </annotations> <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> <createData entity="ApiProductWithDescription" stepKey="product"/> </before> <after> <deleteData createDataKey="product" stepKey="delete"/> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> </test> <test name="AdvanceCatalogSearchSimpleProductBySkuTest"> @@ -87,6 +90,9 @@ <group value="Catalog"/> </annotations> <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> + <createData entity="ApiProductWithDescription" stepKey="product"/> <getData entity="GetProduct" stepKey="arg1"> <requiredEntity createDataKey="product"/> @@ -100,6 +106,7 @@ </before> <after> <deleteData createDataKey="product" stepKey="delete"/> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml index cee40241185b4..4e0e8d03f59d5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml @@ -61,10 +61,10 @@ <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct1"> <argument name="product" value="$$product1$$"/> </actionGroup> - <actionGroup ref="ProductSetWebsite" stepKey="ProductSetWebsite"> + <actionGroup ref="ProductSetWebsiteActionGroup" stepKey="ProductSetWebsite"> <argument name="website" value="secondWebsite"/> </actionGroup> - <actionGroup ref="ProductSetAdvancedPricing" stepKey="ProductSetAdvancedPricing1"> + <actionGroup ref="ProductSetAdvancedPricingActionGroup" stepKey="ProductSetAdvancedPricing1"> <argument name="website" value="secondWebsite"/> </actionGroup> @@ -74,10 +74,10 @@ <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct2"> <argument name="product" value="$$product2$$"/> </actionGroup> - <actionGroup ref="ProductSetWebsite" stepKey="ProductSetWebsite2"> + <actionGroup ref="ProductSetWebsiteActionGroup" stepKey="ProductSetWebsite2"> <argument name="website" value="secondWebsite"/> </actionGroup> - <actionGroup ref="ProductSetAdvancedPricing" stepKey="ProductSetAdvancedPricing2"> + <actionGroup ref="ProductSetAdvancedPricingActionGroup" stepKey="ProductSetAdvancedPricing2"> <argument name="website" value="secondWebsite"/> </actionGroup> @@ -87,10 +87,10 @@ <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct3"> <argument name="product" value="$$product3$$"/> </actionGroup> - <actionGroup ref="ProductSetWebsite" stepKey="ProductSetWebsite3"> + <actionGroup ref="ProductSetWebsiteActionGroup" stepKey="ProductSetWebsite3"> <argument name="website" value="secondWebsite"/> </actionGroup> - <actionGroup ref="ProductSetAdvancedPricing" stepKey="ProductSetAdvancedPricing3"> + <actionGroup ref="ProductSetAdvancedPricingActionGroup" stepKey="ProductSetAdvancedPricing3"> <argument name="website" value="secondWebsite"/> </actionGroup> @@ -100,10 +100,10 @@ <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProduct4"> <argument name="product" value="$$product4$$"/> </actionGroup> - <actionGroup ref="ProductSetWebsite" stepKey="ProductSetWebsite4"> + <actionGroup ref="ProductSetWebsiteActionGroup" stepKey="ProductSetWebsite4"> <argument name="website" value="secondWebsite"/> </actionGroup> - <actionGroup ref="ProductSetAdvancedPricing" stepKey="ProductSetAdvancedPricing4"> + <actionGroup ref="ProductSetAdvancedPricingActionGroup" stepKey="ProductSetAdvancedPricing4"> <argument name="website" value="secondWebsite"/> </actionGroup> <actionGroup ref="ClearProductsFilterActionGroup" stepKey="ClearProductsFilterActionGroup"/> @@ -149,7 +149,7 @@ <see userInput="You saved the rule." stepKey="RuleSaved"/> <!--Create new order--> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="CreateNewOrder"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="CreateNewOrder"> <argument name="customer" value="Simple_US_Customer"/> <argument name="storeView" value="customStoreView"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml b/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml index 899f3e61b5b86..a4785e25d39bb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml @@ -27,7 +27,7 @@ </after> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <actionGroup ref="AdminCreateSimpleProductWithTextOptionCharLimit" stepKey="fillProductFieldsInAdmin"> + <actionGroup ref="AdminCreateSimpleProductWithTextOptionCharLimitActionGroup" stepKey="fillProductFieldsInAdmin"> <argument name="category" value="$$createPreReqCategory$$"/> <argument name="simpleProduct" value="_defaultProduct"/> <argument name="charLimit" value="20"/> @@ -36,10 +36,10 @@ <argument name="category" value="$$createPreReqCategory$$"/> <argument name="product" value="_defaultProduct"/> </actionGroup> - <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="assertProductInStorefront2"> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="assertProductInStorefront2"> <argument name="product" value="_defaultProduct"/> </actionGroup> - <actionGroup ref="testDynamicValidationHint" stepKey="testDynamicValidationHint1"> + <actionGroup ref="TestDynamicValidationHintActionGroup" stepKey="testDynamicValidationHint1"> <argument name="charLimit" value="20"/> </actionGroup> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml index d895993217e32..1e297586ecb65 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml @@ -24,7 +24,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPage"> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> <argument name="ProductAttribute" value="{{textProductAttribute.attribute_code}}"/> </actionGroup> <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> @@ -37,12 +37,12 @@ <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> <!--Create new Product Attribute as TextField, with code and default value.--> - <actionGroup ref="createProductAttributeWithTextField" stepKey="createAttribute"> + <actionGroup ref="CreateProductAttributeWithTextFieldActionGroup" stepKey="createAttribute"> <argument name="attribute" value="textProductAttribute"/> </actionGroup> <!--Navigate to Product Attribute.--> - <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPage"> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> <argument name="ProductAttribute" value="{{textProductAttribute.attribute_code}}"/> </actionGroup> @@ -55,7 +55,7 @@ <!--Go to New Product page, add Attribute and check values--> <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> - <actionGroup ref="addProductAttributeInProductModal" stepKey="addAttributeToProduct"> + <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> <argument name="attributeCode" value="{{textProductAttribute.attribute_code}}"/> </actionGroup> <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> @@ -83,7 +83,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPage"> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> <argument name="ProductAttribute" value="{{dateProductAttribute.attribute_code}}"/> </actionGroup> <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> @@ -99,13 +99,13 @@ <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> <!--Create new Product Attribute as TextField, with code and default value.--> - <actionGroup ref="createProductAttributeWithDateField" stepKey="createAttribute"> + <actionGroup ref="CreateProductAttributeWithDateFieldActionGroup" stepKey="createAttribute"> <argument name="attribute" value="dateProductAttribute"/> <argument name="date" value="{$generateDefaultDate}"/> </actionGroup> <!--Navigate to Product Attribute.--> - <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPage"> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> <argument name="ProductAttribute" value="{{dateProductAttribute.attribute_code}}"/> </actionGroup> @@ -118,7 +118,7 @@ <!--Go to New Product page, add Attribute and check values--> <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> - <actionGroup ref="addProductAttributeInProductModal" stepKey="addAttributeToProduct"> + <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> <argument name="attributeCode" value="{{dateProductAttribute.attribute_code}}"/> </actionGroup> <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> @@ -143,7 +143,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPage"> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> <argument name="ProductAttribute" value="{{priceProductAttribute.attribute_code}}"/> </actionGroup> <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> @@ -156,12 +156,12 @@ <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> <!--Create new Product Attribute with Price--> - <actionGroup ref="createProductAttribute" stepKey="createAttribute"> + <actionGroup ref="CreateProductAttributeActionGroup" stepKey="createAttribute"> <argument name="attribute" value="priceProductAttribute"/> </actionGroup> <!--Navigate to Product Attribute.--> - <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPage"> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> <argument name="ProductAttribute" value="{{priceProductAttribute.attribute_code}}"/> </actionGroup> @@ -173,7 +173,7 @@ <!--Go to New Product page, add Attribute and check values--> <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> - <actionGroup ref="addProductAttributeInProductModal" stepKey="addAttributeToProduct"> + <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> <argument name="attributeCode" value="{{priceProductAttribute.attribute_code}}"/> </actionGroup> <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> @@ -197,7 +197,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPage"> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> <argument name="ProductAttribute" value="{{dropdownProductAttribute.attribute_code}}"/> </actionGroup> <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> @@ -210,25 +210,25 @@ <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> <!--Create new Product Attribute as TextField, with code and default value.--> - <actionGroup ref="createProductAttribute" stepKey="createAttribute"> + <actionGroup ref="CreateProductAttributeActionGroup" stepKey="createAttribute"> <argument name="attribute" value="dropdownProductAttribute"/> </actionGroup> <!--Navigate to Product Attribute, add Product Options and Save - 1--> - <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPage1"> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage1"> <argument name="ProductAttribute" value="{{dropdownProductAttribute.attribute_code}}"/> </actionGroup> - <actionGroup ref="createAttributeDropdownNthOption" stepKey="createOption1"> + <actionGroup ref="CreateAttributeDropdownNthOptionActionGroup" stepKey="createOption1"> <argument name="adminName" value="{{dropdownProductAttribute.option1_admin}}"/> <argument name="frontName" value="{{dropdownProductAttribute.option1_frontend}}"/> <argument name="row" value="1"/> </actionGroup> - <actionGroup ref="createAttributeDropdownNthOption" stepKey="createOption2"> + <actionGroup ref="CreateAttributeDropdownNthOptionActionGroup" stepKey="createOption2"> <argument name="adminName" value="{{dropdownProductAttribute.option2_admin}}"/> <argument name="frontName" value="{{dropdownProductAttribute.option2_frontend}}"/> <argument name="row" value="2"/> </actionGroup> - <actionGroup ref="createAttributeDropdownNthOptionAsDefault" stepKey="createOption3"> + <actionGroup ref="CreateAttributeDropdownNthOptionAsDefaultActionGroup" stepKey="createOption3"> <argument name="adminName" value="{{dropdownProductAttribute.option3_admin}}"/> <argument name="frontName" value="{{dropdownProductAttribute.option3_frontend}}"/> <argument name="row" value="3"/> @@ -236,7 +236,7 @@ <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> <!--Perform appropriate assertions against dropdownProductAttribute entity--> - <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPageForAssertions"> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPageForAssertions"> <argument name="ProductAttribute" value="{{dropdownProductAttribute.attribute_code}}"/> </actionGroup> <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{dropdownProductAttribute.attribute_code}}"/> @@ -257,7 +257,7 @@ <!--Go to New Product page, add Attribute and check dropdown values--> <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> - <actionGroup ref="addProductAttributeInProductModal" stepKey="addAttributeToProduct"> + <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> <argument name="attributeCode" value="{{dropdownProductAttribute.attribute_code}}"/> </actionGroup> <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> @@ -284,7 +284,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPage"> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> <argument name="ProductAttribute" value="{{dropdownProductAttributeWithQuote.attribute_code}}"/> </actionGroup> <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> @@ -297,15 +297,15 @@ <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> <!--Create new Product Attribute as TextField, with code and default value.--> - <actionGroup ref="createProductAttribute" stepKey="createAttribute"> + <actionGroup ref="CreateProductAttributeActionGroup" stepKey="createAttribute"> <argument name="attribute" value="dropdownProductAttributeWithQuote"/> </actionGroup> <!--Navigate to Product Attribute, add Product Option and Save - 1--> - <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPage1"> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage1"> <argument name="ProductAttribute" value="{{dropdownProductAttributeWithQuote.attribute_code}}"/> </actionGroup> - <actionGroup ref="createAttributeDropdownNthOptionAsDefault" stepKey="createOption1"> + <actionGroup ref="CreateAttributeDropdownNthOptionAsDefaultActionGroup" stepKey="createOption1"> <argument name="adminName" value="{{dropdownProductAttributeWithQuote.option1_admin}}"/> <argument name="frontName" value="{{dropdownProductAttributeWithQuote.option1_frontend}}"/> <argument name="row" value="1"/> @@ -313,7 +313,7 @@ <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> <!--Perform appropriate assertions against dropdownProductAttribute entity--> - <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPageForAssertions"> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPageForAssertions"> <argument name="ProductAttribute" value="{{dropdownProductAttributeWithQuote.attribute_code}}"/> </actionGroup> <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{dropdownProductAttributeWithQuote.attribute_code}}"/> @@ -328,7 +328,7 @@ <!--Go to New Product page, add Attribute and check dropdown values--> <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> - <actionGroup ref="addProductAttributeInProductModal" stepKey="addAttributeToProduct"> + <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> <argument name="attributeCode" value="{{dropdownProductAttributeWithQuote.attribute_code}}"/> </actionGroup> <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> @@ -352,7 +352,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPage"> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage"> <argument name="ProductAttribute" value="{{multiselectProductAttribute.attribute_code}}"/> </actionGroup> <click stepKey="clickDelete" selector="{{AttributePropertiesSection.DeleteAttribute}}"/> @@ -365,25 +365,25 @@ <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> <!--Create new Product Attribute as TextField, with code and default value.--> - <actionGroup ref="createProductAttribute" stepKey="createAttribute"> + <actionGroup ref="CreateProductAttributeActionGroup" stepKey="createAttribute"> <argument name="attribute" value="multiselectProductAttribute"/> </actionGroup> <!--Navigate to Product Attribute, add Product Options and Save - 1--> - <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPage1"> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPage1"> <argument name="ProductAttribute" value="{{multiselectProductAttribute.attribute_code}}"/> </actionGroup> - <actionGroup ref="createAttributeDropdownNthOption" stepKey="createOption1"> + <actionGroup ref="CreateAttributeDropdownNthOptionActionGroup" stepKey="createOption1"> <argument name="adminName" value="{{multiselectProductAttribute.option1_admin}}"/> <argument name="frontName" value="{{multiselectProductAttribute.option1_frontend}}"/> <argument name="row" value="1"/> </actionGroup> - <actionGroup ref="createAttributeDropdownNthOption" stepKey="createOption2"> + <actionGroup ref="CreateAttributeDropdownNthOptionActionGroup" stepKey="createOption2"> <argument name="adminName" value="{{multiselectProductAttribute.option2_admin}}"/> <argument name="frontName" value="{{multiselectProductAttribute.option2_frontend}}"/> <argument name="row" value="2"/> </actionGroup> - <actionGroup ref="createAttributeDropdownNthOptionAsDefault" stepKey="createOption3"> + <actionGroup ref="CreateAttributeDropdownNthOptionAsDefaultActionGroup" stepKey="createOption3"> <argument name="adminName" value="{{multiselectProductAttribute.option3_admin}}"/> <argument name="frontName" value="{{multiselectProductAttribute.option3_frontend}}"/> <argument name="row" value="3"/> @@ -391,7 +391,7 @@ <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> <!--Perform appropriate assertions against multiselectProductAttribute entity--> - <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPageForAssertions"> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="goToEditPageForAssertions"> <argument name="ProductAttribute" value="{{multiselectProductAttribute.attribute_code}}"/> </actionGroup> <seeInField stepKey="assertLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{multiselectProductAttribute.attribute_code}}"/> @@ -412,7 +412,7 @@ <!--Go to New Product page, add Attribute and check multiselect values--> <amOnPage url="{{AdminProductCreatePage.url('4', 'simple')}}" stepKey="goToCreateSimpleProductPage"/> - <actionGroup ref="addProductAttributeInProductModal" stepKey="addAttributeToProduct"> + <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> <argument name="attributeCode" value="{{multiselectProductAttribute.attribute_code}}"/> </actionGroup> <click stepKey="openAttributes" selector="{{AdminProductAttributesSection.sectionHeader}}"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml index 674d46b9c18b1..8609d50fecaf2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteCategoriesTest.xml @@ -98,7 +98,7 @@ <!--<argument name="category" value="$$createCategoryC$$"/>--> <!--<argument name="productCount" value="2"/>--> <!--</actionGroup>--> - <!--<actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="browseAssertCategoryProduct1">--> + <!--<actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct1">--> <!--<argument name="product" value="$$createProduct1$$"/>--> <!--</actionGroup>--> @@ -109,7 +109,7 @@ <argument name="category" value="$$createSubCategory$$"/> <argument name="productCount" value="1"/> </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="browseAssertCategoryProduct2"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct2"> <argument name="product" value="$$createProduct2$$"/> </actionGroup> @@ -118,17 +118,17 @@ <!--<argument name="category" value="$$createCategoryB$$"/>--> <!--<argument name="productCount" value="1"/>--> <!--</actionGroup>--> - <!--<actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="browseAssertCategoryProduct3">--> + <!--<actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct3">--> <!--<argument name="product" value="$$createProduct3$$"/>--> <!--</actionGroup>--> <!-- Delete Categories 1(with subcategory) and 3. --> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPageAfterStoreFrontCategoryAssertions"/> <waitForPageLoad time="30" stepKey="waitForCategoryPageLoadAfterStoreFrontCategoryAssertions"/> - <actionGroup ref="DeleteCategory" stepKey="deleteCategoryC"> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategoryC"> <argument name="categoryEntity" value="$$createCategoryC$$"/> </actionGroup> - <actionGroup ref="DeleteCategory" stepKey="deleteCategoryB"> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategoryB"> <argument name="categoryEntity" value="$$createCategoryB$$"/> </actionGroup> <!-- Verify categories 1 and 3 are absent --> @@ -139,17 +139,17 @@ <!-- Verify products 1-3 are available on storefront --> <amOnPage url="{{StorefrontHomePage.url}}$$createProduct1.custom_attributes[url_key]$$.html" stepKey="amOnProduct1Page"/> <waitForPageLoad stepKey="product1WaitForPageLoad"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="browseAssertProduct1Page"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct1Page"> <argument name="product" value="$$createProduct1$$"/> </actionGroup> <amOnPage url="{{StorefrontHomePage.url}}$$createProduct2.custom_attributes[url_key]$$.html" stepKey="amOnProduct2Page"/> <waitForPageLoad stepKey="product2WaitForPageLoad"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="browseAssertProduct2Page"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct2Page"> <argument name="product" value="$$createProduct2$$"/> </actionGroup> <amOnPage url="{{StorefrontHomePage.url}}$$createProduct3.custom_attributes[url_key]$$.html" stepKey="amOnProduct3Page"/> <waitForPageLoad stepKey="product3WaitForPageLoad"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="browseAssertProduct3Page"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct3Page"> <argument name="product" value="$$createProduct3$$"/> </actionGroup> <!-- Rename New Root Category to Default category --> @@ -161,4 +161,4 @@ <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategoryDefaultCategory"/> <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessageAfterSaveDefaultCategory"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteUsedInConfigurableProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteUsedInConfigurableProductAttributeTest.xml index e79e4cea408fb..0daf8361ef9d1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteUsedInConfigurableProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteUsedInConfigurableProductAttributeTest.xml @@ -102,4 +102,4 @@ <argument name="productAttribute" value="$$productAttributeHandle$$"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/DisplayRefreshCacheAfterChangingCategoryPageLayoutTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/DisplayRefreshCacheAfterChangingCategoryPageLayoutTest.xml index 5a94dd4f04d24..1f71ec1e6a850 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/DisplayRefreshCacheAfterChangingCategoryPageLayoutTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/DisplayRefreshCacheAfterChangingCategoryPageLayoutTest.xml @@ -11,6 +11,7 @@ <test name="DisplayRefreshCacheAfterChangingCategoryPageLayoutTest"> <annotations> <features value="Catalog"/> + <stories value="Category Layout Change"/> <title value="'Refresh cache' admin notification is displayed when changing category page layout"/> <description value="'Refresh cache' message is not displayed when changing category page layout"/> <severity value="MAJOR"/> @@ -34,7 +35,7 @@ </after> <!-- Navigate to category details page --> <comment userInput="Navigate to category details page" stepKey="navigateToAdminCategoryPage"/> - <actionGroup ref="goToAdminCategoryPageById" stepKey="goToAdminCategoryPage"> + <actionGroup ref="GoToAdminCategoryPageByIdActionGroup" stepKey="goToAdminCategoryPage"> <argument name="id" value="$$simpleCategory.id$$"/> </actionGroup> <!-- Open design tab and set layout --> @@ -45,7 +46,8 @@ <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig" /> <waitForPageLoad stepKey="waitSaveToApply"/> <!-- See if warning message displays --> - <comment userInput="See if warning message displays" stepKey="checkWarningMessagePresence"/> - <see selector="{{AdminMessagesSection.warningMessage}}" userInput="Please go to Cache Management and refresh cache types" stepKey="seeWarningMessage"/> + <actionGroup ref="AdminSystemMessagesWarningActionGroup" stepKey="seeWarningMessage"> + <argument name="message" value="Please go to Cache Management and refresh cache types"/> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml index 1419ca4cb42ef..232321610eadc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -31,40 +31,40 @@ <!--Create Simple Product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPageSimple"/> <waitForPageLoad time="30" stepKey="waitForProductPageLoadSimple"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateSimpleProduct"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateSimpleProduct"> <argument name="product" value="SimpleProduct"/> </actionGroup> - <actionGroup ref="checkRequiredFieldsInProductForm" stepKey="checkRequiredFieldsProductSimple"/> - <actionGroup ref="fillMainProductForm" stepKey="fillSimpleProductMain"> + <actionGroup ref="CheckRequiredFieldsInProductFormActionGroup" stepKey="checkRequiredFieldsProductSimple"/> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillSimpleProductMain"> <argument name="product" value="SimpleProduct"/> </actionGroup> - <actionGroup ref="addProductImage" stepKey="addImageForProductSimple"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImageForProductSimple"> <argument name="image" value="ProductImage"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveSimpleProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSimpleProduct"/> <click selector="{{AdminProductFormActionSection.backButton}}" stepKey="clickBackToGridSimple"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridSimple"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridSimple"> <argument name="product" value="SimpleProduct"/> </actionGroup> <grabAttributeFrom selector="{{AdminProductGridSection.productThumbnail('1')}}" userInput="src" stepKey="getSimpleProductThumbnail"/> <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$getSimpleProductThumbnail" stepKey="simpleThumbnailIsNotDefault"/> - <actionGroup ref="viewProductInAdminGrid" stepKey="seeSimpleProductInGrid"> + <actionGroup ref="ViewProductInAdminGridActionGroup" stepKey="seeSimpleProductInGrid"> <argument name="product" value="SimpleProduct"/> </actionGroup> <!--Create Virtual Product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPageVirtual"/> <waitForPageLoad time="30" stepKey="waitForProductPageLoadVirtual"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateVirtualProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateVirtualProduct"> <argument name="product" value="VirtualProduct"/> </actionGroup> <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{VirtualProduct.sku}}" stepKey="fillVirtualName"/> <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{VirtualProduct.name}}" stepKey="fillVirtualSku"/> <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{VirtualProduct.price}}" stepKey="fillVirtualPrice"/> <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{VirtualProduct.quantity}}" stepKey="fillVirtualQty"/> - <actionGroup ref="saveProductForm" stepKey="saveVirtualProduct"/> - <actionGroup ref="viewProductInAdminGrid" stepKey="viewVirtualProductInGrid"> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveVirtualProduct"/> + <actionGroup ref="ViewProductInAdminGridActionGroup" stepKey="viewVirtualProductInGrid"> <argument name="product" value="VirtualProduct"/> </actionGroup> @@ -74,15 +74,15 @@ <waitForPageLoad stepKey="waitForProductGridPageLoad"/> <!--Search by keyword--> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetGridToDefaultKeywordSearch"/> - <actionGroup ref="searchProductGridByKeyword" stepKey="useKeywordSearchSimpleProduct"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> + <actionGroup ref="SearchProductGridByKeywordActionGroup" stepKey="useKeywordSearchSimpleProduct"> <argument name="keyword" value="SimpleProduct.name"/> </actionGroup> <seeNumberOfElements selector="{{AdminProductGridSection.productGridRows}}" userInput="1" stepKey="seeOnlyOneProductInGrid"/> <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="{{SimpleProduct.name}}" stepKey="seeOnlySimpleProductInGrid"/> <!--Paging works--> - <actionGroup ref="resetProductGridToDefaultView" stepKey="setProductGridToDefaultPagination"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="setProductGridToDefaultPagination"/> <comment userInput="Admin uses paging on product grid" stepKey="usePagingProductGridComment"/> <click selector="{{AdminProductGridPaginationSection.perPageDropdown}}" stepKey="clickProductPerPageDropdown"/> <click selector="{{AdminProductGridPaginationSection.perPageOption('50')}}" stepKey="selectProductsPerPage"/> @@ -100,14 +100,14 @@ <seeNumberOfElements selector="{{AdminProductGridSection.productGridRows}}" parameterArray="[1,5]" stepKey="seeProductsOnSecondPage"/> <!--Filtering works (by Name, By Price, by Status)--> - <actionGroup ref="resetProductGridToDefaultView" stepKey="setProductGridToDefaultFiltering"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridByGroupedSku"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="setProductGridToDefaultFiltering"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridByGroupedSku"> <argument name="product" value="GroupedProduct"/> </actionGroup> <seeNumberOfElements selector="{{AdminProductGridSection.productGridRows}}" userInput="1" stepKey="seeOneMatchingSkuInProductGrid"/> <see selector="{{AdminProductGridSection.productGridCell('1','SKU')}}" userInput="{{GroupedProduct.sku}}" stepKey="seeProductInFilteredGridSku"/> <!--Filter by price--> - <actionGroup ref="filterProductGridByPriceRange" stepKey="filterProductGridByPrice"> + <actionGroup ref="FilterProductGridByPriceRangeActionGroup" stepKey="filterProductGridByPrice"> <argument name="filter" value="PriceFilterRange"/> </actionGroup> <click selector="{{AdminProductGridSection.columnHeader('Price')}}" stepKey="clickPriceHeaderToSortAscForFilter"/> @@ -118,17 +118,17 @@ <assertRegExp expected="'/\$[0-9]{2}\.[0-9]{2}/'" actual="$getMaximumPriceInGrid" stepKey="assertMaximumPriceIsCorrect"/> <assertLessThan expected="$getMaximumPriceInGrid" actual="$getMinimumPriceInGrid" stepKey="checkPriceSortCorrect"/> <!--Filter by status--> - <actionGroup ref="filterProductGridByEnabledStatus" stepKey="filterGridByEnabledProducts"/> + <actionGroup ref="FilterProductGridByEnabledStatusActionGroup" stepKey="filterGridByEnabledProducts"/> <seeNumberOfElements selector="{{AdminProductGridSection.productGridRows}}" parameterArray="[1,20]" stepKey="seeEnabledProductsNotEmpty"/> <see selector="{{AdminProductGridSection.column('Status')}}" userInput="Enabled" stepKey="seeOnlyEnabledProducts"/> - <actionGroup ref="filterProductGridByDisabledStatus" stepKey="filterGridByDisabledProducts"/> + <actionGroup ref="FilterProductGridByDisabledStatusActionGroup" stepKey="filterGridByDisabledProducts"/> <dontSee selector="{{AdminProductGridSection.column('Status')}}" userInput="Enabled" stepKey="dontSeeEnabledProducts"/> <!--Sorting works (By Price, by ID)--> <!--By Price--> - <actionGroup ref="resetProductGridToDefaultView" stepKey="setProductGridToDefaultSortingPrice"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="setProductGridToDefaultSortingPrice"/> <!--Filter by price so grid contains prices that we can compare correctly--> - <actionGroup ref="filterProductGridByPriceRange" stepKey="filterProductGridByPriceForCompare"> + <actionGroup ref="FilterProductGridByPriceRangeActionGroup" stepKey="filterProductGridByPriceForCompare"> <argument name="filter" value="PriceFilterRange"/> </actionGroup> <!--Sort Ascending--> @@ -142,7 +142,7 @@ <grabTextFrom selector="{{AdminProductGridSection.productGridCell('2', 'Price')}}" stepKey="getSecondPriceSortDesc"/> <assertGreaterThanOrEqual expected="$getSecondPriceSortDesc" actual="$getFirstPriceSortDesc" stepKey="checkPriceDescSortCorrect"/> <!--By Product ID--> - <actionGroup ref="resetProductGridToDefaultView" stepKey="setProductGridToDefaultSortingId"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="setProductGridToDefaultSortingId"/> <!--Sort Ascending--> <grabTextFrom selector="{{AdminProductGridSection.productGridCell('1', 'ID')}}" stepKey="getFirstProductIdSortAsc"/> <grabTextFrom selector="{{AdminProductGridSection.productGridCell('2', 'ID')}}" stepKey="getSecondProductIdSortAsc"/> @@ -154,7 +154,7 @@ <assertGreaterThan expected="$getSecondProductIdSortDesc" actual="$getFirstProductIdSortDesc" stepKey="checkProductIdDescSortCorrect"/> <!--Adding column works--> - <actionGroup ref="resetProductGridToDefaultView" stepKey="setProductGridToDefaultColumns"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="setProductGridToDefaultColumns"/> <click selector="{{AdminProductGridFilterSection.columnsDropdown}}" stepKey="openColumnsDropdownToReset"/> <click selector="{{AdminProductGridFilterSection.resetGridColumns}}" stepKey="resetProductGridColumns"/> <click selector="{{AdminProductGridFilterSection.columnsDropdown}}" stepKey="closeColumnsDropdownAfterReset"/> @@ -170,7 +170,7 @@ <checkOption selector="{{AdminProductGridFilterSection.viewColumnOption('Weight')}}" stepKey="showWeightColumn"/> <click selector="{{AdminProductGridFilterSection.columnsDropdown}}" stepKey="closeColumnsDropdownWeight"/> <seeElement selector="{{AdminProductGridSection.columnHeader('Weight')}}" stepKey="seeWeightColumn"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridToCheckWeightColumn"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridToCheckWeightColumn"> <argument name="product" value="SimpleProduct"/> </actionGroup> <see selector="{{AdminProductGridSection.productGridCell('1','Weight')}}" userInput="{{SimpleProduct.weight}}" stepKey="seeCorrectProductWeightInGrid"/> @@ -182,13 +182,13 @@ <waitForPageLoad time="30" stepKey="waitForCategoryPageLoad"/> <!--Create category under Default Category--> <click selector="{{AdminCategorySidebarTreeSection.categoryTreeRoot}}" stepKey="clickDefaultCategory"/> - <actionGroup ref="CheckCategoryNameIsRequiredField" stepKey="checkCategoryNameIsRequired"/> - <actionGroup ref="CreateCategory" stepKey="createCategory"> + <actionGroup ref="CheckCategoryNameIsRequiredFieldActionGroup" stepKey="checkCategoryNameIsRequired"/> + <actionGroup ref="CreateCategoryActionGroup" stepKey="createCategory"> <argument name="categoryEntity" value="_defaultCategory"/> </actionGroup> <!--Create category under newly created category--> <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(_defaultCategory.name)}}" stepKey="clickCreatedCategoryInTree"/> - <actionGroup ref="CreateCategory" stepKey="createSubCategory"> + <actionGroup ref="CreateCategoryActionGroup" stepKey="createSubCategory"> <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> @@ -211,24 +211,24 @@ <comment userInput="Admin deletes category" stepKey="deleteCategoryComment"/> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="onCategoryPageToDeleteCategory"/> <waitForPageLoad time="30" stepKey="waitForCategoryPageDelete"/> - <actionGroup ref="DeleteCategory" stepKey="deleteCategory"> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"> <argument name="categoryEntity" value="_defaultCategory"/> </actionGroup> <!--@TODO Move cleanup to "after" when MQE-830 is resolved--> <!--Clean up categories--> <comment userInput="Clean up categories" stepKey="cleanupCategoriesComment"/> - <actionGroup ref="DeleteCategory" stepKey="cleanSimpleSubCategory"> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="cleanSimpleSubCategory"> <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> <!--Clean up products--> <comment userInput="Clean up simple product" stepKey="cleanUpSimpleProduct"/> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteSimpleProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteSimpleProduct"> <argument name="product" value="SimpleProduct"/> </actionGroup> <comment userInput="Clean up virtual product" stepKey="cleanUpVirtualProduct"/> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteVirtualProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteVirtualProduct"> <argument name="product" value="VirtualProduct"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index ad66214e902fe..f7ebb090124d6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -76,7 +76,7 @@ </actionGroup> <!-- Check simple product 1 in category --> <comment userInput="Check simple product 1 in category" stepKey="commentCheckSimpleProductInCategory" /> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="browseAssertCategoryProduct1"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct1"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -84,7 +84,7 @@ <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabSimpleProduct1ImageSrc" stepKey="browseAssertSimpleProduct1ImageNotDefault"/> <!-- Check simple product 2 in category --> <comment userInput="Check simple product 2 in category" stepKey="commentCheckSimpleProduct2InCategory" /> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="browseAssertCategoryProduct2"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct2"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -95,7 +95,7 @@ <comment userInput="View simple product 1" stepKey="commentViewSimpleProduct1" after="browseAssertSimpleProduct2ImageNotDefault"/> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="browseClickCategorySimpleProduct1View" after="commentViewSimpleProduct1"/> <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct1Viewloaded" /> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="browseAssertProduct1Page"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct1Page"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -107,7 +107,7 @@ <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory1"/> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct2.name$$)}}" stepKey="browseClickCategorySimpleProduct2View"/> <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct2ViewLoaded" /> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="browseAssertProduct2Page"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct2Page"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -125,7 +125,7 @@ <argument name="category" value="$$createCategory$$"/> <argument name="productCount" value="3"/> </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="compareAssertSimpleProduct1"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct1"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -133,7 +133,7 @@ <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct1ImageSrc" stepKey="compareAssertSimpleProduct1ImageNotDefault"/> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="compareClickSimpleProduct1"/> <waitForLoadingMaskToDisappear stepKey="waitForCompareSimpleProduct1loaded" /> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="compareAssertProduct1Page"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="compareAssertProduct1Page"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -151,7 +151,7 @@ <argument name="category" value="$$createCategory$$"/> <argument name="productCount" value="3"/> </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="compareAssertSimpleProduct2"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct2"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -269,7 +269,7 @@ </actionGroup> <!-- Check simple product 1 in category --> <comment userInput="Check simple product 1 in category" stepKey="commentCheckSimpleProductInCategory" /> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="browseAssertCategoryProduct1"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct1"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -277,7 +277,7 @@ <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabSimpleProduct1ImageSrc" stepKey="browseAssertSimpleProduct1ImageNotDefault"/> <!-- Check simple product 2 in category --> <comment userInput="Check simple product 2 in category" stepKey="commentCheckSimpleProduct2InCategory" /> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="browseAssertCategoryProduct2"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct2"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -288,7 +288,7 @@ <comment userInput="View simple product 1" stepKey="commentViewSimpleProduct1" after="browseAssertSimpleProduct2ImageNotDefault"/> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="browseClickCategorySimpleProduct1View" after="commentViewSimpleProduct1"/> <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct1Viewloaded" /> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="browseAssertProduct1Page"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct1Page"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -300,7 +300,7 @@ <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory1"/> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct2.name$$)}}" stepKey="browseClickCategorySimpleProduct2View"/> <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct2ViewLoaded" /> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="browseAssertProduct2Page"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct2Page"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -318,7 +318,7 @@ <argument name="category" value="$$createCategory$$"/> <argument name="productCount" value="3"/> </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="compareAssertSimpleProduct1"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct1"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -326,7 +326,7 @@ <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct1ImageSrc" stepKey="compareAssertSimpleProduct1ImageNotDefault"/> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="compareClickSimpleProduct1"/> <waitForLoadingMaskToDisappear stepKey="waitForCompareSimpleProduct1loaded" /> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="compareAssertProduct1Page"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="compareAssertProduct1Page"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -344,7 +344,7 @@ <argument name="category" value="$$createCategory$$"/> <argument name="productCount" value="3"/> </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="compareAssertSimpleProduct2"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct2"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index 3f48c3ca811e3..74c6da1c47f60 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -61,7 +61,7 @@ </actionGroup> <!-- Check simple product 1 in category --> <comment userInput="Check simple product 1 in category" stepKey="commentCheckSimpleProductInCategory" after="browseAssertCategory"/> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="browseAssertCategoryProduct1" after="commentCheckSimpleProductInCategory"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct1" after="commentCheckSimpleProductInCategory"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -69,7 +69,7 @@ <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$browseGrabSimpleProduct1ImageSrc" stepKey="browseAssertSimpleProduct1ImageNotDefault" after="browseGrabSimpleProduct1ImageSrc"/> <!-- Check simple product 2 in category --> <comment userInput="Check simple product 2 in category" stepKey="commentCheckSimpleProduct2InCategory" after="browseAssertSimpleProduct1ImageNotDefault"/> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="browseAssertCategoryProduct2" after="commentCheckSimpleProduct2InCategory"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct2" after="commentCheckSimpleProduct2InCategory"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -80,7 +80,7 @@ <comment userInput="View simple product 1" stepKey="commentViewSimpleProduct1" after="browseAssertSimpleProduct2ImageNotDefault"/> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="browseClickCategorySimpleProduct1View" after="commentViewSimpleProduct1"/> <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct1Viewloaded" after="browseClickCategorySimpleProduct1View"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="browseAssertProduct1Page" after="waitForSimpleProduct1Viewloaded"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct1Page" after="waitForSimpleProduct1Viewloaded"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -92,7 +92,7 @@ <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory1" after="commentViewSimpleProduct2"/> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct2.name$$)}}" stepKey="browseClickCategorySimpleProduct2View" after="clickCategory1"/> <waitForLoadingMaskToDisappear stepKey="waitForSimpleProduct2ViewLoaded" after="browseClickCategorySimpleProduct2View"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="browseAssertProduct2Page" after="waitForSimpleProduct2ViewLoaded"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="browseAssertProduct2Page" after="waitForSimpleProduct2ViewLoaded"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -110,7 +110,7 @@ <argument name="category" value="$$createCategory$$"/> <argument name="productCount" value="3"/> </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="compareAssertSimpleProduct1" after="compareAssertCategory"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct1" after="compareAssertCategory"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -118,7 +118,7 @@ <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$compareGrabSimpleProduct1ImageSrc" stepKey="compareAssertSimpleProduct1ImageNotDefault" after="compareGrabSimpleProduct1ImageSrc"/> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="compareClickSimpleProduct1" after="compareAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCompareSimpleProduct1loaded" after="compareClickSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="compareAssertProduct1Page" after="waitForCompareSimpleProduct1loaded"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="compareAssertProduct1Page" after="waitForCompareSimpleProduct1loaded"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -136,7 +136,7 @@ <argument name="category" value="$$createCategory$$"/> <argument name="productCount" value="3"/> </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="compareAssertSimpleProduct2" after="compareAssertCategory1"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="compareAssertSimpleProduct2" after="compareAssertCategory1"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/ProductAttributeWithoutValueInCompareListTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAttributeWithoutValueInCompareListTest.xml new file mode 100644 index 0000000000000..70d2fb63941c7 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/ProductAttributeWithoutValueInCompareListTest.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="ProductAttributeWithoutValueInCompareListTest"> + <annotations> + <features value="Catalog"/> + <title value="Product attribute without value in compare list test"/> + <description + value="The product attribute that has no value should output 'N/A' on the product comparison page."/> + <severity value="MINOR"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="textProductAttribute" stepKey="createProductAttribute"/> + <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> + <amOnPage url="{{AdminProductAttributeSetEditPage.url}}/$$createAttributeSet.attribute_set_id$$/" + stepKey="onAttributeSetEdit"/> + <actionGroup ref="AssignAttributeToGroupActionGroup" stepKey="assignAttributeToGroup"> + <argument name="group" value="Product Details"/> + <argument name="attribute" value="$$createProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="SaveAttributeSetActionGroup" stepKey="SaveAttributeSet"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProductDefault"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProductWithCustomAttributeSet" stepKey="createProductCustom"> + <requiredEntity createDataKey="createCategory"/> + <requiredEntity createDataKey="createAttributeSet"/> + </createData> + </before> + <after> + <deleteData createDataKey="createProductDefault" stepKey="deleteProductDefault"/> + <deleteData createDataKey="createProductCustom" stepKey="deleteProductCustom"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createAttributeSet" stepKey="deleteAttributeSet"/> + <deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!--Open product page--> + <amOnPage url="{{StorefrontProductPage.url($$createProductDefault.name$$)}}" stepKey="goToProductDefaultPage"/> + <waitForPageLoad stepKey="waitForProductPage"/> + <!--Click on 'Add to Compare' link--> + <actionGroup ref="StorefrontAddProductToCompareActionGroup" stepKey="clickOnAddToCompare"> + <argument name="productVar" value="$$createProductDefault$$"/> + </actionGroup> + <!--See product in the comparison list--> + <actionGroup ref="SeeProductInComparisonListActionGroup" stepKey="seeProductDefaultInComparisonListActionGroup"> + <argument name="productVar" value="$$createProductDefault$$"/> + </actionGroup> + <!--Open product with custom attribute page--> + <amOnPage url="{{StorefrontProductPage.url($$createProductCustom.name$$)}}" stepKey="goToProductCustomPage"/> + <waitForPageLoad stepKey="waitForProductCustomPage"/> + <!--Click on 'Add to Compare' link--> + <actionGroup ref="StorefrontAddProductToCompareActionGroup" stepKey="clickOnAddToCompareCustom"> + <argument name="productVar" value="$$createProductCustom$$"/> + </actionGroup> + <!--See product with custom attribute in the comparison list--> + <actionGroup ref="SeeProductInComparisonListActionGroup" stepKey="seeProductCustomInComparisonListActionGroup"> + <argument name="productVar" value="$$createProductCustom$$"/> + </actionGroup> + <!--See attribute default value in the comparison list--> + <see userInput="$createProductAttribute.defaultValue$" + selector="{{StorefrontProductCompareMainSection.ProductAttributeByCodeAndProductName(ProductAttributeFrontendLabel.label, $createProductCustom.name$)}}" + stepKey="assertAttributeValueForProductCustom"/> + <!--See N/A if attribute has no value in the comparison list--> + <see userInput="N/A" + selector="{{StorefrontProductCompareMainSection.ProductAttributeByCodeAndProductName(ProductAttributeFrontendLabel.label, $createProductDefault.name$)}}" + stepKey="assertNAForProductDefault"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml index 8092b03c53cba..89e784d67eaea 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/SaveProductWithCustomOptionsSecondWebsiteTest.xml @@ -49,14 +49,14 @@ <see userInput="You saved the store view." stepKey="seeSaveMessage" /> </before> <after> - <actionGroup ref="ResetWebUrlOptions" stepKey="resetUrlOption"/> + <actionGroup ref="ResetWebUrlOptionsActionGroup" stepKey="resetUrlOption"/> <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteTestWebsite"> <argument name="websiteName" value="Second Website"/> </actionGroup> <actionGroup ref="logout" stepKey="adminLogout"/> </after> - <actionGroup ref="EnableWebUrlOptions" stepKey="addStoreCodeToUrls"/> + <actionGroup ref="EnableWebUrlOptionsActionGroup" stepKey="addStoreCodeToUrls"/> <!--Create a Simple Product with Custom Options --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToCatalogProductGrid"/> <waitForPageLoad stepKey="waitForCatalogProductGrid"/> @@ -103,4 +103,4 @@ <seeNumberOfElements selector=".admin__dynamic-rows[data-index='values'] tr.data-row" userInput="3" stepKey="see4RowsOfOptions"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/SimpleProductTwoCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/SimpleProductTwoCustomOptionsTest.xml index 0049dcb504335..41bacc69baca4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/SimpleProductTwoCustomOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/SimpleProductTwoCustomOptionsTest.xml @@ -25,16 +25,16 @@ <!--Create product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPage"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateSimpleProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateSimpleProduct"> <argument name="product" value="SimpleProduct3"/> </actionGroup> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillSimpleProductMain"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillSimpleProductMain"> <argument name="product" value="SimpleProduct3"/> </actionGroup> </before> <after> <!-- Delete the created product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="SimpleProduct3"/> </actionGroup> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> @@ -45,14 +45,14 @@ <waitForPageLoad stepKey="waitForCustomOptionsOpen"/> <!-- Create a custom option with 2 values --> - <actionGroup ref="CreateCustomRadioOptions" stepKey="createCustomOption1"> + <actionGroup ref="CreateCustomRadioOptionsActionGroup" stepKey="createCustomOption1"> <argument name="customOptionName" value="ProductOptionRadiobutton.title"/> <argument name="productOption" value="ProductOptionField"/> <argument name="productOption2" value="ProductOptionField2"/> </actionGroup> <!-- Create another custom option with 2 values --> - <actionGroup ref="CreateCustomRadioOptions" stepKey="createCustomOption2"> + <actionGroup ref="CreateCustomRadioOptionsActionGroup" stepKey="createCustomOption2"> <argument name="customOptionName" value="ProductOptionRadiobutton.title"/> <argument name="productOption" value="ProductOptionField"/> <argument name="productOption2" value="ProductOptionField2"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCatalogNavigationMenuUIDesktopTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCatalogNavigationMenuUIDesktopTest.xml index ae54b72a5a702..22ec0048497fa 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCatalogNavigationMenuUIDesktopTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontCatalogNavigationMenuUIDesktopTest.xml @@ -21,10 +21,10 @@ <before> <!-- Login as admin --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="DeleteDefaultCategoryChildren" stepKey="deleteRootCategoryChildren"/> + <actionGroup ref="DeleteDefaultCategoryChildrenActionGroup" stepKey="deleteRootCategoryChildren"/> </before> <after> - <actionGroup ref="DeleteDefaultCategoryChildren" stepKey="deleteRootCategoryChildren"/> + <actionGroup ref="DeleteDefaultCategoryChildrenActionGroup" stepKey="deleteRootCategoryChildren"/> <actionGroup ref="AdminChangeStorefrontThemeActionGroup" stepKey="changeThemeToDefault"> <argument name="theme" value="{{MagentoLumaTheme.name}}"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontEnsureThatAccordionAnchorIsVisibleOnViewportOnceClickedTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontEnsureThatAccordionAnchorIsVisibleOnViewportOnceClickedTest.xml index 0f9f542a97d02..30461a30e53cb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontEnsureThatAccordionAnchorIsVisibleOnViewportOnceClickedTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontEnsureThatAccordionAnchorIsVisibleOnViewportOnceClickedTest.xml @@ -84,7 +84,7 @@ <selectOption selector="{{AdminProductFormSection.customSelectField($$createFourthAttribute.attribute[attribute_code]$$)}}" userInput="$$createSecondOption.option[store_labels][0][label]$$" stepKey="setFourthAttributeValue"/> <!-- Save product form --> - <actionGroup ref="saveProductForm" stepKey="saveSimpleProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSimpleProduct"/> <!-- Go to frontend and make a user account and login with it --> <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="signUpNewUser"> @@ -102,7 +102,7 @@ <!-- Go to Pending reviews page and clear filters --> <actionGroup ref="AdminOpenPendingReviewsPageActionGroup" stepKey="openReviewsPage"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilters"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilters"/> <!-- Moderate first product reviews: change review status from pending to approved, save --> <actionGroup ref="AdminOpenReviewByUserNicknameActionGroup" stepKey="openFirstCustomerReviews"/> @@ -110,7 +110,7 @@ <actionGroup ref="AdminSaveReviewActionGroup" stepKey="saveModeratedFirstReview"/> <!-- Moderate second product reviews: change review status from pending to approved, save --> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilter"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilter"/> <actionGroup ref="AdminOpenReviewByUserNicknameActionGroup" stepKey="openSecondCustomerReviews"/> <actionGroup ref="AdminChangeReviewStatusActionGroup" stepKey="changeSecondReviewStatus"/> <actionGroup ref="AdminSaveReviewActionGroup" stepKey="saveModeratedSecondReview"/> @@ -133,19 +133,19 @@ </actionGroup> <!-- Assert that product page has all product attributes in More Info tab --> - <actionGroup ref="checkAttributeInMoreInformationTab" stepKey="checkFirstAttributeInMoreInformationTab"> + <actionGroup ref="CheckAttributeInMoreInformationTabActionGroup" stepKey="checkFirstAttributeInMoreInformationTab"> <argument name="attributeLabel" value="$$createFirstAttribute.attribute[frontend_labels][0][label]$$"/> <argument name="attributeValue" value="$$createOption.option[store_labels][0][label]$$"/> </actionGroup> - <actionGroup ref="checkAttributeInMoreInformationTab" stepKey="checkSecondAttributeInMoreInformationTab"> + <actionGroup ref="CheckAttributeInMoreInformationTabActionGroup" stepKey="checkSecondAttributeInMoreInformationTab"> <argument name="attributeLabel" value="$$createSecondAttribute.attribute[frontend_labels][0][label]$$"/> <argument name="attributeValue" value="{{ProductAttributeOption8.value}}"/> </actionGroup> - <actionGroup ref="checkAttributeInMoreInformationTab" stepKey="checkThirdAttributeInMoreInformationTab"> + <actionGroup ref="CheckAttributeInMoreInformationTabActionGroup" stepKey="checkThirdAttributeInMoreInformationTab"> <argument name="attributeLabel" value="$$createThirdAttribute.attribute[frontend_labels][0][label]$$"/> <argument name="attributeValue" value="{{ProductAttributeOption8.value}}"/> </actionGroup> - <actionGroup ref="checkAttributeInMoreInformationTab" stepKey="checkFourthAttributeInMoreInformationTab"> + <actionGroup ref="CheckAttributeInMoreInformationTabActionGroup" stepKey="checkFourthAttributeInMoreInformationTab"> <argument name="attributeLabel" value="$$createFourthAttribute.attribute[frontend_labels][0][label]$$"/> <argument name="attributeValue" value="$$createSecondOption.option[store_labels][0][label]$$"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontForthLevelCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontForthLevelCategoryTest.xml new file mode 100644 index 0000000000000..bb46f8010eaa8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontForthLevelCategoryTest.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="StorefrontForthLevelCategoryTest"> + <annotations> + <features value="Catalog"/> + <stories value="Category"/> + <title value="Storefront forth level category test"/> + <description value="When the submenu was created in the third stage follow, the submenu works"/> + <severity value="MAJOR"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="category1"/> + <createData entity="SubCategoryWithParent" stepKey="category2"> + <requiredEntity createDataKey="category1"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="category3"> + <requiredEntity createDataKey="category2"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="category4"> + <requiredEntity createDataKey="category3"/> + </createData> + </before> + <after> + <deleteData createDataKey="category4" stepKey="deleteCategory4"/> + <deleteData createDataKey="category3" stepKey="deleteCategory3"/> + <deleteData createDataKey="category2" stepKey="deleteCategory2"/> + <deleteData createDataKey="category1" stepKey="deleteCategory1"/> + </after> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> + <moveMouseOver + selector="{{StorefrontHeaderSection.NavigationCategoryByName($$category1.name$$)}}" + stepKey="hoverCategoryLevelOne"/> + <moveMouseOver + selector="{{StorefrontHeaderSection.NavigationCategoryByName($$category2.name$$)}}" + stepKey="hoverCategoryLevelTwo"/> + <moveMouseOver + selector="{{StorefrontHeaderSection.NavigationCategoryByName($$category3.name$$)}}" + stepKey="hoverCategoryLevelThree"/> + <moveMouseOver + selector="{{StorefrontHeaderSection.NavigationCategoryByName($$category4.name$$)}}" + stepKey="hoverCategoryLevelFour"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontFotoramaArrowsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontFotoramaArrowsTest.xml new file mode 100644 index 0000000000000..1a8e0c95a304c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontFotoramaArrowsTest.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminAddDefaultImageSimpleProductTest"> + <annotations> + <features value="Catalog"/> + <title value="Storefront Fotorama arrows test"/> + <description value="Check arrows next to the thumbs are not visible than there is room for all pictures."/> + <severity value="MINOR"/> + <group value="Catalog"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <!-- Open Product for edit --> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="findCreatedProductInGrid"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="goToEditProductPage"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + + <!-- Add images to product --> + <actionGroup ref="AddProductImageActionGroup" stepKey="addFirstImageToProduct"> + <argument name="image" value="MagentoLogo"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addSecondImageToProduct"> + <argument name="image" value="ProductImage"/> + </actionGroup> + <actionGroup ref="AddProductImageActionGroup" stepKey="addThirdImageToProduct"> + <argument name="image" value="TestImageNew"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSimpleProduct"/> + + <!-- Assert product in storefront product page --> + <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openCreatedProductPage"> + <argument name="productUrl" value="$$createProduct.name$$"/> + </actionGroup> + + <!-- Assert Fotorama arrows aren't visible --> + <dontSeeElement selector="{{StorefrontProductMediaSection.fotoramaPrevButton}}" stepKey="dontSeePrevButton"/> + <dontSeeElement selector="{{StorefrontProductMediaSection.fotoramaNextButton}}" stepKey="dontSeeNextButton"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml index 21f8e2e070e32..07c2e8a972596 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductNameWithDoubleQuote.xml @@ -29,17 +29,17 @@ <!--Create product via admin--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPage"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToProductCreatePage"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToProductCreatePage"> <argument name="product" value="SimpleProductNameWithDoubleQuote"/> </actionGroup> - <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="SimpleProductNameWithDoubleQuote"/> </actionGroup> <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="selectCategory"/> - <actionGroup ref="addProductImage" stepKey="addImageToProduct"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImageToProduct"> <argument name="image" value="ProductImage"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!--Run re-index task--> <magentoCLI command="indexer:reindex" stepKey="reindex"/> @@ -61,7 +61,7 @@ <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="{{SimpleProductNameWithDoubleQuote.name}}" stepKey="seeCorrectBreadCrumbProduct"/> <!--Remove product--> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="SimpleProductNameWithDoubleQuote"/> </actionGroup> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml index 1c1b47a6bded9..db693b7229b17 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml @@ -23,7 +23,7 @@ <createData entity="productAttributeWithDropdownTwoOptions" stepKey="createProductAttribute"/> </before> <after> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteSimpleProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteSimpleProduct"> <argument name="product" value="SimpleProduct"/> </actionGroup> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> @@ -38,7 +38,7 @@ <click selector="{{AttributeSetSection.Save}}" stepKey="saveAttributeSet"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear" /> <seeElement selector=".message-success" stepKey="assertSuccess"/> - <actionGroup ref="FillAdminSimpleProductForm" stepKey="fillProductFieldsInAdmin"> + <actionGroup ref="FillAdminSimpleProductFormActionGroup" stepKey="fillProductFieldsInAdmin"> <argument name="category" value="$$createPreReqCategory$$"/> <argument name="simpleProduct" value="SimpleProduct"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml index a0670bdee54c7..8637de9711da0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml @@ -63,12 +63,12 @@ <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView2"> <argument name="customStore" value="customStoreFR"/> </actionGroup> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearWebsitesGridFilters"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearWebsitesGridFilters"/> <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearOrdersGridFilter"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearProductsGridFilters"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> <actionGroup ref="logout" stepKey="logout"/> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogoutStorefront"/> </after> @@ -196,7 +196,7 @@ <!-- Open Order --> - <actionGroup ref="filterOrderGridById" stepKey="openOrdersGrid"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="openOrdersGrid"> <argument name="orderId" value="{$grabOrderNumber}"/> </actionGroup> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml index 12a465188aa85..c0653d9dbba7e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml @@ -130,7 +130,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <actionGroup ref="filterOrderGridById" stepKey="filterByOrderId"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterByOrderId"> <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> <click selector="{{AdminDataGridTableSection.firstRow}}" stepKey="clickOrderRow"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml index e9e23cf157a26..7a667ce9667d4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml @@ -100,7 +100,7 @@ <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnOrdersPage"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilter"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/> <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="fillOrderNum"/> <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchOrderNum"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnSearch"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontRememberCategoryPaginationTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontRememberCategoryPaginationTest.xml index 0ed61b8636c4f..c8872425552be 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontRememberCategoryPaginationTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontRememberCategoryPaginationTest.xml @@ -1,68 +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="StorefrontRememberCategoryPaginationTest"> - <annotations> - <title value="Verify that Number of Products per page retained when visiting a different category"/> - <stories value="MAGETWO-61478: Number of Products displayed per page not retained when visiting a different category"/> - <description value="Verify that Number of Products per page retained when visiting a different category"/> - <features value="Catalog"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-94210"/> - <group value="Catalog"/> - </annotations> - - <before> - <createData entity="_defaultCategory" stepKey="defaultCategory1"/> - <createData entity="SimpleProduct" stepKey="simpleProduct1"> - <requiredEntity createDataKey="defaultCategory1"/> - </createData> - - <createData entity="_defaultCategory" stepKey="defaultCategory2"/> - <createData entity="SimpleProduct" stepKey="simpleProduct2"> - <requiredEntity createDataKey="defaultCategory2"/> - </createData> - - <createData entity="RememberPaginationCatalogStorefrontConfig" stepKey="setRememberPaginationCatalogStorefrontConfig"/> - </before> - - <actionGroup ref="GoToStorefrontCategoryPageByParameters" stepKey="GoToStorefrontCategory1Page"> - <argument name="category" value="$$defaultCategory1.custom_attributes[url_key]$$"/> - <argument name="mode" value="grid"/> - <argument name="numOfProductsPerPage" value="12"/> - </actionGroup> - - <actionGroup ref="VerifyCategoryPageParameters" stepKey="verifyCategory1PageParameters"> - <argument name="category" value="$$defaultCategory1$$"/> - <argument name="mode" value="grid"/> - <argument name="numOfProductsPerPage" value="12"/> - </actionGroup> - - <amOnPage url="{{StorefrontCategoryPage.url($$defaultCategory2.name$$)}}" stepKey="navigateToCategory2Page"/> - <waitForPageLoad stepKey="waitForCategory2PageToLoad"/> - - <actionGroup ref="VerifyCategoryPageParameters" stepKey="verifyCategory2PageParameters"> - <argument name="category" value="$$defaultCategory2$$"/> - <argument name="mode" value="grid"/> - <argument name="numOfProductsPerPage" value="12"/> - </actionGroup> - - <after> - <createData entity="DefaultCatalogStorefrontConfiguration" stepKey="setDefaultCatalogStorefrontConfiguration"/> - - <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct1"/> - <deleteData createDataKey="defaultCategory1" stepKey="deleteCategory1"/> - <deleteData createDataKey="simpleProduct2" stepKey="deleteProduct2"/> - <deleteData createDataKey="defaultCategory2" stepKey="deleteCategory2"/> - - <magentoCLI command="cache:flush" stepKey="flushCache"/> - - </after> - </test> -</tests> +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontRememberCategoryPaginationTest"> + <annotations> + <title value="Verify that Number of Products per page retained when visiting a different category"/> + <stories value="MAGETWO-61478: Number of Products displayed per page not retained when visiting a different category"/> + <description value="Verify that Number of Products per page retained when visiting a different category"/> + <features value="Catalog"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-94210"/> + <group value="Catalog"/> + </annotations> + + <before> + <createData entity="_defaultCategory" stepKey="defaultCategory1"/> + <createData entity="SimpleProduct" stepKey="simpleProduct1"> + <requiredEntity createDataKey="defaultCategory1"/> + </createData> + + <createData entity="_defaultCategory" stepKey="defaultCategory2"/> + <createData entity="SimpleProduct" stepKey="simpleProduct2"> + <requiredEntity createDataKey="defaultCategory2"/> + </createData> + + <createData entity="RememberPaginationCatalogStorefrontConfig" stepKey="setRememberPaginationCatalogStorefrontConfig"/> + </before> + + <actionGroup ref="GoToStorefrontCategoryPageByParametersActionGroup" stepKey="GoToStorefrontCategory1Page"> + <argument name="category" value="$$defaultCategory1.custom_attributes[url_key]$$"/> + <argument name="mode" value="grid"/> + <argument name="numOfProductsPerPage" value="12"/> + </actionGroup> + + <actionGroup ref="VerifyCategoryPageParametersActionGroup" stepKey="verifyCategory1PageParameters"> + <argument name="category" value="$$defaultCategory1$$"/> + <argument name="mode" value="grid"/> + <argument name="numOfProductsPerPage" value="12"/> + </actionGroup> + + <amOnPage url="{{StorefrontCategoryPage.url($$defaultCategory2.name$$)}}" stepKey="navigateToCategory2Page"/> + <waitForPageLoad stepKey="waitForCategory2PageToLoad"/> + + <actionGroup ref="VerifyCategoryPageParametersActionGroup" stepKey="verifyCategory2PageParameters"> + <argument name="category" value="$$defaultCategory2$$"/> + <argument name="mode" value="grid"/> + <argument name="numOfProductsPerPage" value="12"/> + </actionGroup> + + <after> + <createData entity="DefaultCatalogStorefrontConfiguration" stepKey="setDefaultCatalogStorefrontConfiguration"/> + + <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="defaultCategory1" stepKey="deleteCategory1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteProduct2"/> + <deleteData createDataKey="defaultCategory2" stepKey="deleteCategory2"/> + + <magentoCLI command="cache:flush" stepKey="flushCache"/> + + </after> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml index 514e12bb355a8..3e887a0a83aa4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontSpecialPriceForDifferentTimezonesForWebsitesTest.xml @@ -61,7 +61,7 @@ <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="setSpecialPriceToCreatedProduct"> <argument name="price" value="15"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProductForm"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> <!--Login to storefront from customer and check price--> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="logInFromCustomer"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml index 4d7c97b26457c..0413018128491 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/TieredPricingAndQuantityIncrementsWorkWithDecimalinventoryTest.xml @@ -79,7 +79,7 @@ <waitForElementVisible selector="{{StorefrontProductPageSection.successMsg}}" time="30" stepKey="waitForProductAdded"/> <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added $$createPreReqSimpleProduct.name$$ to your shopping cart." stepKey="seeAddedToCartMessage"/> <!--Step9. Click on *Cart* icon. Click on *View and Edit Cart* link. Change *Qty* value to *5.5*--> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <fillField selector="{{CheckoutCartProductSection.ProductQuantityByName(('$$createPreReqSimpleProduct.name$$'))}}" userInput="5.5" stepKey="fillQty2"/> <click selector="{{CheckoutCartProductSection.updateShoppingCartButton}}" stepKey="clickOnUpdateShoppingCartButton"/> <seeInField userInput="5.5" selector="{{CheckoutCartProductSection.ProductQuantityByName(('$$createPreReqSimpleProduct.name$$'))}}" stepKey="seeInField2"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml index 6184a220f047c..e91f9742b2841 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyCategoryProductAndProductCategoryPartialReindexTest.xml @@ -50,12 +50,13 @@ <createData entity="SimpleProduct2" stepKey="productC"/> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> - <actionGroup ref="AdminAssignProductToCategory" stepKey="assignCategoryNAndMToProductC"> + <actionGroup ref="AdminAssignProductToCategoryActionGroup" stepKey="assignCategoryNAndMToProductC"> <argument name="productId" value="$$productC.id$$"/> <argument name="categoryName" value="$$categoryN.name$$, $$categoryM.name$$"/> </actionGroup> <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" arguments="full_page" stepKey="flushCache"/> </before> <after> <!-- Change "Category Products" and "Product Categories" indexers to "Update on Save" mode --> @@ -96,7 +97,7 @@ <!-- Open Products A, B, C to edit. Assign/unassign categories to/from them. Save changes --> <!-- Assign category K to Product A --> - <actionGroup ref="AdminAssignProductToCategory" stepKey="assignCategoryK"> + <actionGroup ref="AdminAssignProductToCategoryActionGroup" stepKey="assignCategoryK"> <argument name="productId" value="$$productA.id$$"/> <argument name="categoryName" value="$$categoryK.name$$"/> </actionGroup> @@ -108,14 +109,14 @@ </actionGroup> <!-- Assign category L to Product C --> - <actionGroup ref="AdminAssignProductToCategory" stepKey="assignCategoryNAndM"> + <actionGroup ref="AdminAssignProductToCategoryActionGroup" stepKey="assignCategoryNAndM"> <argument name="productId" value="$$productC.id$$"/> <argument name="categoryName" value="$$categoryL.name$$"/> </actionGroup> <!-- "One or more indexers are invalid. Make sure your Magento cron job is running." global warning message appears --> <click selector="{{AdminSystemMessagesSection.systemMessagesDropdown}}" stepKey="openMessageSection"/> - <see userInput="One or more indexers are invalid. Make sure your Magento cron job is running." selector="{{AdminMessagesSection.warningMessage}}" stepKey="seeWarningMessage"/> + <see userInput="One or more indexers are invalid. Make sure your Magento cron job is running." selector="{{AdminSystemMessagesSection.warning}}" stepKey="seeWarningMessage"/> <!-- Open categories K, L, M, N on Storefront in order to make sure that new assigments are not applied yet --> <!-- Category K contains only Products B & C --> @@ -175,7 +176,7 @@ </actionGroup> <!-- Add Product B assignment for category N --> - <actionGroup ref="AdminAssignProductToCategory" stepKey="assignCategoryN"> + <actionGroup ref="AdminAssignProductToCategoryActionGroup" stepKey="assignCategoryN"> <argument name="productId" value="$$productB.id$$"/> <argument name="categoryName" value="$$categoryN.name$$"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml index 455e9b58156eb..d39d54400279c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyChildCategoriesShouldNotIncludeInMenuTest.xml @@ -22,7 +22,7 @@ <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToCategoryPage2"/> <waitForPageLoad stepKey="waitForPageLoad3"/> <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(SimpleSubCategory.name)}}" stepKey="clickOnCreatedSimpleSubCategoryBeforeDelete"/> - <actionGroup ref="DeleteCategory" stepKey="deleteCategory"> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"> <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> @@ -32,11 +32,11 @@ <waitForPageLoad stepKey="waitForPageLoad1"/> <scrollToTopOfPage stepKey="scrollToTopOfPage"/> <!--Create new category under Default Category--> - <actionGroup ref="CreateCategory" stepKey="createSubcategory1"> + <actionGroup ref="CreateCategoryActionGroup" stepKey="createSubcategory1"> <argument name="categoryEntity" value="SimpleSubCategory"/> </actionGroup> <!--Create another subcategory under created category--> - <actionGroup ref="CreateCategory" stepKey="createSubcategory2"> + <actionGroup ref="CreateCategoryActionGroup" stepKey="createSubcategory2"> <argument name="categoryEntity" value="SubCategoryWithParent"/> </actionGroup> <!--Go to storefront and verify visibility of categories--> @@ -85,4 +85,4 @@ <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SimpleSubCategory.name)}}" stepKey="dontSeeSimpleSubCategoryOnStorefront3"/> <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SubCategoryWithParent.name)}}" stepKey="dontSeeSubCategoryWithParentOnStorefront4"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest.xml index 53bcac5b1d5f0..fe0ed32f39e1e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyDefaultWYSIWYGToolbarOnProductTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <amOnPage url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, 'simple')}}" stepKey="navigateToProduct"/> @@ -43,7 +43,7 @@ <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.InsertTable}}" stepKey="assertInfo13"/> <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.SpecialCharacter}}" stepKey="assertInfo14"/> <after> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> @@ -59,7 +59,7 @@ </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <amOnPage url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, 'simple')}}" stepKey="navigateToProduct"/> @@ -82,7 +82,7 @@ <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.InsertTable}}" stepKey="assertInfo27"/> <seeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.SpecialCharacter}}" stepKey="assertInfo28"/> <after> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCatalogTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCatalogTest.xml index aad83cd0c2aff..cc69d0828015f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCatalogTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCatalogTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToNewCatalog"/> @@ -45,7 +45,7 @@ <waitForElementVisible selector="{{StorefrontCategoryMainSection.CatalogDescription}}" stepKey="waitForDesVisible" /> <see userInput="Hello World!" selector="{{StorefrontCategoryMainSection.CatalogDescription}}" stepKey="assertCatalogDescription"/> <after> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnProductTest.xml index 29ed3af4f01d9..5a2728b6532c8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnProductTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <amOnPage url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, 'simple')}}" stepKey="navigateToNewProduct"/> @@ -58,7 +58,7 @@ <see userInput="Hello World!" selector="{{StorefrontProductInfoMainSection.productDescription}}" stepKey="assertProductDescription"/> <see userInput="Hello World! Short Content" selector="{{StorefrontProductInfoMainSection.productShortDescription}}" stepKey="assertProductShortDescription"/> <after> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Attribute/Edit/Tab/AdvancedTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Attribute/Edit/Tab/AdvancedTest.php index 0352bc83cafb7..4d9345d0b3f22 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Attribute/Edit/Tab/AdvancedTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Attribute/Edit/Tab/AdvancedTest.php @@ -5,66 +5,87 @@ */ namespace Magento\Catalog\Test\Unit\Block\Adminhtml\Product\Attribute\Edit\Tab; +use Magento\Catalog\Block\Adminhtml\Product\Attribute\Edit\Tab\Advanced; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\Config\Model\Config\Source\Yesno; use Magento\Eav\Block\Adminhtml\Attribute\PropertyLocker; +use Magento\Eav\Helper\Data as EavHelper; +use Magento\Eav\Model\Entity\Type as EntityType; +use Magento\Framework\Data\Form; +use Magento\Framework\Data\Form\Element\Fieldset; +use Magento\Framework\Data\Form\Element\Text; +use Magento\Framework\Data\FormFactory; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\Registry; +use Magento\Framework\Stdlib\DateTime; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; /** + * Test product attribute add/edit advanced form tab + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class AdvancedTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Catalog\Block\Adminhtml\Product\Attribute\Grid + * @var Advanced */ protected $block; /** - * @var \Magento\Framework\Data\FormFactory|\PHPUnit_Framework_MockObject_MockObject + * @var FormFactory|MockObject */ protected $formFactory; /** - * @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject + * @var Registry|MockObject */ protected $registry; /** - * @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface|\PHPUnit_Framework_MockObject_MockObject + * @var TimezoneInterface|MockObject */ protected $localeDate; /** - * @var \Magento\Config\Model\Config\Source\Yesno|\PHPUnit_Framework_MockObject_MockObject + * @var Yesno|MockObject */ protected $yesNo; /** - * @var \Magento\Eav\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + * @var EavHelper|MockObject */ protected $eavData; /** - * @var \Magento\Framework\Filesystem|\PHPUnit_Framework_MockObject_MockObject + * @var Filesystem|MockObject */ protected $filesystem; /** - * @var PropertyLocker|\PHPUnit_Framework_MockObject_MockObject + * @var PropertyLocker|MockObject */ protected $propertyLocker; + /** + * @inheritdoc + */ protected function setUp() { - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->registry = $this->createMock(\Magento\Framework\Registry::class); - $this->formFactory = $this->createMock(\Magento\Framework\Data\FormFactory::class); - $this->yesNo = $this->createMock(\Magento\Config\Model\Config\Source\Yesno::class); - $this->localeDate = $this->createMock(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class); - $this->eavData = $this->createMock(\Magento\Eav\Helper\Data::class); - $this->filesystem = $this->createMock(\Magento\Framework\Filesystem::class); + $objectManager = new ObjectManager($this); + $this->registry = $this->createMock(Registry::class); + $this->formFactory = $this->createMock(FormFactory::class); + $this->yesNo = $this->createMock(Yesno::class); + $this->localeDate = $this->createMock(TimezoneInterface::class); + $this->eavData = $this->createMock(EavHelper::class); + $this->filesystem = $this->createMock(Filesystem::class); $this->propertyLocker = $this->createMock(PropertyLocker::class); $this->block = $objectManager->getObject( - \Magento\Catalog\Block\Adminhtml\Product\Attribute\Edit\Tab\Advanced::class, + Advanced::class, [ 'registry' => $this->registry, 'formFactory' => $this->formFactory, @@ -77,17 +98,35 @@ protected function setUp() ); } + /** + * Test the block's html output + */ public function testToHtml() { - $fieldSet = $this->createMock(\Magento\Framework\Data\Form\Element\Fieldset::class); - $form = $this->createMock(\Magento\Framework\Data\Form::class); + $defaultValue = 'default_value'; + $localizedDefaultValue = 'localized_default_value'; + $frontendInput = 'datetime'; + $dateFormat = 'mm/dd/yy'; + $timeFormat = 'H:i:s:'; + $timeZone = 'America/Chicago'; + + $fieldSet = $this->createMock(Fieldset::class); + $form = $this->createMock(Form::class); $attributeModel = $this->createPartialMock( - \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class, - ['getDefaultValue', 'setDisabled', 'getId', 'getEntityType', 'getIsUserDefined', 'getAttributeCode'] + Attribute::class, + [ + 'getDefaultValue', + 'setDisabled', + 'getId', + 'getEntityType', + 'getIsUserDefined', + 'getAttributeCode', + 'getFrontendInput' + ] ); - $entityType = $this->createMock(\Magento\Eav\Model\Entity\Type::class); - $formElement = $this->createPartialMock(\Magento\Framework\Data\Form\Element\Text::class, ['setDisabled']); - $directoryReadInterface = $this->createMock(\Magento\Framework\Filesystem\Directory\ReadInterface::class); + $entityType = $this->createMock(EntityType::class); + $formElement = $this->createPartialMock(Text::class, ['setDisabled']); + $directoryReadInterface = $this->createMock(ReadInterface::class); $this->registry->expects($this->any())->method('registry')->with('entity_attribute') ->willReturn($attributeModel); @@ -95,13 +134,28 @@ public function testToHtml() $form->expects($this->any())->method('addFieldset')->willReturn($fieldSet); $form->expects($this->any())->method('getElement')->willReturn($formElement); $fieldSet->expects($this->any())->method('addField')->willReturnSelf(); - $attributeModel->expects($this->any())->method('getDefaultValue')->willReturn('default_value'); + $attributeModel->expects($this->any())->method('getDefaultValue')->willReturn($defaultValue); $attributeModel->expects($this->any())->method('setDisabled')->willReturnSelf(); $attributeModel->expects($this->any())->method('getId')->willReturn(1); $attributeModel->expects($this->any())->method('getEntityType')->willReturn($entityType); $attributeModel->expects($this->any())->method('getIsUserDefined')->willReturn(false); $attributeModel->expects($this->any())->method('getAttributeCode')->willReturn('attribute_code'); - $this->localeDate->expects($this->any())->method('getDateFormat')->willReturn('mm/dd/yy'); + $attributeModel->expects($this->any())->method('getFrontendInput')->willReturn($frontendInput); + + $dateTimeMock = $this->createMock(\DateTime::class); + $dateTimeMock->expects($this->once())->method('setTimezone')->with(new \DateTimeZone($timeZone)); + $dateTimeMock->expects($this->once()) + ->method('format') + ->with(DateTime::DATETIME_PHP_FORMAT) + ->willReturn($localizedDefaultValue); + $this->localeDate->expects($this->any())->method('getDateFormat')->willReturn($dateFormat); + $this->localeDate->expects($this->any())->method('getTimeFormat')->willReturn($timeFormat); + $this->localeDate->expects($this->once())->method('getConfigTimezone')->willReturn($timeZone); + $this->localeDate->expects($this->once()) + ->method('date') + ->with($defaultValue, null, false) + ->willReturn($dateTimeMock); + $entityType->expects($this->any())->method('getEntityTypeCode')->willReturn('entity_type_code'); $this->eavData->expects($this->any())->method('getFrontendClasses')->willReturn([]); $formElement->expects($this->exactly(2))->method('setDisabled')->willReturnSelf(); diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/MoveTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/MoveTest.php index 746d3340a6605..da58943bb3722 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/MoveTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Category/MoveTest.php @@ -134,13 +134,10 @@ public function testExecuteWithGenericException() ->willReturn($categoryMock); $this->objectManager->expects($this->any()) ->method('get') - ->withConsecutive([Registry::class], [Registry::class], [\Magento\Cms\Model\Wysiwyg\Config::class]) ->willReturnMap([[Registry::class, $registry], [\Magento\Cms\Model\Wysiwyg\Config::class, $wysiwygConfig]]); $categoryMock->expects($this->once()) ->method('move') - ->willThrowException(new \Exception( - __('Some exception') - )); + ->willThrowException(new \Exception(__('Some exception'))); $this->messageManager->expects($this->once()) ->method('addErrorMessage') ->with(__('There was a category move error.')); @@ -208,7 +205,6 @@ public function testExecuteWithLocalizedException() ->willReturn($categoryMock); $this->objectManager->expects($this->any()) ->method('get') - ->withConsecutive([Registry::class], [Registry::class], [\Magento\Cms\Model\Wysiwyg\Config::class]) ->willReturnMap([[Registry::class, $registry], [\Magento\Cms\Model\Wysiwyg\Config::class, $wysiwygConfig]]); $this->messageManager->expects($this->once()) ->method('addExceptionMessage'); @@ -236,9 +232,7 @@ public function testExecuteWithLocalizedException() ->willReturn(true); $categoryMock->expects($this->once()) ->method('move') - ->willThrowException(new \Magento\Framework\Exception\LocalizedException( - __($exceptionMessage) - )); + ->willThrowException(new \Magento\Framework\Exception\LocalizedException(__($exceptionMessage))); $this->resultJsonFactoryMock ->expects($this->once()) ->method('create') @@ -280,7 +274,6 @@ public function testSuccessfulCategorySave() ->willReturn($categoryMock); $this->objectManager->expects($this->any()) ->method('get') - ->withConsecutive([Registry::class], [Registry::class], [\Magento\Cms\Model\Wysiwyg\Config::class]) ->willReturnMap([[Registry::class, $registry], [\Magento\Cms\Model\Wysiwyg\Config::class, $wysiwygConfig]]); $this->messageManager->expects($this->once()) ->method('getMessages') diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/ValidateTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/ValidateTest.php index 742148b1bf7f1..856f8a20b9daf 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/ValidateTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/ValidateTest.php @@ -136,18 +136,22 @@ public function testExecute() $serializedOptions = '{"key":"value"}'; $this->requestMock->expects($this->any()) ->method('getParam') - ->willReturnMap([ + ->willReturnMap( + [ ['frontend_label', null, 'test_frontend_label'], ['attribute_code', null, 'test_attribute_code'], ['new_attribute_set_name', null, 'test_attribute_set_name'], ['serialized_options', '[]', $serializedOptions], - ]); + ] + ); $this->objectManagerMock->expects($this->exactly(2)) ->method('create') - ->willReturnMap([ + ->willReturnMap( + [ [\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class, [], $this->attributeMock], [\Magento\Eav\Model\Entity\Attribute\Set::class, [], $this->attributeSetMock] - ]); + ] + ); $this->attributeMock->expects($this->once()) ->method('loadByCode') ->willReturnSelf(); @@ -182,9 +186,9 @@ public function testExecute() /** * @dataProvider provideUniqueData - * @param array $options - * @param boolean $isError - * @throws \Magento\Framework\Exception\NotFoundException + * @param array $options + * @param boolean $isError + * @throws \Magento\Framework\Exception\NotFoundException */ public function testUniqueValidation(array $options, $isError) { @@ -192,13 +196,15 @@ public function testUniqueValidation(array $options, $isError) $countFunctionCalls = ($isError) ? 6 : 5; $this->requestMock->expects($this->exactly($countFunctionCalls)) ->method('getParam') - ->willReturnMap([ + ->willReturnMap( + [ ['frontend_label', null, null], ['attribute_code', null, "test_attribute_code"], ['new_attribute_set_name', null, 'test_attribute_set_name'], ['message_key', null, Validate::DEFAULT_MESSAGE_KEY], ['serialized_options', '[]', $serializedOptions], - ]); + ] + ); $this->formDataSerializerMock ->expects($this->once()) @@ -323,22 +329,24 @@ public function provideUniqueData() * Check that empty admin scope labels will trigger error. * * @dataProvider provideEmptyOption - * @param array $options - * @throws \Magento\Framework\Exception\NotFoundException + * @param array $options + * @throws \Magento\Framework\Exception\NotFoundException */ public function testEmptyOption(array $options, $result) { $serializedOptions = '{"key":"value"}'; $this->requestMock->expects($this->any()) ->method('getParam') - ->willReturnMap([ + ->willReturnMap( + [ ['frontend_label', null, null], ['frontend_input', 'select', 'multipleselect'], ['attribute_code', null, "test_attribute_code"], ['new_attribute_set_name', null, 'test_attribute_set_name'], ['message_key', Validate::DEFAULT_MESSAGE_KEY, 'message'], ['serialized_options', '[]', $serializedOptions], - ]); + ] + ); $this->formDataSerializerMock ->expects($this->once()) @@ -439,6 +447,129 @@ public function provideEmptyOption() ]; } + /** + * Check that admin scope labels which only contain spaces will trigger error. + * + * @dataProvider provideWhitespaceOption + * @param array $options + * @param $result + * @throws \Magento\Framework\Exception\NotFoundException + */ + public function testWhitespaceOption(array $options, $result) + { + $serializedOptions = '{"key":"value"}'; + $this->requestMock->expects($this->any()) + ->method('getParam') + ->willReturnMap( + [ + ['frontend_label', null, null], + ['frontend_input', 'select', 'multipleselect'], + ['attribute_code', null, "test_attribute_code"], + ['new_attribute_set_name', null, 'test_attribute_set_name'], + ['message_key', Validate::DEFAULT_MESSAGE_KEY, 'message'], + ['serialized_options', '[]', $serializedOptions], + ] + ); + + $this->formDataSerializerMock + ->expects($this->once()) + ->method('unserialize') + ->with($serializedOptions) + ->willReturn($options); + + $this->objectManagerMock->expects($this->once()) + ->method('create') + ->willReturn($this->attributeMock); + + $this->attributeMock->expects($this->once()) + ->method('loadByCode') + ->willReturnSelf(); + + $this->attributeCodeValidatorMock->expects($this->once()) + ->method('isValid') + ->with('test_attribute_code') + ->willReturn(true); + + $this->resultJsonFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->resultJson); + + $this->resultJson->expects($this->once()) + ->method('setJsonData') + ->willReturnArgument(0); + + $response = $this->getModel()->execute(); + $responseObject = json_decode($response); + $this->assertEquals($responseObject, $result); + } + + /** + * Dataprovider for testWhitespaceOption. + * + * @return array + */ + public function provideWhitespaceOption() + { + return [ + 'whitespace admin scope options' => [ + [ + 'option' => [ + 'value' => [ + "option_0" => [' '], + ], + ], + ], + (object) [ + 'error' => true, + 'message' => 'The value of Admin scope can\'t be empty.', + ] + ], + 'not empty admin scope options' => [ + [ + 'option' => [ + 'value' => [ + "option_0" => ['asdads'], + ], + ], + ], + (object) [ + 'error' => false, + ] + ], + 'whitespace admin scope options and deleted' => [ + [ + 'option' => [ + 'value' => [ + "option_0" => [' '], + ], + 'delete' => [ + 'option_0' => '1', + ], + ], + ], + (object) [ + 'error' => false, + ], + ], + 'whitespace admin scope options and not deleted' => [ + [ + 'option' => [ + 'value' => [ + "option_0" => [' '], + ], + 'delete' => [ + 'option_0' => '0', + ], + ], + ], + (object) [ + 'error' => true, + 'message' => 'The value of Admin scope can\'t be empty.', + ], + ], + ]; + } + /** * @throws \Magento\Framework\Exception\NotFoundException */ @@ -449,13 +580,15 @@ public function testExecuteWithOptionsDataError() . "If the error persists, please try again later."; $this->requestMock->expects($this->any()) ->method('getParam') - ->willReturnMap([ + ->willReturnMap( + [ ['frontend_label', null, 'test_frontend_label'], ['attribute_code', null, 'test_attribute_code'], ['new_attribute_set_name', null, 'test_attribute_set_name'], ['message_key', Validate::DEFAULT_MESSAGE_KEY, 'message'], ['serialized_options', '[]', $serializedOptions], - ]); + ] + ); $this->formDataSerializerMock ->expects($this->once()) @@ -465,10 +598,12 @@ public function testExecuteWithOptionsDataError() $this->objectManagerMock ->method('create') - ->willReturnMap([ + ->willReturnMap( + [ [\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class, [], $this->attributeMock], [\Magento\Eav\Model\Entity\Attribute\Set::class, [], $this->attributeSetMock] - ]); + ] + ); $this->attributeCodeValidatorMock ->method('isValid') @@ -485,10 +620,14 @@ public function testExecuteWithOptionsDataError() ->willReturn($this->resultJson); $this->resultJson->expects($this->once()) ->method('setJsonData') - ->with(json_encode([ - 'error' => true, - 'message' => $message - ])) + ->with( + json_encode( + [ + 'error' => true, + 'message' => $message + ] + ) + ) ->willReturnSelf(); $this->getModel()->execute(); @@ -498,23 +637,25 @@ public function testExecuteWithOptionsDataError() * Test execute with an invalid attribute code * * @dataProvider provideInvalidAttributeCodes - * @param string $attributeCode - * @param $result - * @throws \Magento\Framework\Exception\NotFoundException + * @param string $attributeCode + * @param $result + * @throws \Magento\Framework\Exception\NotFoundException */ public function testExecuteWithInvalidAttributeCode($attributeCode, $result) { $serializedOptions = '{"key":"value"}'; $this->requestMock->expects($this->any()) ->method('getParam') - ->willReturnMap([ + ->willReturnMap( + [ ['frontend_label', null, null], ['frontend_input', 'select', 'multipleselect'], ['attribute_code', null, $attributeCode], ['new_attribute_set_name', null, 'test_attribute_set_name'], ['message_key', Validate::DEFAULT_MESSAGE_KEY, 'message'], ['serialized_options', '[]', $serializedOptions], - ]); + ] + ); $this->formDataSerializerMock ->expects($this->once()) diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php index 134c6f9edeaf7..2aea34244437d 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php @@ -165,6 +165,8 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); + $this->dateTimeFilterMock = $this->createMock(\Magento\Catalog\Model\Product\Filter\DateTime::class); + $this->helper = $this->objectManager->getObject( Helper::class, [ @@ -178,6 +180,7 @@ protected function setUp() 'linkTypeProvider' => $this->linkTypeProviderMock, 'attributeFilter' => $this->attributeFilterMock, 'localeFormat' => $this->localeFormatMock, + 'dateTimeFilter' => $this->dateTimeFilterMock ] ); @@ -188,11 +191,6 @@ protected function setUp() $resolverProperty = $helperReflection->getProperty('linkResolver'); $resolverProperty->setAccessible(true); $resolverProperty->setValue($this->helper, $this->linkResolverMock); - - $this->dateTimeFilterMock = $this->createMock(\Magento\Framework\Stdlib\DateTime\Filter\DateTime::class); - $dateTimeFilterProperty = $helperReflection->getProperty('dateTimeFilter'); - $dateTimeFilterProperty->setAccessible(true); - $dateTimeFilterProperty->setValue($this->helper, $this->dateTimeFilterMock); } /** @@ -226,6 +224,7 @@ public function testInitialize( ]; $specialFromDate = '2018-03-03 19:30:00'; $productData = [ + 'name' => 'Simple Product', 'stock_data' => ['stock_data'], 'options' => $optionsData, 'website_ids' => $websiteIds, @@ -235,30 +234,23 @@ public function testInitialize( $productData = array_merge($productData, ['tier_price' => $tierPrice]); } - $this->dateTimeFilterMock->expects($this->once()) + $this->dateTimeFilterMock + ->expects($this->once()) ->method('filter') - ->with($specialFromDate) - ->willReturn($specialFromDate); - - $attributeNonDate = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) - ->disableOriginalConstructor() - ->getMock(); - $attributeDate = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) - ->disableOriginalConstructor() - ->getMock(); - - $attributeNonDateBackEnd = - $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Backend\DefaultBackend::class) - ->disableOriginalConstructor() - ->getMock(); - $attributeDateBackEnd = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Backend\Datetime::class) - ->disableOriginalConstructor() - ->getMock(); + ->willReturnArgument(0); - $attributeNonDate->expects($this->any())->method('getBackend')->willReturn($attributeNonDateBackEnd); - $attributeDate->expects($this->any())->method('getBackend')->willReturn($attributeDateBackEnd); - $attributeNonDateBackEnd->expects($this->any())->method('getType')->willReturn('non-datetime'); - $attributeDateBackEnd->expects($this->any())->method('getType')->willReturn('datetime'); + $this->setProductAttributes( + [ + [ + 'code' => 'name', + 'backend_type' => 'varchar', + ], + [ + 'code' => 'special_from_date', + 'backend_type' => 'datetime', + ] + ] + ); $useDefaults = ['attributeCode1', 'attributeCode2']; @@ -274,8 +266,6 @@ public function testInitialize( $this->productMock->expects($this->once())->method('isLockedAttribute')->with('media')->willReturn(true); $this->productMock->expects($this->once())->method('unlockAttribute')->with('media'); $this->productMock->expects($this->once())->method('lockAttribute')->with('media'); - $this->productMock->expects($this->once())->method('getAttributes') - ->willReturn([$attributeNonDate, $attributeDate]); $this->productMock->expects($this->any())->method('getSku')->willReturn('sku'); $this->productMock->expects($this->any())->method('getOptionsReadOnly')->willReturn(false); @@ -348,7 +338,35 @@ function () { } $this->assertEquals($expectedLinks, $resultLinks); - $this->assertEquals($specialFromDate, $productData['special_from_date']); + $this->assertEquals($specialFromDate, $this->productMock->getSpecialFromDate()); + } + + /** + * Mock product attributes + * + * @param array $attributes + */ + private function setProductAttributes(array $attributes): void + { + $attributesModels = []; + foreach ($attributes as $attribute) { + $attributeModel = $this->createMock(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class); + $backendModel = $attribute['backend_model'] + ?? $this->createMock(\Magento\Eav\Model\Entity\Attribute\Backend\DefaultBackend::class); + $attributeModel->expects($this->any()) + ->method('getBackend') + ->willReturn($backendModel); + $attributeModel->expects($this->any()) + ->method('getAttributeCode') + ->willReturn($attribute['code']); + $backendModel->expects($this->any()) + ->method('getType') + ->willReturn($attribute['backend_type']); + $attributesModels[$attribute['code']] = $attributeModel; + } + $this->productMock->expects($this->once()) + ->method('getAttributes') + ->willReturn($attributesModels); } /** diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/CustomlayoutupdateTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/CustomlayoutupdateTest.php deleted file mode 100644 index 01fad60609c29..0000000000000 --- a/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/CustomlayoutupdateTest.php +++ /dev/null @@ -1,137 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Catalog\Test\Unit\Model\Attribute\Backend; - -use Magento\Framework\DataObject; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; - -class CustomlayoutupdateTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var string - */ - private $attributeName = 'private'; - - /** - * @var \Magento\Catalog\Model\Attribute\Backend\Customlayoutupdate - */ - private $model; - - /** - * @expectedException \Magento\Eav\Model\Entity\Attribute\Exception - */ - public function testValidateException() - { - $object = new DataObject(); - $object->setData($this->attributeName, 'exception'); - $this->model->validate($object); - } - - /** - * @param string - * @dataProvider validateProvider - */ - public function testValidate($data) - { - $object = new DataObject(); - $object->setData($this->attributeName, $data); - - $this->assertTrue($this->model->validate($object)); - $this->assertTrue($this->model->validate($object)); - } - - /** - * @return array - */ - public function validateProvider() - { - return [[''], ['xml']]; - } - - protected function setUp() - { - $helper = new ObjectManager($this); - $this->model = $helper->getObject( - \Magento\Catalog\Model\Attribute\Backend\Customlayoutupdate::class, - [ - 'layoutUpdateValidatorFactory' => $this->getMockedLayoutUpdateValidatorFactory() - ] - ); - $this->model->setAttribute($this->getMockedAttribute()); - } - - /** - * @return \Magento\Framework\View\Model\Layout\Update\ValidatorFactory - */ - private function getMockedLayoutUpdateValidatorFactory() - { - $mockBuilder = $this->getMockBuilder(\Magento\Framework\View\Model\Layout\Update\ValidatorFactory::class); - $mockBuilder->disableOriginalConstructor(); - $mockBuilder->setMethods(['create']); - $mock = $mockBuilder->getMock(); - - $mock->expects($this->any()) - ->method('create') - ->will($this->returnValue($this->getMockedValidator())); - - return $mock; - } - - /** - * @return \Magento\Framework\View\Model\Layout\Update\Validator - */ - private function getMockedValidator() - { - $mockBuilder = $this->getMockBuilder(\Magento\Framework\View\Model\Layout\Update\Validator::class); - $mockBuilder->disableOriginalConstructor(); - $mock = $mockBuilder->getMock(); - - $mock->expects($this->any()) - ->method('isValid') - ->will( - /** - * @param string $xml - * $return bool - */ - $this->returnCallback( - function ($xml) { - if ($xml == 'exception') { - return false; - } else { - return true; - } - } - ) - ); - - $mock->expects($this->any()) - ->method('getMessages') - ->will($this->returnValue(['error'])); - - return $mock; - } - - /** - * @return \Magento\Eav\Model\Entity\Attribute\AbstractAttribute - */ - private function getMockedAttribute() - { - $mockBuilder = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class); - $mockBuilder->disableOriginalConstructor(); - $mock = $mockBuilder->getMock(); - - $mock->expects($this->any()) - ->method('getName') - ->will($this->returnValue($this->attributeName)); - - $mock->expects($this->any()) - ->method('getIsRequired') - ->will($this->returnValue(false)); - - return $mock; - } -} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/TierPrice/UpdateHandlerTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/TierPrice/UpdateHandlerTest.php index cce00c50d37af..fde793d5c5f89 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/TierPrice/UpdateHandlerTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Attribute/Backend/TierPrice/UpdateHandlerTest.php @@ -108,6 +108,7 @@ public function testExecute(): void ]; $linkField = 'entity_id'; $productId = 10; + $originalProductId = 11; /** @var \PHPUnit_Framework_MockObject_MockObject $product */ $product = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class) @@ -124,7 +125,7 @@ public function testExecute(): void ->willReturnMap( [ ['tier_price', $originalTierPrices], - ['entity_id', $productId] + ['entity_id', $originalProductId] ] ); $product->expects($this->atLeastOnce())->method('getStoreId')->willReturn(0); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php index a76ae5244076f..f8a89f9d9fd90 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/Attribute/Backend/ImageTest.php @@ -3,10 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Test\Unit\Model\Category\Attribute\Backend; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Exception\FileSystemException; use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; /** * Test for Magento\Catalog\Model\Category\Attribute\Backend\Image class. @@ -39,6 +43,16 @@ class ImageTest extends \PHPUnit\Framework\TestCase */ private $filesystem; + /** + * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject; + */ + private $storeManagerInterfaceMock; + + /** + * @var Store|\PHPUnit_Framework_MockObject_MockObject + */ + private $storeMock; + /** * @inheritdoc */ @@ -56,10 +70,6 @@ protected function setUp() ['getName'] ); - $this->attribute->expects($this->once()) - ->method('getName') - ->will($this->returnValue('test_attribute')); - $this->logger = $this->getMockForAbstractClass( \Psr\Log\LoggerInterface::class, [], @@ -75,6 +85,14 @@ protected function setUp() ['moveFileFromTmp', 'getBasePath'] ); + $this->storeManagerInterfaceMock = $this->getMockBuilder( + StoreManagerInterface::class + )->disableOriginalConstructor()->getMock(); + + $this->storeMock = $this->getMockBuilder( + Store::class + )->disableOriginalConstructor()->getMock(); + $this->filesystem = $this->getMockBuilder(\Magento\Framework\Filesystem::class)->disableOriginalConstructor() ->getMock(); } @@ -97,6 +115,10 @@ public function deletedValueDataProvider() */ public function testBeforeSaveValueDeletion($value) { + $this->attribute->expects($this->once()) + ->method('getName') + ->will($this->returnValue('test_attribute')); + $model = $this->objectManager->getObject(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class); $model->setAttribute($this->attribute); @@ -132,6 +154,10 @@ public function invalidValueDataProvider() */ public function testBeforeSaveValueInvalid($value) { + $this->attribute->expects($this->once()) + ->method('getName') + ->will($this->returnValue('test_attribute')); + $model = $this->objectManager->getObject(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class); $model->setAttribute($this->attribute); @@ -147,7 +173,11 @@ public function testBeforeSaveValueInvalid($value) */ public function testBeforeSaveAttributeFileName() { - $model = $this->setUpModelForAfterSave(); + $this->attribute->expects($this->once()) + ->method('getName') + ->will($this->returnValue('test_attribute')); + + $model = $this->setUpModelForTests(); $mediaDirectoryMock = $this->createMock(WriteInterface::class); $this->filesystem->expects($this->once()) ->method('getDirectoryWrite') @@ -177,7 +207,11 @@ public function testBeforeSaveAttributeFileName() */ public function testBeforeSaveAttributeFileNameOutsideOfCategoryDir() { - $model = $this->setUpModelForAfterSave(); + $this->attribute->expects($this->once()) + ->method('getName') + ->will($this->returnValue('test_attribute')); + + $model = $this->setUpModelForTests(); $model->setAttribute($this->attribute); $imagePath = '/pub/media/wysiwyg/test123.jpg'; $this->filesystem @@ -211,7 +245,19 @@ public function testBeforeSaveAttributeFileNameOutsideOfCategoryDir() */ public function testBeforeSaveTemporaryAttribute() { - $model = $this->setUpModelForAfterSave(); + $this->attribute->expects($this->once()) + ->method('getName') + ->will($this->returnValue('test_attribute')); + + $this->storeManagerInterfaceMock->expects($this->once()) + ->method('getStore') + ->willReturn($this->storeMock); + + $this->storeMock->expects($this->once()) + ->method('getBaseMediaDir') + ->willReturn('pub/media'); + + $model = $this->setUpModelForTests(); $model->setAttribute($this->attribute); $mediaDirectoryMock = $this->createMock(WriteInterface::class); @@ -220,10 +266,16 @@ public function testBeforeSaveTemporaryAttribute() ->with(DirectoryList::MEDIA) ->willReturn($mediaDirectoryMock); + $this->imageUploader->expects($this->any())->method('moveFileFromTmp')->willReturn('test123.jpg'); + $object = new \Magento\Framework\DataObject( [ 'test_attribute' => [ - ['name' => 'test123.jpg', 'tmp_name' => 'abc123', 'url' => 'http://www.example.com/test123.jpg'], + [ + 'name' => 'test123.jpg', + 'tmp_name' => 'abc123', + 'url' => 'http://www.example.com/pub/media/temp/test123.jpg' + ], ], ] ); @@ -232,7 +284,7 @@ public function testBeforeSaveTemporaryAttribute() $this->assertEquals( [ - ['name' => 'test123.jpg', 'tmp_name' => 'abc123', 'url' => 'http://www.example.com/test123.jpg'], + ['name' => '/pub/media/test123.jpg', 'tmp_name' => 'abc123', 'url' => '/pub/media/test123.jpg'], ], $object->getData('_additional_data_test_attribute') ); @@ -257,7 +309,7 @@ public function testBeforeSaveAttributeStringValue() /** * @return \Magento\Catalog\Model\Category\Attribute\Backend\Image */ - private function setUpModelForAfterSave() + private function setUpModelForTests() { $objectManagerMock = $this->createPartialMock(\Magento\Framework\App\ObjectManager::class, ['get']); @@ -268,7 +320,7 @@ private function setUpModelForAfterSave() ->will( $this->returnCallback( function ($class, $params = []) use ($imageUploaderMock) { - if ($class == \Magento\Catalog\CategoryImageUpload::class) { + if ($class == "\Magento\Catalog\CategoryImageUpload") { return $imageUploaderMock; } @@ -283,6 +335,7 @@ function ($class, $params = []) use ($imageUploaderMock) { 'objectManager' => $objectManagerMock, 'logger' => $this->logger, 'filesystem' => $this->filesystem, + 'storeManager' => $this->storeManagerInterfaceMock ] ); $this->objectManager->setBackwardCompatibleProperty($model, 'imageUploader', $this->imageUploader); @@ -307,12 +360,13 @@ public function attributeValueDataProvider() * @dataProvider attributeValueDataProvider * * @param array $value + * @throws FileSystemException */ - public function testAfterSaveWithAdditionalData($value) + public function testBeforeSaveWithAdditionalData($value) { - $model = $this->setUpModelForAfterSave(); + $model = $this->setUpModelForTests(); - $this->imageUploader->expects($this->once()) + $this->imageUploader->expects($this->never()) ->method('moveFileFromTmp') ->with($this->equalTo('test1234.jpg')); @@ -323,17 +377,18 @@ public function testAfterSaveWithAdditionalData($value) ] ); - $model->afterSave($object); + $model->beforeSave($object); } /** * @dataProvider attributeValueDataProvider * * @param array $value + * @throws FileSystemException */ - public function testAfterSaveWithoutAdditionalData($value) + public function testBeforeSaveWithoutAdditionalData($value) { - $model = $this->setUpModelForAfterSave(); + $model = $this->setUpModelForTests(); $this->imageUploader->expects($this->never()) ->method('moveFileFromTmp'); @@ -344,15 +399,38 @@ public function testAfterSaveWithoutAdditionalData($value) ] ); - $model->afterSave($object); + $model->beforeSave($object); } /** * Test afterSaveWithExceptions. */ - public function testAfterSaveWithExceptions() + public function testBeforeSaveWithExceptions() { - $model = $this->setUpModelForAfterSave(); + $model = $this->setUpModelForTests(); + + $this->storeManagerInterfaceMock->expects($this->once()) + ->method('getStore') + ->willReturn($this->storeMock); + + $this->storeMock->expects($this->once()) + ->method('getBaseMediaDir') + ->willReturn('pub/media'); + + $this->attribute->expects($this->once()) + ->method('getName') + ->will($this->returnValue('_additional_data_test_attribute')); + + $mediaDirectoryMock = $this->createMock(WriteInterface::class); + $this->filesystem->expects($this->any()) + ->method('getDirectoryWrite') + ->with(DirectoryList::MEDIA) + ->willReturn($mediaDirectoryMock); + $this->imageUploader->expects($this->any())->method('getBasePath')->willReturn('base/path'); + $mediaDirectoryMock->expects($this->any()) + ->method('getAbsolutePath') + ->with('base/path/test1234.jpg') + ->willReturn('absolute/path/base/path/test1234.jpg'); $exception = new \Exception(); @@ -370,6 +448,6 @@ public function testAfterSaveWithExceptions() ] ); - $model->afterSave($object); + $model->beforeSave($object); } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php index 4c3450d555f1d..4ce50537f27bd 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php @@ -3,87 +3,101 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Test\Unit\Model\Category; +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Category\Attribute\Backend\Image; use Magento\Catalog\Model\Category\DataProvider; use Magento\Catalog\Model\Category\FileInfo; use Magento\Catalog\Model\CategoryFactory; use Magento\Catalog\Model\ResourceModel\Category\Collection; use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; use Magento\Eav\Model\Config; use Magento\Eav\Model\Entity\Type; use Magento\Framework\App\RequestInterface; +use Magento\Framework\AuthorizationInterface; use Magento\Framework\Registry; +use Magento\Framework\Stdlib\ArrayUtils; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; use Magento\Ui\DataProvider\EavValidationRules; use Magento\Ui\DataProvider\Modifier\PoolInterface; -use Magento\Framework\Stdlib\ArrayUtils; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class DataProviderTest extends \PHPUnit\Framework\TestCase +class DataProviderTest extends TestCase { /** - * @var EavValidationRules|\PHPUnit_Framework_MockObject_MockObject + * @var EavValidationRules|MockObject */ private $eavValidationRules; /** - * @var CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CollectionFactory|MockObject */ private $categoryCollectionFactory; /** - * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|MockObject */ private $storeManager; /** - * @var Registry|\PHPUnit_Framework_MockObject_MockObject + * @var Registry|MockObject */ private $registry; /** - * @var Config|\PHPUnit_Framework_MockObject_MockObject + * @var Config|MockObject */ private $eavConfig; /** - * @var RequestInterface|\PHPUnit_Framework_MockObject_MockObject + * @var RequestInterface|MockObject */ private $request; /** - * @var CategoryFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CategoryFactory|MockObject */ private $categoryFactory; /** - * @var Collection|\PHPUnit_Framework_MockObject_MockObject + * @var Collection|MockObject */ private $collection; /** - * @var Type|\PHPUnit_Framework_MockObject_MockObject + * @var Type|MockObject */ private $eavEntityMock; /** - * @var FileInfo|\PHPUnit_Framework_MockObject_MockObject + * @var FileInfo|MockObject */ private $fileInfo; /** - * @var PoolInterface|\PHPUnit_Framework_MockObject_MockObject + * @var PoolInterface|MockObject */ private $modifierPool; /** - * @var ArrayUtils|\PHPUnit_Framework_MockObject_MockObject + * @var ArrayUtils|MockObject */ private $arrayUtils; + /** + * @var AuthorizationInterface|MockObject + */ + private $auth; + /** * @inheritDoc */ @@ -96,8 +110,7 @@ protected function setUp() $this->collection = $this->getMockBuilder(Collection::class) ->disableOriginalConstructor() ->getMock(); - $this->collection->expects($this->any()) - ->method('addAttributeToSelect') + $this->collection->method('addAttributeToSelect') ->with('*') ->willReturnSelf(); @@ -105,8 +118,7 @@ protected function setUp() ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->categoryCollectionFactory->expects($this->any()) - ->method('create') + $this->categoryCollectionFactory->method('create') ->willReturn($this->collection); $this->storeManager = $this->getMockBuilder(StoreManagerInterface::class) @@ -138,6 +150,8 @@ protected function setUp() $this->modifierPool = $this->getMockBuilder(PoolInterface::class)->getMockForAbstractClass(); + $this->auth = $this->getMockBuilder(AuthorizationInterface::class)->getMockForAbstractClass(); + $this->arrayUtils = $this->getMockBuilder(ArrayUtils::class) ->setMethods(['flatten']) ->disableOriginalConstructor()->getMock(); @@ -152,12 +166,11 @@ private function getModel() ->method('getAttributeCollection') ->willReturn([]); - $this->eavConfig->expects($this->any()) - ->method('getEntityType') + $this->eavConfig->method('getEntityType') ->with('catalog_category') ->willReturn($this->eavEntityMock); - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManager = new ObjectManager($this); /** @var DataProvider $model */ $model = $objectManager->getObject( @@ -171,6 +184,7 @@ private function getModel() 'request' => $this->request, 'categoryFactory' => $this->categoryFactory, 'pool' => $this->modifierPool, + 'auth' => $this->auth, 'arrayUtils' => $this->arrayUtils ] ); @@ -204,18 +218,17 @@ public function testGetDataNoFileExists() 'image' => $fileName, ]; - $imageBackendMock = $this->getMockBuilder(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class) - ->disableOriginalConstructor() + $imageBackendMock = $this->getMockBuilder(Image::class)->disableOriginalConstructor() ->getMock(); - $attributeMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) + $attributeMock = $this->getMockBuilder(Attribute::class) ->disableOriginalConstructor() ->getMock(); $attributeMock->expects($this->once()) ->method('getBackend') ->willReturn($imageBackendMock); - $categoryMock = $this->getMockBuilder(\Magento\Catalog\Model\Category::class) + $categoryMock = $this->getMockBuilder(Category::class) ->disableOriginalConstructor() ->getMock(); $categoryMock->expects($this->exactly(2)) @@ -226,13 +239,11 @@ public function testGetDataNoFileExists() ['image', null, $categoryData['image']], ] ); - $categoryMock->expects($this->any()) - ->method('getExistsStoreValueFlag') + $categoryMock->method('getExistsStoreValueFlag') ->with('url_key') ->willReturn(false); - $categoryMock->expects($this->any()) - ->method('getStoreId') - ->willReturn(\Magento\Store\Model\Store::DEFAULT_STORE_ID); + $categoryMock->method('getStoreId') + ->willReturn(Store::DEFAULT_STORE_ID); $categoryMock->expects($this->once()) ->method('getId') ->willReturn($categoryId); @@ -253,7 +264,7 @@ public function testGetDataNoFileExists() $model = $this->getModel(); $result = $model->getData(); - $this->assertTrue(is_array($result)); + $this->assertInternalType('array', $result); $this->assertArrayHasKey($categoryId, $result); $this->assertArrayNotHasKey('image', $result[$categoryId]); } @@ -280,18 +291,18 @@ public function testGetData() ], ]; - $imageBackendMock = $this->getMockBuilder(\Magento\Catalog\Model\Category\Attribute\Backend\Image::class) + $imageBackendMock = $this->getMockBuilder(Image::class) ->disableOriginalConstructor() ->getMock(); - $attributeMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) + $attributeMock = $this->getMockBuilder(Attribute::class) ->disableOriginalConstructor() ->getMock(); $attributeMock->expects($this->once()) ->method('getBackend') ->willReturn($imageBackendMock); - $categoryMock = $this->getMockBuilder(\Magento\Catalog\Model\Category::class) + $categoryMock = $this->getMockBuilder(Category::class) ->disableOriginalConstructor() ->getMock(); $categoryMock->expects($this->exactly(2)) @@ -302,13 +313,11 @@ public function testGetData() ['image', null, $categoryData['image']], ] ); - $categoryMock->expects($this->any()) - ->method('getExistsStoreValueFlag') + $categoryMock->method('getExistsStoreValueFlag') ->with('url_key') ->willReturn(false); - $categoryMock->expects($this->any()) - ->method('getStoreId') - ->willReturn(\Magento\Store\Model\Store::DEFAULT_STORE_ID); + $categoryMock->method('getStoreId') + ->willReturn(Store::DEFAULT_STORE_ID); $categoryMock->expects($this->once()) ->method('getId') ->willReturn($categoryId); @@ -340,7 +349,7 @@ public function testGetData() $model = $this->getModel(); $result = $model->getData(); - $this->assertTrue(is_array($result)); + $this->assertInternalType('array', $result); $this->assertArrayHasKey($categoryId, $result); $this->assertArrayHasKey('image', $result[$categoryId]); @@ -351,14 +360,14 @@ public function testGetMetaWithoutParentInheritanceResolving() { $this->arrayUtils->expects($this->atLeastOnce())->method('flatten')->willReturn([1,3,3]); - $categoryMock = $this->getMockBuilder(\Magento\Catalog\Model\Category::class) + $categoryMock = $this->getMockBuilder(Category::class) ->disableOriginalConstructor() ->getMock(); $this->registry->expects($this->atLeastOnce()) ->method('registry') ->with('category') ->willReturn($categoryMock); - $attributeMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class) + $attributeMock = $this->getMockBuilder(Attribute::class) ->disableOriginalConstructor() ->getMock(); $categoryMock->expects($this->once()) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CollectionProviderTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CollectionProviderTest.php index f0e17c7938b27..09fbdf293ffc9 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/CollectionProviderTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/CollectionProviderTest.php @@ -95,11 +95,11 @@ public function testGetCollection() ); $expectedResult = [ - 0 => ['name' => 'Product Four', 'position' => 0], - 1 => ['name' => 'Product Five', 'position' => 0], - 2 => ['name' => 'Product Three', 'position' => 2], - 3 => ['name' => 'Product Two', 'position' => 2], - 4 => ['name' => 'Product One', 'position' => 10], + 0 => ['name' => 'Product Four', 'position' => 0, 'link_type' => 'crosssell'], + 1 => ['name' => 'Product Five', 'position' => 0, 'link_type' => 'crosssell'], + 2 => ['name' => 'Product Three', 'position' => 2, 'link_type' => 'crosssell'], + 3 => ['name' => 'Product Two', 'position' => 2, 'link_type' => 'crosssell'], + 4 => ['name' => 'Product One', 'position' => 10, 'link_type' => 'crosssell'], ]; $actualResult = $this->model->getCollection($this->productMock, 'crosssell'); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/ProductTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/ProductTest.php index 7a7c11e95d9b7..f7dde23e1510e 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/ProductTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Category/ProductTest.php @@ -84,15 +84,13 @@ public function testExecuteWithIndexerWorking() { $ids = [1, 2, 3]; - $this->indexerMock->expects($this->once())->method('isWorking')->will($this->returnValue(true)); $this->prepareIndexer(); $rowMock = $this->createPartialMock( \Magento\Catalog\Model\Indexer\Category\Product\Action\Rows::class, ['execute'] ); - $rowMock->expects($this->at(0))->method('execute')->with($ids, true)->will($this->returnSelf()); - $rowMock->expects($this->at(1))->method('execute')->with($ids, false)->will($this->returnSelf()); + $rowMock->expects($this->at(0))->method('execute')->with($ids)->will($this->returnSelf()); $this->rowsMock->expects($this->once())->method('create')->will($this->returnValue($rowMock)); @@ -103,14 +101,13 @@ public function testExecuteWithIndexerNotWorking() { $ids = [1, 2, 3]; - $this->indexerMock->expects($this->once())->method('isWorking')->will($this->returnValue(false)); $this->prepareIndexer(); $rowMock = $this->createPartialMock( \Magento\Catalog\Model\Indexer\Category\Product\Action\Rows::class, ['execute'] ); - $rowMock->expects($this->once())->method('execute')->with($ids, false)->will($this->returnSelf()); + $rowMock->expects($this->once())->method('execute')->with($ids)->will($this->returnSelf()); $this->rowsMock->expects($this->once())->method('create')->will($this->returnValue($rowMock)); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/CategoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/CategoryTest.php index 74f748ef9bf00..4e6659f85f5df 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/CategoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/CategoryTest.php @@ -84,15 +84,13 @@ public function testExecuteWithIndexerWorking() { $ids = [1, 2, 3]; - $this->indexerMock->expects($this->once())->method('isWorking')->will($this->returnValue(true)); $this->prepareIndexer(); $rowMock = $this->createPartialMock( \Magento\Catalog\Model\Indexer\Product\Category\Action\Rows::class, ['execute'] ); - $rowMock->expects($this->at(0))->method('execute')->with($ids, true)->will($this->returnSelf()); - $rowMock->expects($this->at(1))->method('execute')->with($ids, false)->will($this->returnSelf()); + $rowMock->expects($this->at(0))->method('execute')->with($ids)->will($this->returnSelf()); $this->rowsMock->expects($this->once())->method('create')->will($this->returnValue($rowMock)); @@ -103,14 +101,13 @@ public function testExecuteWithIndexerNotWorking() { $ids = [1, 2, 3]; - $this->indexerMock->expects($this->once())->method('isWorking')->will($this->returnValue(false)); $this->prepareIndexer(); $rowMock = $this->createPartialMock( \Magento\Catalog\Model\Indexer\Product\Category\Action\Rows::class, ['execute'] ); - $rowMock->expects($this->once())->method('execute')->with($ids, false)->will($this->returnSelf()); + $rowMock->expects($this->once())->method('execute')->with($ids)->will($this->returnSelf()); $this->rowsMock->expects($this->once())->method('create')->will($this->returnValue($rowMock)); @@ -123,7 +120,7 @@ public function testExecuteWithIndexerNotWorking() protected function prepareIndexer() { - $this->indexerRegistryMock->expects($this->once()) + $this->indexerRegistryMock->expects($this->any()) ->method('get') ->with(\Magento\Catalog\Model\Indexer\Product\Category::INDEXER_ID) ->will($this->returnValue($this->indexerMock)); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Layer/Filter/DataProvider/PriceTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Layer/Filter/DataProvider/PriceTest.php index 8ca23df31cdee..c59aa1988be55 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Layer/Filter/DataProvider/PriceTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Layer/Filter/DataProvider/PriceTest.php @@ -178,6 +178,7 @@ public function validateFilterDataProvider() ['filter' => '0', 'result' => false], ['filter' => 0, 'result' => false], ['filter' => '100500INF', 'result' => false], + ['filter' => '-10\'[0]', 'result' => false], ]; } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/CopierTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/CopierTest.php index 80b6db2a516bd..809fa0225278c 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/CopierTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/CopierTest.php @@ -6,6 +6,7 @@ namespace Magento\Catalog\Test\Unit\Model\Product; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Attribute\ScopeOverriddenValue; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Copier; @@ -46,6 +47,11 @@ class CopierTest extends \PHPUnit\Framework\TestCase */ protected $metadata; + /** + * @var ScopeOverriddenValue|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeOverriddenValue; + protected function setUp() { $this->copyConstructorMock = $this->createMock(\Magento\Catalog\Model\Product\CopyConstructorInterface::class); @@ -59,6 +65,7 @@ protected function setUp() $this->optionRepositoryMock; $this->productMock = $this->createMock(Product::class); $this->productMock->expects($this->any())->method('getEntityId')->willReturn(1); + $this->scopeOverriddenValue = $this->createMock(ScopeOverriddenValue::class); $this->metadata = $this->getMockBuilder(\Magento\Framework\EntityManager\EntityMetadata::class) ->disableOriginalConstructor() @@ -67,15 +74,20 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); $metadataPool->expects($this->any())->method('getMetadata')->willReturn($this->metadata); + $this->_model = new Copier( $this->copyConstructorMock, - $this->productFactoryMock + $this->productFactoryMock, + $this->scopeOverriddenValue ); - $this->setProperties($this->_model, [ - 'optionRepository' => $this->optionRepositoryMock, - 'metadataPool' => $metadataPool, - ]); + $this->setProperties( + $this->_model, + [ + 'optionRepository' => $this->optionRepositoryMock, + 'metadataPool' => $metadataPool, + ] + ); } /** @@ -103,10 +115,12 @@ public function testCopy() ]; $this->productMock->expects($this->atLeastOnce())->method('getWebsiteIds'); $this->productMock->expects($this->atLeastOnce())->method('getCategoryIds'); - $this->productMock->expects($this->any())->method('getData')->willReturnMap([ - ['', null, $productData], - ['linkField', null, '1'], - ]); + $this->productMock->expects($this->any())->method('getData')->willReturnMap( + [ + ['', null, $productData], + ['linkField', null, '1'], + ] + ); $entityMock = $this->getMockForAbstractClass( \Magento\Eav\Model\Entity\AbstractEntity::class, @@ -191,9 +205,11 @@ public function testCopy() $this->metadata->expects($this->any())->method('getLinkField')->willReturn('linkField'); - $duplicateMock->expects($this->any())->method('getData')->willReturnMap([ - ['linkField', null, '2'], - ]); + $duplicateMock->expects($this->any())->method('getData')->willReturnMap( + [ + ['linkField', null, '2'], + ] + ); $this->optionRepositoryMock->expects($this->once()) ->method('duplicate') ->with($this->productMock, $duplicateMock); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Filter/DateTimeTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Filter/DateTimeTest.php new file mode 100644 index 0000000000000..aefa0b1cf106d --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Filter/DateTimeTest.php @@ -0,0 +1,91 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Test\Unit\Model\Product\Filter; + +use Magento\Framework\Locale\Resolver; +use Magento\Framework\Locale\ResolverInterface; +use Magento\Framework\Stdlib\DateTime\Timezone; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Test datetime filter + */ +class DateTimeTest extends TestCase +{ + /** + * @var string + */ + private $locale; + /** + * @var \Magento\Catalog\Model\Product\Filter\DateTime + */ + private $model; + + /** + * @inheritDoc + */ + protected function setUp() + { + parent::setUp(); + $objectManager = new ObjectManager($this); + $this->locale = Resolver::DEFAULT_LOCALE; + $localeResolver = $this->getMockForAbstractClass(ResolverInterface::class); + $localeResolver->expects($this->any()) + ->method('getLocale') + ->willReturnCallback( + function () { + return $this->locale; + } + ); + $timezone = $objectManager->getObject( + Timezone::class, + ['localeResolver' => $localeResolver] + ); + $stdlibDateTimeFilter = $objectManager->getObject( + \Magento\Framework\Stdlib\DateTime\Filter\DateTime::class, + ['localeDate' => $timezone] + ); + $this->model = $objectManager->getObject( + \Magento\Catalog\Model\Product\Filter\DateTime::class, + [ + 'stdlibDateTimeFilter' => $stdlibDateTimeFilter + ] + ); + } + + /** + * Test filter with different dates formats and locales + * + * @dataProvider provideFilter + */ + public function testFilter(string $date, string $expectedDate, string $locale = Resolver::DEFAULT_LOCALE) + { + $this->locale = $locale; + $this->assertEquals($expectedDate, $this->model->filter($date)); + } + + /** + * Provide date formats and locales + * + * @return array + */ + public function provideFilter(): array + { + return [ + ['1999-12-31', '1999-12-31 00:00:00', 'en_US'], + ['12-31-1999', '1999-12-31 00:00:00', 'en_US'], + ['12/31/1999', '1999-12-31 00:00:00', 'en_US'], + ['December 31, 1999', '1999-12-31 00:00:00', 'en_US'], + ['1999-12-31', '1999-12-31 00:00:00', 'fr_FR'], + ['31-12-1999', '1999-12-31 00:00:00', 'fr_FR'], + ['31/12/1999', '1999-12-31 00:00:00', 'fr_FR'], + ['31 Décembre 1999', '1999-12-31 00:00:00', 'fr_FR'], + ]; + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/GalleryManagementTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/GalleryManagementTest.php index 1d12645019d1e..6d4e98b60ad18 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/GalleryManagementTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Gallery/GalleryManagementTest.php @@ -7,6 +7,9 @@ namespace Magento\Catalog\Test\Unit\Model\Product\Gallery; +/** + * Tests for \Magento\Catalog\Model\Product\Gallery\GalleryManagement. + */ class GalleryManagementTest extends \PHPUnit\Framework\TestCase { /** @@ -39,11 +42,16 @@ class GalleryManagementTest extends \PHPUnit\Framework\TestCase */ protected $attributeValueMock; + /** + * @inheritdoc + */ protected function setUp() { $this->productRepositoryMock = $this->createMock(\Magento\Catalog\Api\ProductRepositoryInterface::class); $this->contentValidatorMock = $this->createMock(\Magento\Framework\Api\ImageContentValidatorInterface::class); - $this->productMock = $this->createPartialMock(\Magento\Catalog\Model\Product::class, [ + $this->productMock = $this->createPartialMock( + \Magento\Catalog\Model\Product::class, + [ 'setStoreId', 'getData', 'getStoreId', @@ -51,7 +59,8 @@ protected function setUp() 'getCustomAttribute', 'getMediaGalleryEntries', 'setMediaGalleryEntries', - ]); + ] + ); $this->mediaGalleryEntryMock = $this->createMock(\Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterface::class); $this->model = new \Magento\Catalog\Model\Product\Gallery\GalleryManagement( @@ -151,6 +160,8 @@ public function testUpdateWithNonExistingImage() $existingEntryMock->expects($this->once())->method('getId')->willReturn(43); $this->productMock->expects($this->once())->method('getMediaGalleryEntries') ->willReturn([$existingEntryMock]); + $existingEntryMock->expects($this->once())->method('getTypes')->willReturn([]); + $entryMock->expects($this->once())->method('getTypes')->willReturn([]); $entryMock->expects($this->once())->method('getId')->willReturn($entryId); $this->model->update($productSku, $entryMock); } @@ -172,12 +183,19 @@ public function testUpdateWithCannotSaveException() $existingEntryMock->expects($this->once())->method('getId')->willReturn($entryId); $this->productMock->expects($this->once())->method('getMediaGalleryEntries') ->willReturn([$existingEntryMock]); + $existingEntryMock->expects($this->once())->method('getTypes')->willReturn([]); + $entryMock->expects($this->once())->method('getTypes')->willReturn([]); $entryMock->expects($this->once())->method('getId')->willReturn($entryId); $this->productRepositoryMock->expects($this->once())->method('save')->with($this->productMock) ->willThrowException(new \Exception()); $this->model->update($productSku, $entryMock); } + /** + * Check update gallery entry behavior. + * + * @return void + */ public function testUpdate() { $productSku = 'testProduct'; @@ -203,14 +221,13 @@ public function testUpdate() ->willReturn([$existingEntryMock, $existingSecondEntryMock]); $entryMock->expects($this->exactly(2))->method('getId')->willReturn($entryId); - $entryMock->expects($this->once())->method('getFile')->willReturn("base64"); - $entryMock->expects($this->once())->method('setId')->with(null); - $entryMock->expects($this->exactly(2))->method('getTypes')->willReturn(['image']); + $entryMock->expects($this->once())->method('getTypes')->willReturn(['image']); $this->productMock->expects($this->once())->method('setMediaGalleryEntries') ->with([$entryMock, $existingSecondEntryMock]) ->willReturnSelf(); $this->productRepositoryMock->expects($this->once())->method('save')->with($this->productMock); + $this->assertTrue($this->model->update($productSku, $entryMock)); } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Image/ParamsBuilderTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Image/ParamsBuilderTest.php new file mode 100644 index 0000000000000..22e3a88574e03 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Image/ParamsBuilderTest.php @@ -0,0 +1,134 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Unit\Model\Product\Image; + +use Magento\Catalog\Model\Product\Image; +use Magento\Catalog\Model\Product\Image\ParamsBuilder; +use Magento\Framework\App\Area; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Config\View; +use Magento\Framework\View\ConfigInterface; +use Magento\Store\Model\ScopeInterface; + +class ParamsBuilderTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var ConfigInterface + */ + private $viewConfig; + + /** + * @var ParamsBuilder + */ + private $model; + + protected function setUp() + { + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->scopeConfig = $this->createMock(ScopeConfigInterface::class); + $this->viewConfig = $this->createMock(ConfigInterface::class); + $this->model = $objectManager->getObject( + ParamsBuilder::class, + [ + 'scopeConfig' => $this->scopeConfig, + 'viewConfig' => $this->viewConfig, + ] + ); + } + + /** + * Test watermark location. + */ + public function testWatermarkLocation() + { + $imageArguments = [ + 'type' => 'type', + 'height' => 'image_height', + 'width' => 'image_width', + 'angle' => 'angle', + 'background' => [1, 2, 3] + ]; + $scopeId = 1; + $quality = 100; + $file = 'file'; + $width = 'width'; + $height = 'height'; + $size = "{$width}x{$height}"; + $opacity = 'opacity'; + $position = 'position'; + + $viewMock = $this->createMock(View::class); + $viewMock->expects($this->once()) + ->method('getVarValue') + ->with('Magento_Catalog', 'product_image_white_borders') + ->willReturn(true); + + $this->viewConfig->expects($this->once()) + ->method('getViewConfig') + ->with(['area' => Area::AREA_FRONTEND]) + ->willReturn($viewMock); + + $this->scopeConfig->expects($this->exactly(5))->method('getValue')->withConsecutive( + [ + Image::XML_PATH_JPEG_QUALITY + ], + [ + "design/watermark/{$imageArguments['type']}_image", + ScopeInterface::SCOPE_STORE, + $scopeId, + ], + [ + "design/watermark/{$imageArguments['type']}_size", + ScopeInterface::SCOPE_STORE], + [ + "design/watermark/{$imageArguments['type']}_imageOpacity", + ScopeInterface::SCOPE_STORE, + $scopeId + ], + [ + "design/watermark/{$imageArguments['type']}_position", + ScopeInterface::SCOPE_STORE, + $scopeId + ] + )->willReturnOnConsecutiveCalls( + $quality, + $file, + $size, + $opacity, + $position + ); + + $actual = $this->model->build($imageArguments, $scopeId); + $expected = [ + 'image_type' => $imageArguments['type'], + 'background' => $imageArguments['background'], + 'angle' => $imageArguments['angle'], + 'quality' => $quality, + 'keep_aspect_ratio' => true, + 'keep_frame' => true, + 'keep_transparency' => true, + 'constrain_only' => true, + 'watermark_file' => $file, + 'watermark_image_opacity' => $opacity, + 'watermark_position' => $position, + 'watermark_width' => $width, + 'watermark_height' => $height, + 'image_height' => $imageArguments['height'], + 'image_width' => $imageArguments['width'], + ]; + + $this->assertEquals( + $expected, + $actual + ); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/ValueTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/ValueTest.php index 1ff5bef78cd79..212c8020750d2 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/ValueTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/ValueTest.php @@ -3,15 +3,21 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\Catalog\Test\Unit\Model\Product\Option; +declare(strict_types=1); -use \Magento\Catalog\Model\Product\Option\Value; +namespace Magento\Catalog\Test\Unit\Model\Product\Option; use Magento\Catalog\Model\Product; + use Magento\Catalog\Model\Product\Option; -use Magento\Framework\Model\ActionValidator\RemoveAction; +use Magento\Catalog\Model\Product\Option\Value; +use Magento\Catalog\Pricing\Price\CalculateCustomOptionCatalogRule; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +/** + * Test for \Magento\Catalog\Model\Product\Option\Value class. + */ class ValueTest extends \PHPUnit\Framework\TestCase { /** @@ -24,6 +30,11 @@ class ValueTest extends \PHPUnit\Framework\TestCase */ private $customOptionPriceCalculatorMock; + /** + * @var CalculateCustomOptionCatalogRule|MockObject + */ + private $CalculateCustomOptionCatalogRule; + protected function setUp() { $mockedResource = $this->getMockedResource(); @@ -33,6 +44,10 @@ protected function setUp() \Magento\Catalog\Pricing\Price\CustomOptionPriceCalculator::class ); + $this->CalculateCustomOptionCatalogRule = $this->createMock( + CalculateCustomOptionCatalogRule::class + ); + $helper = new ObjectManager($this); $this->model = $helper->getObject( \Magento\Catalog\Model\Product\Option\Value::class, @@ -40,6 +55,7 @@ protected function setUp() 'resource' => $mockedResource, 'valueCollectionFactory' => $mockedCollectionFactory, 'customOptionPriceCalculator' => $this->customOptionPriceCalculatorMock, + 'CalculateCustomOptionCatalogRule' => $this->CalculateCustomOptionCatalogRule ] ); $this->model->setOption($this->getMockedOption()); @@ -66,11 +82,11 @@ public function testGetPrice() $this->model->setPriceType(Value::TYPE_PERCENT); $this->assertEquals($price, $this->model->getPrice(false)); - $percentPice = 100; - $this->customOptionPriceCalculatorMock->expects($this->atLeastOnce()) - ->method('getOptionPriceByPriceCode') - ->willReturn($percentPice); - $this->assertEquals($percentPice, $this->model->getPrice(true)); + $percentPrice = 100; + $this->CalculateCustomOptionCatalogRule->expects($this->atLeastOnce()) + ->method('execute') + ->willReturn($percentPrice); + $this->assertEquals($percentPrice, $this->model->getPrice(true)); } public function testGetValuesCollection() diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/Validation/ResultTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/Validation/ResultTest.php new file mode 100644 index 0000000000000..7ed32d564ca19 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/Validation/ResultTest.php @@ -0,0 +1,91 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Test\Unit\Model\Product\Price\Validation; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Catalog\Model\Product\Price\Validation\Result; +use PHPUnit\Framework\TestCase; +use Magento\Catalog\Api\Data\PriceUpdateResultInterface; +use Magento\Catalog\Api\Data\PriceUpdateResultInterfaceFactory; + +class ResultTest extends TestCase +{ + /** + * @var Result + */ + private $model; + + /** + * @var PriceUpdateResultInterfaceFactory|PHPUnit_Framework_MockObject_MockObject + */ + private $priceUpdateResultFactory; + + /** + * @var ObjectManagerHelper|PHPUnit_Framework_MockObject_MockObject + */ + private $objectManager; + + /** + * Setup environment for test + */ + protected function setUp() + { + $this->priceUpdateResultFactory = $this->getMockBuilder(PriceUpdateResultInterfaceFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->objectManager = new ObjectManagerHelper($this); + $this->model = $this->objectManager->getObject( + Result::class, + [ + 'priceUpdateResultFactory' => $this->priceUpdateResultFactory + ] + ); + + $this->model->addFailedItem(1, 'Invalid attribute color = 1', ['SKU' => 'ABC', 'storeId' => 1]); + $this->model->addFailedItem(2, 'Invalid attribute size = M', ['SKU' => 'DEF', 'storeId' => 1]); + } + + /** + * Test getFailedRowIds() function + */ + public function testGetFailedRowIds() + { + $this->assertEquals([1, 2], $this->model->getFailedRowIds()); + } + + /** + * Test getFailedItems() function + */ + public function testGetFailedItems() + { + $priceUpdateResult1 = $this->createMock(PriceUpdateResultInterface::class); + $priceUpdateResult2 = $this->createMock(PriceUpdateResultInterface::class); + + $this->priceUpdateResultFactory->expects($this->at(0)) + ->method('create') + ->willReturn($priceUpdateResult1); + $this->priceUpdateResultFactory->expects($this->at(1)) + ->method('create') + ->willReturn($priceUpdateResult2); + + $priceUpdateResult1->expects($this->once())->method('setMessage') + ->with('Invalid attribute color = 1'); + $priceUpdateResult1->expects($this->once())->method('setParameters') + ->with(['SKU' => 'ABC', 'storeId' => 1]); + + $priceUpdateResult2->expects($this->once())->method('setMessage') + ->with('Invalid attribute size = M'); + $priceUpdateResult2->expects($this->once())->method('setParameters') + ->with(['SKU' => 'DEF', 'storeId' => 1]); + + $this->assertEquals([$priceUpdateResult1, $priceUpdateResult2], $this->model->getFailedItems()); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Type/PriceTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Type/PriceTest.php index 99151d1c8dd39..cdbef1bec3872 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Type/PriceTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Type/PriceTest.php @@ -165,9 +165,13 @@ public function testTierPrices($priceScope, $expectedWebsiteId) $this->websiteMock->expects($this->any())->method('getId')->will($this->returnValue($expectedWebsiteId)); $this->tpFactory->expects($this->any()) ->method('create') - ->will($this->returnCallback(function () { - return $this->objectManagerHelper->getObject(\Magento\Catalog\Model\Product\TierPrice::class); - })); + ->will( + $this->returnCallback( + function () { + return $this->objectManagerHelper->getObject(\Magento\Catalog\Model\Product\TierPrice::class); + } + ) + ); // create sample TierPrice objects that would be coming from a REST call $tierPriceExtensionMock = $this->getMockBuilder(ProductTierPriceExtensionInterface::class) @@ -198,9 +202,10 @@ public function testTierPrices($priceScope, $expectedWebsiteId) $tpArray = $this->product->getData($this::KEY_TIER_PRICE); $this->assertNotNull($tpArray); $this->assertTrue(is_array($tpArray)); - $this->assertEquals(sizeof($tps), sizeof($tpArray)); + $this->assertEquals(count($tps), count($tpArray)); - for ($i = 0; $i < sizeof($tps); $i++) { + $count = count($tps); + for ($i = 0; $i < $count; $i++) { $tpData = $tpArray[$i]; $this->assertEquals($expectedWebsiteId, $tpData['website_id'], 'Website Id does not match'); $this->assertEquals($tps[$i]->getValue(), $tpData['price'], 'Price/Value does not match'); @@ -226,12 +231,13 @@ public function testTierPrices($priceScope, $expectedWebsiteId) $tpRests = $this->model->getTierPrices($this->product); $this->assertNotNull($tpRests); $this->assertTrue(is_array($tpRests)); - $this->assertEquals(sizeof($tps), sizeof($tpRests)); + $this->assertEquals(count($tps), count($tpRests)); foreach ($tpRests as $tpRest) { $this->assertEquals(50, $tpRest->getExtensionAttributes()->getPercentageValue()); } - for ($i = 0; $i < sizeof($tps); $i++) { + $count = count($tps); + for ($i = 0; $i < $count; $i++) { $this->assertEquals( $tps[$i]->getValue(), $tpRests[$i]->getValue(), diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php index 028c7ea83e1c2..ce234e17c41aa 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php @@ -1077,6 +1077,12 @@ public function testGetProductLinks() $outputRelatedLink->setPosition(0); $expectedOutput = [$outputRelatedLink]; $this->productLinkRepositoryMock->expects($this->once())->method('getList')->willReturn($expectedOutput); + $typeInstance = $this->getMockBuilder(\Magento\Catalog\Model\Product\Type\AbstractType::class) + ->setMethods(['getSku']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $typeInstance->method('getSku')->willReturn('model'); + $this->productTypeInstanceMock->method('factory')->willReturn($typeInstance); $links = $this->model->getProductLinks(); $this->assertEquals($links, $expectedOutput); } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Link/Product/CollectionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Link/Product/CollectionTest.php index 596148b627506..a29e76c5c8ff1 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Link/Product/CollectionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Link/Product/CollectionTest.php @@ -7,6 +7,8 @@ use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitation; use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; +use Magento\Framework\EntityManager\EntityMetadataInterface; +use Magento\Framework\EntityManager\MetadataPool; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -105,11 +107,11 @@ protected function setUp() $this->storeManagerMock ->expects($this->any()) ->method('getStore') - ->will($this->returnCallback( + ->willReturnCallback( function ($store) { return is_object($store) ? $store : new \Magento\Framework\DataObject(['id' => 42]); } - )); + ); $this->catalogHelperMock = $this->createMock(\Magento\Catalog\Helper\Data::class); $this->stateMock = $this->createMock(\Magento\Catalog\Model\Indexer\Product\Flat\State::class); $this->scopeConfigInterfaceMock = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); @@ -125,6 +127,11 @@ function ($store) { $productLimitationFactoryMock->method('create') ->willReturn($this->createMock(ProductLimitation::class)); + $metadataMock = $this->getMockForAbstractClass(EntityMetadataInterface::class); + $metadataMock->method('getLinkField')->willReturn('entity_id'); + $metadataPoolMock = $this->getMockBuilder(MetadataPool::class)->disableOriginalConstructor()->getMock(); + $metadataPoolMock->method('getMetadata')->willReturn($metadataMock); + $this->collection = $this->objectManager->getObject( \Magento\Catalog\Model\ResourceModel\Product\Link\Product\Collection::class, [ @@ -147,6 +154,7 @@ function ($store) { 'customerSession' => $this->sessionMock, 'dateTime' => $this->dateTimeMock, 'productLimitationFactory' => $productLimitationFactoryMock, + 'metadataPool' => $metadataPoolMock ] ); } diff --git a/app/code/Magento/Catalog/Test/Unit/Pricing/Price/CalculateCustomOptionCatalogRuleTest.php b/app/code/Magento/Catalog/Test/Unit/Pricing/Price/CalculateCustomOptionCatalogRuleTest.php new file mode 100644 index 0000000000000..1a99ac5d451f0 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Pricing/Price/CalculateCustomOptionCatalogRuleTest.php @@ -0,0 +1,266 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Test\Unit\Pricing\Price; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\PriceModifier\Composite as PriceModifier; +use Magento\Catalog\Pricing\Price\CalculateCustomOptionCatalogRule; +use Magento\Catalog\Pricing\Price\RegularPrice; +use Magento\Catalog\Pricing\Price\SpecialPrice; +use Magento\CatalogRule\Pricing\Price\CatalogRulePrice; +use Magento\Directory\Model\PriceCurrency; +use Magento\Framework\Pricing\PriceInfo\Base; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test for CalculateCustomOptionCatalogRule class. + */ +class CalculateCustomOptionCatalogRuleTest extends TestCase +{ + /** + * @var Product|MockObject + */ + private $saleableItemMock; + + /** + * @var RegularPrice|MockObject + */ + private $regularPriceMock; + + /** + * @var SpecialPrice|MockObject + */ + private $specialPriceMock; + + /** + * @var CatalogRulePrice|MockObject + */ + private $catalogRulePriceMock; + + /** + * @var PriceModifier|MockObject + */ + private $priceModifierMock; + + /** + * @var CalculateCustomOptionCatalogRule + */ + private $calculateCustomOptionCatalogRule; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->saleableItemMock = $this->createMock(Product::class); + $this->regularPriceMock = $this->createMock(RegularPrice::class); + $this->specialPriceMock = $this->createMock(SpecialPrice::class); + $this->catalogRulePriceMock = $this->createMock(CatalogRulePrice::class); + $priceInfoMock = $this->createMock(Base::class); + $this->saleableItemMock->expects($this->any()) + ->method('getPriceInfo') + ->willReturn($priceInfoMock); + $this->regularPriceMock->expects($this->any()) + ->method('getPriceCode') + ->willReturn(RegularPrice::PRICE_CODE); + $this->specialPriceMock->expects($this->any()) + ->method('getPriceCode') + ->willReturn(SpecialPrice::PRICE_CODE); + $this->catalogRulePriceMock->expects($this->any()) + ->method('getPriceCode') + ->willReturn(CatalogRulePrice::PRICE_CODE); + $priceInfoMock->expects($this->any()) + ->method('getPrices') + ->willReturn( + [ + 'regular_price' => $this->regularPriceMock, + 'special_price' => $this->specialPriceMock, + 'catalog_rule_price' => $this->catalogRulePriceMock + ] + ); + $priceInfoMock->expects($this->any()) + ->method('getPrice') + ->willReturnMap( + [ + ['regular_price', $this->regularPriceMock], + ['special_price', $this->specialPriceMock], + ['catalog_rule_price', $this->catalogRulePriceMock], + ] + ); + $priceCurrencyMock = $this->createMock(PriceCurrency::class); + $priceCurrencyMock->expects($this->any()) + ->method('convertAndRound') + ->willReturnArgument(0); + $this->priceModifierMock = $this->createMock(PriceModifier::class); + + $this->calculateCustomOptionCatalogRule = $objectManager->getObject( + CalculateCustomOptionCatalogRule::class, + [ + 'priceCurrency' => $priceCurrencyMock, + 'priceModifier' => $this->priceModifierMock, + ] + ); + } + + /** + * Tests correct option price calculation with different catalog rules and special prices combination. + * + * @dataProvider executeDataProvider + * @param array $prices + * @param float $catalogRulePriceModifier + * @param float $optionPriceValue + * @param bool $isPercent + * @param float $expectedResult + */ + public function testExecute( + array $prices, + float $catalogRulePriceModifier, + float $optionPriceValue, + bool $isPercent, + float $expectedResult + ) { + $this->regularPriceMock->expects($this->any()) + ->method('getValue') + ->willReturn($prices['regularPriceValue']); + $this->specialPriceMock->expects($this->any()) + ->method('getValue') + ->willReturn($prices['specialPriceValue']); + $this->priceModifierMock->expects($this->any()) + ->method('modifyPrice') + ->willReturnCallback( + function ($price) use ($catalogRulePriceModifier) { + return $price * $catalogRulePriceModifier; + } + ); + + $finalPrice = $this->calculateCustomOptionCatalogRule->execute( + $this->saleableItemMock, + $optionPriceValue, + $isPercent + ); + + $this->assertSame($expectedResult, $finalPrice); + } + + /** + * Data provider for testExecute. + * + * "Active" means this price type has biggest discount, so other prices doesn't count. + * + * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function executeDataProvider(): array + { + return [ + 'No special price, no catalog price rules, fixed option price' => [ + 'prices' => [ + 'regularPriceValue' => 1000, + 'specialPriceValue' => 1000, + ], + 'catalogRulePriceModifier' => 1.0, + 'optionPriceValue' => 100.0, + 'isPercent' => false, + 'expectedResult' => 100.0 + ], + 'No special price, no catalog price rules, percent option price' => [ + 'prices' => [ + 'regularPriceValue' => 1000, + 'specialPriceValue' => 1000, + ], + 'catalogRulePriceModifier' => 1.0, + 'optionPriceValue' => 100.0, + 'isPercent' => true, + 'expectedResult' => 1000.0 + ], + 'No special price, catalog price rule set, fixed option price' => [ + 'prices' => [ + 'regularPriceValue' => 1000, + 'specialPriceValue' => 1000, + ], + 'catalogRulePriceModifier' => 0.9, + 'optionPriceValue' => 100.0, + 'isPercent' => false, + 'expectedResult' => 90.0 + ], + 'No special price, catalog price rule set, percent option price' => [ + 'prices' => [ + 'regularPriceValue' => 1000, + 'specialPriceValue' => 1000, + ], + 'catalogRulePriceModifier' => 0.9, + 'optionPriceValue' => 100.0, + 'isPercent' => true, + 'expectedResult' => 900.0 + ], + 'Special price set, no catalog price rule, fixed option price' => [ + 'prices' => [ + 'regularPriceValue' => 1000, + 'specialPriceValue' => 900, + ], + 'catalogRulePriceModifier' => 1.0, + 'optionPriceValue' => 100.0, + 'isPercent' => false, + 'expectedResult' => 100.0 + ], + 'Special price set, no catalog price rule, percent option price' => [ + 'prices' => [ + 'regularPriceValue' => 1000, + 'specialPriceValue' => 900, + ], + 'catalogRulePriceModifier' => 1.0, + 'optionPriceValue' => 100.0, + 'isPercent' => true, + 'expectedResult' => 900.0 + ], + 'Special price set and active, catalog price rule set, fixed option price' => [ + 'prices' => [ + 'regularPriceValue' => 1000, + 'specialPriceValue' => 800, + ], + 'catalogRulePriceModifier' => 0.9, + 'optionPriceValue' => 100.0, + 'isPercent' => false, + 'expectedResult' => 100.0 + ], + 'Special price set and active, catalog price rule set, percent option price' => [ + 'prices' => [ + 'regularPriceValue' => 1000, + 'specialPriceValue' => 800, + ], + 'catalogRulePriceModifier' => 0.9, + 'optionPriceValue' => 100.0, + 'isPercent' => true, + 'expectedResult' => 800.0 + ], + 'Special price set, catalog price rule set and active, fixed option price' => [ + 'prices' => [ + 'regularPriceValue' => 1000, + 'specialPriceValue' => 950, + ], + 'catalogRulePriceModifier' => 0.9, + 'optionPriceValue' => 100.0, + 'isPercent' => false, + 'expectedResult' => 90.0 + ], + 'Special price set, catalog price rule set and active, percent option price' => [ + 'prices' => [ + 'regularPriceValue' => 1000, + 'specialPriceValue' => 950, + ], + 'catalogRulePriceModifier' => 0.9, + 'optionPriceValue' => 100.0, + 'isPercent' => true, + 'expectedResult' => 900.0 + ], + ]; + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php index 55acfff6d87d3..b3acaa4b8bbed 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/Component/ColumnFactoryTest.php @@ -7,17 +7,19 @@ namespace Magento\Catalog\Test\Unit\Ui\Component; -use PHPUnit\Framework\TestCase; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; use Magento\Catalog\Ui\Component\ColumnFactory; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Catalog\Api\Data\ProductAttributeInterface; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Framework\View\Element\UiComponentFactory; use Magento\Ui\Component\Listing\Columns\ColumnInterface; use Magento\Ui\Component\Filters\FilterModifier; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** - * ColumnFactory test. + * Test to Create columns factory on product grid page */ class ColumnFactoryTest extends TestCase { @@ -32,25 +34,30 @@ class ColumnFactoryTest extends TestCase private $objectManager; /** - * @var ProductAttributeInterface|\PHPUnit\Framework\MockObject\MockObject + * @var Attribute|MockObject */ private $attribute; /** - * @var ContextInterface|\PHPUnit\Framework\MockObject\MockObject + * @var ContextInterface|MockObject */ private $context; /** - * @var UiComponentFactory|\PHPUnit\Framework\MockObject\MockObject + * @var UiComponentFactory|MockObject */ private $uiComponentFactory; /** - * @var ColumnInterface|\PHPUnit\Framework\MockObject\MockObject + * @var ColumnInterface|MockObject */ private $column; + /** + * @var TimezoneInterface|MockObject + */ + private $timezone; + /** * @inheritdoc */ @@ -58,18 +65,29 @@ protected function setUp(): void { $this->objectManager = new ObjectManager($this); - $this->attribute = $this->getMockBuilder(ProductAttributeInterface::class) - ->setMethods(['usesSource']) - ->getMockForAbstractClass(); + $this->attribute = $this->createPartialMock( + Attribute::class, + [ + 'getAttributeCode', + 'getIsFilterableInGrid', + 'getFrontendInput', + 'getDefaultFrontendLabel', + 'getIsVisibleInGrid', + ] + ); $this->context = $this->createMock(ContextInterface::class); $this->uiComponentFactory = $this->createMock(UiComponentFactory::class); $this->column = $this->getMockForAbstractClass(ColumnInterface::class); $this->uiComponentFactory->method('create') ->willReturn($this->column); + $this->timezone = $this->createMock(TimezoneInterface::class); $this->columnFactory = $this->objectManager->getObject( ColumnFactory::class, - ['componentFactory' => $this->uiComponentFactory] + [ + 'componentFactory' => $this->uiComponentFactory, + 'timezone' => $this->timezone, + ] ); } @@ -97,7 +115,6 @@ public function testCreatedObject(): void * * @param array $filterModifiers * @param null|string $filter - * * @return void * @dataProvider filterModifiersProvider */ @@ -134,7 +151,7 @@ public function testCreateWithNotFilterableInGridAttribute(array $filterModifier } /** - * Filter modifiers data provider. + * Filter modifiers data provider * * @return array */ @@ -155,4 +172,102 @@ public function filterModifiersProvider(): array ], ]; } + + /** + * Test to create date column + * + * @param string $frontendInput + * @param bool $showsTime + * @param string $expectedDateFormat + * @param string $expectedTimezone + * @dataProvider createDateColumnDataProvider + */ + public function testCreateDateColumn( + string $frontendInput, + bool $showsTime, + string $expectedDateFormat, + string $expectedTimezone + ) { + $attributeCode = 'attribute_code'; + $dateFormat = 'date_format'; + $dateTimeFormat = 'datetime_format'; + $defaultTimezone = 'default_timezone'; + $configTimezone = 'config_timezone'; + $label = 'Date label'; + + $expectedConfig = [ + 'data' => [ + 'config' => [ + 'label' => __($label), + 'dataType' => 'date', + 'add_field' => true, + 'visible' => true, + 'filter' => 'dateRange', + 'component' => 'Magento_Ui/js/grid/columns/date', + 'timezone' => $expectedTimezone, + 'dateFormat' => $expectedDateFormat, + '__disableTmpl' => ['label' => true], + 'options' => [ + 'showsTime' => $showsTime + ] + ], + ], + 'context' => $this->context, + ]; + + $this->attribute->method('getAttributeCode') + ->willReturn($attributeCode); + $this->attribute->method('getDefaultFrontendLabel') + ->willReturn($label); + $this->attribute->method('getIsFilterableInGrid') + ->willReturn(true); + $this->attribute->method('getIsVisibleInGrid') + ->willReturn(true); + $this->attribute->method('getFrontendInput') + ->willReturn($frontendInput); + + $this->timezone->method('getDateFormat') + ->with(\IntlDateFormatter::MEDIUM) + ->willReturn($dateFormat); + $this->timezone->method('getDateTimeFormat') + ->with(\IntlDateFormatter::MEDIUM) + ->willReturn($dateTimeFormat); + $this->timezone->method('getDefaultTimezone') + ->willReturn($defaultTimezone); + $this->timezone->method('getConfigTimezone') + ->willReturn($configTimezone); + + $this->uiComponentFactory->expects($this->once()) + ->method('create') + ->with($attributeCode, 'column', $expectedConfig) + ->willReturn($this->column); + + $this->assertEquals( + $this->column, + $this->columnFactory->create($this->attribute, $this->context) + ); + } + + /** + * Data provider to create date column test + * + * @return array + */ + public function createDateColumnDataProvider(): array + { + return [ + [ + 'frontendInput' => 'date', + 'showsTime' => false, + 'dateFormat' => 'date_format', + 'expectedTimezone' => 'default_timezone', + ], + [ + 'frontendInput' => 'datetime', + 'showsTime' => true, + 'expectedDateFormat' => 'datetime_format', + 'expectedTimezone' => 'config_timezone', + ], + ]; + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/Component/FilterFactoryTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/Component/FilterFactoryTest.php new file mode 100644 index 0000000000000..1e72b7ba35864 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Ui/Component/FilterFactoryTest.php @@ -0,0 +1,103 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Test\Unit\Ui\Component; + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Ui\Component\FilterFactory; +use Magento\Eav\Model\Entity\Attribute\Source\SourceInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\View\Element\UiComponentFactory; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class FilterFactoryTest extends TestCase +{ + /** + * Stub attribute + */ + private const STUB_ATTRIBUTE = [ + 'attribute_code' => 'color', + 'default_frontend_label' => 'Color', + 'uses_source' => 'Color', + 'source_model' => 'getSourceModel value', + 'frontend_input' => 'select', + 'all_options' => [ + [ + 'value' => 1, + 'label' => 'Black', + ], + [ + 'value' => 2, + 'label' => 'White', + ] + ] + ]; + + /** + * @var FilterFactory + */ + private $filterFactory; + + /** + * @var UiComponentFactory|MockObject + */ + private $componentFactoryMock; + + /** + * Setup environment for test + */ + protected function setUp() + { + $objectManager = new ObjectManager($this); + + $this->componentFactoryMock = $this->createMock(UiComponentFactory::class); + + $this->filterFactory = $objectManager->getObject( + FilterFactory::class, + [ + 'componentFactory' => $this->componentFactoryMock + ] + ); + } + + /** + * Test create() with use source attribute + */ + public function testCreateWithUseSourceAttribute() + { + $contextMock = $this->createMock(ContextInterface::class); + $attributeMock = $this->getMockBuilder(ProductAttributeInterface::class) + ->setMethods(['usesSource', 'getSource']) + ->getMockForAbstractClass(); + $attributeMock->method('getAttributeCode')->willReturn(self::STUB_ATTRIBUTE['attribute_code']); + $attributeMock->method('getDefaultFrontendLabel') + ->willReturn(self::STUB_ATTRIBUTE['default_frontend_label']); + $attributeMock->method('usesSource')->willReturn(self::STUB_ATTRIBUTE['uses_source']); + $attributeMock->method('getSourceModel')->willReturn(self::STUB_ATTRIBUTE['source_model']); + $attributeMock->method('getFrontendInput')->willReturn(self::STUB_ATTRIBUTE['frontend_input']); + $sourceMock = $this->createMock(SourceInterface::class); + $attributeMock->method('getSource')->willReturn($sourceMock); + $sourceMock->method('getAllOptions')->willReturn(self::STUB_ATTRIBUTE['all_options']); + $this->componentFactoryMock->expects($this->once()) + ->method('create') + ->with(self::STUB_ATTRIBUTE['attribute_code'], 'filterSelect', [ + 'data' => [ + 'config' => [ + 'options' => self::STUB_ATTRIBUTE['all_options'], + 'caption' => (string)__('Select...'), + 'dataScope' => self::STUB_ATTRIBUTE['attribute_code'], + 'label' => self::STUB_ATTRIBUTE['default_frontend_label'], + ] + ], + 'context' => $contextMock + ]); + + $this->filterFactory->create($attributeMock, $contextMock); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php index e4c8414ce07b4..91e22407acc43 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/EavTest.php @@ -38,9 +38,10 @@ use Magento\Framework\Stdlib\ArrayManager; use Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory as EavAttributeFactory; use Magento\Framework\Event\ManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; /** - * Class EavTest + * Class to test Data provider for eav attributes on product page * * @method Eav getModel * @SuppressWarnings(PHPMD.TooManyFields) @@ -49,142 +50,142 @@ class EavTest extends AbstractModifierTest { /** - * @var Config|\PHPUnit_Framework_MockObject_MockObject + * @var Config|MockObject */ private $eavConfigMock; /** - * @var EavValidationRules|\PHPUnit_Framework_MockObject_MockObject + * @var EavValidationRules|MockObject */ private $eavValidationRulesMock; /** - * @var RequestInterface|\PHPUnit_Framework_MockObject_MockObject + * @var RequestInterface|MockObject */ private $requestMock; /** - * @var GroupCollectionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var GroupCollectionFactory|MockObject */ private $groupCollectionFactoryMock; /** - * @var GroupCollection|\PHPUnit_Framework_MockObject_MockObject + * @var GroupCollection|MockObject */ private $groupCollectionMock; /** - * @var Group|\PHPUnit_Framework_MockObject_MockObject + * @var Group|MockObject */ private $groupMock; /** - * @var EavAttribute|\PHPUnit_Framework_MockObject_MockObject + * @var EavAttribute|MockObject */ private $attributeMock; /** - * @var EntityType|\PHPUnit_Framework_MockObject_MockObject + * @var EntityType|MockObject */ private $entityTypeMock; /** - * @var AttributeCollectionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var AttributeCollectionFactory|MockObject */ private $attributeCollectionFactoryMock; /** - * @var AttributeCollection|\PHPUnit_Framework_MockObject_MockObject + * @var AttributeCollection|MockObject */ private $attributeCollectionMock; /** - * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|MockObject */ private $storeManagerMock; /** - * @var FormElementMapper|\PHPUnit_Framework_MockObject_MockObject + * @var FormElementMapper|MockObject */ private $formElementMapperMock; /** - * @var MetaPropertiesMapper|\PHPUnit_Framework_MockObject_MockObject + * @var MetaPropertiesMapper|MockObject */ private $metaPropertiesMapperMock; /** - * @var SearchCriteriaBuilder|\PHPUnit_Framework_MockObject_MockObject + * @var SearchCriteriaBuilder|MockObject */ private $searchCriteriaBuilderMock; /** - * @var ProductAttributeGroupRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ProductAttributeGroupRepositoryInterface|MockObject */ private $attributeGroupRepositoryMock; /** - * @var SearchCriteria|\PHPUnit_Framework_MockObject_MockObject + * @var SearchCriteria|MockObject */ private $searchCriteriaMock; /** - * @var SortOrderBuilder|\PHPUnit_Framework_MockObject_MockObject + * @var SortOrderBuilder|MockObject */ private $sortOrderBuilderMock; /** - * @var ProductAttributeRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ProductAttributeRepositoryInterface|MockObject */ private $attributeRepositoryMock; /** - * @var AttributeGroupInterface|\PHPUnit_Framework_MockObject_MockObject + * @var AttributeGroupInterface|MockObject */ private $attributeGroupMock; /** - * @var SearchResultsInterface|\PHPUnit_Framework_MockObject_MockObject + * @var SearchResultsInterface|MockObject */ private $searchResultsMock; /** - * @var Attribute|\PHPUnit_Framework_MockObject_MockObject + * @var Attribute|MockObject */ private $eavAttributeMock; /** - * @var StoreInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreInterface|MockObject */ protected $storeMock; /** - * @var Currency|\PHPUnit_Framework_MockObject_MockObject + * @var Currency|MockObject */ protected $currencyMock; /** - * @var CurrencyLocale|\PHPUnit_Framework_MockObject_MockObject + * @var CurrencyLocale|MockObject */ protected $currencyLocaleMock; /** - * @var ProductAttributeInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ProductAttributeInterface|MockObject */ protected $productAttributeMock; /** - * @var ArrayManager|\PHPUnit_Framework_MockObject_MockObject + * @var ArrayManager|MockObject */ protected $arrayManagerMock; /** - * @var EavAttributeFactory|\PHPUnit_Framework_MockObject_MockObject + * @var EavAttributeFactory|MockObject */ protected $eavAttributeFactoryMock; /** - * @var ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ManagerInterface|MockObject */ protected $eventManagerMock; @@ -457,8 +458,10 @@ public function testModifyData() * @param string|null $attrValue * @param array $expected * @param bool $locked - * @covers \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav::isProductExists - * @covers \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav::setupAttributeMeta + * @param string|null $frontendInput + * @param array $expectedCustomize + * @covers \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav::isProductExists + * @covers \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav::setupAttributeMeta * @dataProvider setupAttributeMetaDataProvider */ public function testSetupAttributeMetaDefaultAttribute( @@ -466,7 +469,9 @@ public function testSetupAttributeMetaDefaultAttribute( bool $productRequired, $attrValue, array $expected, - $locked = false + bool $locked = false, + string $frontendInput = null, + array $expectedCustomize = [] ) : void { $configPath = 'arguments/data/config'; $groupCode = 'product-details'; @@ -492,6 +497,7 @@ public function testSetupAttributeMetaDefaultAttribute( $this->productAttributeMock->method('getDefaultValue')->willReturn('required_value'); $this->productAttributeMock->method('getAttributeCode')->willReturn('code'); $this->productAttributeMock->method('getValue')->willReturn('value'); + $this->productAttributeMock->method('getFrontendInput')->willReturn($frontendInput); $attributeMock = $this->getMockBuilder(AttributeInterface::class) ->setMethods(['getValue']) @@ -527,14 +533,16 @@ function ($value) use ($attributeOptionsExpected) { } ) ) - ->willReturn($expected); + ->willReturn($expected + $expectedCustomize); $this->arrayManagerMock->method('get')->willReturn([]); $this->arrayManagerMock->method('exists')->willReturn(true); + $actual = $this->eav->setupAttributeMeta($this->productAttributeMock, $groupCode, $sortOrder); + $this->assertEquals( - $expected, - $this->eav->setupAttributeMeta($this->productAttributeMock, $groupCode, $sortOrder) + $expected + $expectedCustomize, + $actual ); } @@ -666,7 +674,30 @@ public function setupAttributeMetaDataProvider() 'sortOrder' => 0, '__disableTmpl' => ['label' => true, 'code' => true] ], - ] + ], + 'datetime_null_prod_not_new_and_required' => [ + 'productId' => 1, + 'productRequired' => true, + 'attrValue' => 'val', + 'expected' => [ + 'dataType' => 'datetime', + 'formElement' => 'datetime', + 'visible' => null, + 'required' => true, + 'notice' => null, + 'default' => null, + 'label' => new Phrase(null), + 'code' => 'code', + 'source' => 'product-details', + 'scopeLabel' => '', + 'globalScope' => false, + 'sortOrder' => 0, + '__disableTmpl' => ['label' => true, 'code' => true] + ], + 'locked' => false, + 'frontendInput' => 'datetime', + 'expectedCustomize' => ['arguments' => ['data' => ['config' => ['options' => ['showsTime' => 1]]]]], + ], ]; } } diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/ProductCustomOptionsDataProviderTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/ProductCustomOptionsDataProviderTest.php index 0e0cb676cdf3e..143ef4461173a 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/ProductCustomOptionsDataProviderTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/ProductCustomOptionsDataProviderTest.php @@ -11,6 +11,9 @@ use Magento\Framework\App\RequestInterface; use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection; use Magento\Framework\DB\Select as DbSelect; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\EntityManager\EntityMetadataInterface; +use Magento\Ui\DataProvider\Modifier\PoolInterface; class ProductCustomOptionsDataProviderTest extends \PHPUnit\Framework\TestCase { @@ -44,6 +47,21 @@ class ProductCustomOptionsDataProviderTest extends \PHPUnit\Framework\TestCase */ protected $dbSelectMock; + /** + * @var MetadataPool|\PHPUnit_Framework_MockObject_MockObject + */ + private $metadataPool; + + /** + * @var EntityMetadataInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $entityMetadata; + + /** + * @var PoolInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $modifiersPool; + protected function setUp() { $this->collectionFactoryMock = $this->getMockBuilder(CollectionFactory::class) @@ -73,12 +91,29 @@ protected function setUp() ->method('create') ->willReturn($this->collectionMock); + $this->modifiersPool = $this->getMockBuilder(PoolInterface::class) + ->getMockForAbstractClass(); + $this->entityMetadata = $this->getMockBuilder(EntityMetadataInterface::class) + ->getMockForAbstractClass(); + $this->entityMetadata->expects($this->any()) + ->method('getLinkField') + ->willReturn('entity_id'); + $this->metadataPool = $this->getMockBuilder(MetadataPool::class) + ->disableOriginalConstructor() + ->setMethods(['getMetadata']) + ->getMock(); + $this->metadataPool->expects($this->any()) + ->method('getMetadata') + ->willReturn($this->entityMetadata); + $this->objectManagerHelper = new ObjectManagerHelper($this); $this->dataProvider = $this->objectManagerHelper->getObject( ProductCustomOptionsDataProvider::class, [ 'collectionFactory' => $this->collectionFactoryMock, - 'request' => $this->requestMock + 'request' => $this->requestMock, + 'modifiersPool' => $this->modifiersPool, + 'metadataPool' => $this->metadataPool ] ); } diff --git a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php index a4ccaffc8fb6a..a442041660893 100644 --- a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php +++ b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/BreadcrumbsTest.php @@ -12,6 +12,7 @@ use Magento\Catalog\ViewModel\Product\Breadcrumbs; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\Serialize\Serializer\JsonHexTag; /** * Unit test for Magento\Catalog\ViewModel\Product\Breadcrumbs. @@ -38,6 +39,11 @@ class BreadcrumbsTest extends \PHPUnit\Framework\TestCase */ private $objectManager; + /** + * @var JsonHexTag|\PHPUnit_Framework_MockObject_MockObject + */ + private $serializer; + /** * @inheritdoc */ @@ -55,12 +61,15 @@ protected function setUp() : void $escaper = $this->getObjectManager()->getObject(\Magento\Framework\Escaper::class); + $this->serializer = $this->createMock(JsonHexTag::class); + $this->viewModel = $this->getObjectManager()->getObject( Breadcrumbs::class, [ 'catalogData' => $this->catalogHelper, 'scopeConfig' => $this->scopeConfig, - 'escaper' => $escaper + 'escaper' => $escaper, + 'jsonSerializer' => $this->serializer ] ); } @@ -141,6 +150,8 @@ public function testGetJsonConfiguration($product, string $expectedJson) : void ->with('catalog/seo/category_url_suffix', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) ->willReturn('."html'); + $this->serializer->expects($this->once())->method('serialize')->willReturn($expectedJson); + $this->assertEquals($expectedJson, $this->viewModel->getJsonConfiguration()); } diff --git a/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php new file mode 100644 index 0000000000000..55bfbe8b4ec71 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/ViewModel/Product/Checker/AddToCompareAvailabilityTest.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Test\Unit\ViewModel\Product\Checker; + +use Magento\Catalog\ViewModel\Product\Checker\AddToCompareAvailability; +use Magento\CatalogInventory\Api\StockConfigurationInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; + +/** + * Unit test for Magento\Catalog\ViewModel\Product\Checker\AddToCompareAvailability. + */ +class AddToCompareAvailabilityTest extends \PHPUnit\Framework\TestCase +{ + + /** + * @var AddToCompareAvailability + */ + private $viewModel; + + /** + * @var StockConfigurationInterface|MockObject + */ + protected $stockConfigurationMock; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + + $objectManager = new ObjectManager($this); + + $this->stockConfigurationMock = + $this->getMockBuilder(StockConfigurationInterface::class) + ->getMock(); + + $this->viewModel = $objectManager->getObject( + AddToCompareAvailability::class, + [ + 'stockConfiguration' => $this->stockConfigurationMock + ] + ); + } + + /** + * Test IsAvailableForCompare() with data provider + * + * @param bool $status + * @param bool $isSalable + * @param array $isInStock + * @param bool $isShowOutOfStock + * @param bool $expectedBool + * @return void + * @dataProvider isAvailableForCompareDataProvider + */ + public function testIsAvailableForCompare($status, $isSalable, $isInStock, $isShowOutOfStock, $expectedBool): void + { + $productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + + $productMock->expects($this->once()) + ->method('getStatus') + ->willReturn($status); + + $productMock->expects($this->any()) + ->method('isSalable') + ->willReturn($isSalable); + + $productMock->expects($this->any()) + ->method('getQuantityAndStockStatus') + ->willReturn($isInStock); + + $this->stockConfigurationMock->expects($this->any()) + ->method('isShowOutOfStock') + ->willReturn($isShowOutOfStock); + + $this->assertEquals($expectedBool, $this->viewModel->isAvailableForCompare($productMock)); + } + + /** + * Data provider for isAvailableForCompare() + * + * @return array + */ + public function isAvailableForCompareDataProvider(): array + { + return [ + [Status::STATUS_ENABLED, true, ['is_in_stock' => true], false, true], + [Status::STATUS_ENABLED, true, ['is_in_stock' => false], true, true], + [Status::STATUS_ENABLED, true, [], false, true], + [Status::STATUS_ENABLED, false, [], false, false], + [Status::STATUS_DISABLED, true, ['is_in_stock' => true], false, false] + ]; + } +} diff --git a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php index 638f225ca1b82..b902e741c006c 100644 --- a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php +++ b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php @@ -5,10 +5,17 @@ */ namespace Magento\Catalog\Ui\Component; +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\View\Element\UiComponentFactory; use Magento\Ui\Component\Filters\FilterModifier; +use Magento\Ui\Component\Listing\Columns\ColumnInterface; /** - * Column Factory + * Create columns factory on product grid page * * @api * @since 100.0.2 @@ -16,7 +23,7 @@ class ColumnFactory { /** - * @var \Magento\Framework\View\Element\UiComponentFactory + * @var UiComponentFactory */ protected $componentFactory; @@ -40,25 +47,36 @@ class ColumnFactory 'select' => 'select', 'multiselect' => 'multiselect', 'date' => 'date', + 'datetime' => 'date', ]; /** - * @param \Magento\Framework\View\Element\UiComponentFactory $componentFactory + * @var TimezoneInterface */ - public function __construct(\Magento\Framework\View\Element\UiComponentFactory $componentFactory) - { + private $timezone; + + /** + * @param UiComponentFactory $componentFactory + * @param TimezoneInterface|null $timezone + */ + public function __construct( + UiComponentFactory $componentFactory, + TimezoneInterface $timezone = null + ) { $this->componentFactory = $componentFactory; + $this->timezone = $timezone + ?? ObjectManager::getInstance()->get(TimezoneInterface::class); } /** * Create Factory * - * @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute - * @param \Magento\Framework\View\Element\UiComponent\ContextInterface $context + * @param ProductAttributeInterface $attribute + * @param ContextInterface $context * @param array $config * - * @return \Magento\Ui\Component\Listing\Columns\ColumnInterface - * @throws \Magento\Framework\Exception\LocalizedException + * @return ColumnInterface + * @throws LocalizedException */ public function create($attribute, $context, array $config = []) { @@ -85,19 +103,46 @@ public function create($attribute, $context, array $config = []) $optionData['__disableTmpl'] = true; } } - + $config['component'] = $this->getJsComponent($config['dataType']); - + + if ($config['dataType'] === 'date') { + $config += $this->getDateConfig($attribute); + } + $arguments = [ 'data' => [ 'config' => $config, ], 'context' => $context, ]; - + return $this->componentFactory->create($columnName, 'column', $arguments); } + /** + * Get config for Date columns + * + * @param ProductAttributeInterface $attribute + * @return array + */ + private function getDateConfig(ProductAttributeInterface $attribute): array + { + $isDatetime = $attribute->getFrontendInput() === 'datetime'; + $dateFormat = $isDatetime + ? $this->timezone->getDateTimeFormat(\IntlDateFormatter::MEDIUM) + : $this->timezone->getDateFormat(\IntlDateFormatter::MEDIUM); + $timezone = $isDatetime + ? $this->timezone->getConfigTimezone() + : $this->timezone->getDefaultTimezone(); + + return [ + 'timezone' => $timezone, + 'dateFormat' => $dateFormat, + 'options' => ['showsTime' => $isDatetime], + ]; + } + /** * Get Js Component * @@ -113,7 +158,7 @@ protected function getJsComponent($dataType) /** * Get Data Type * - * @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute + * @param ProductAttributeInterface $attribute * * @return string */ @@ -130,8 +175,9 @@ protected function getDataType($attribute) */ protected function getFilterType($frontendInput) { - $filtersMap = ['date' => 'dateRange']; + $filtersMap = ['date' => 'dateRange', 'datetime' => 'dateRange']; $result = array_replace_recursive($this->dataTypeMap, $filtersMap); + return $result[$frontendInput] ?? $result['default']; } } diff --git a/app/code/Magento/Catalog/Ui/Component/Listing/Columns.php b/app/code/Magento/Catalog/Ui/Component/Listing/Columns.php index 8ea6d8b9e5a06..1e056d9f8d517 100644 --- a/app/code/Magento/Catalog/Ui/Component/Listing/Columns.php +++ b/app/code/Magento/Catalog/Ui/Component/Listing/Columns.php @@ -6,13 +6,15 @@ namespace Magento\Catalog\Ui\Component\Listing; /** + * Column IU component + * * @api * @since 100.0.2 */ class Columns extends \Magento\Ui\Component\Listing\Columns { /** - * Default columns max order + * Default columns max order value */ const DEFAULT_COLUMNS_MAX_ORDER = 100; @@ -30,6 +32,7 @@ class Columns extends \Magento\Ui\Component\Listing\Columns 'boolean' => 'select', 'multiselect' => 'select', 'date' => 'dateRange', + 'datetime' => 'datetimeRange', ]; /** @@ -52,7 +55,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function prepare() { diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php index 00132c6ad89e8..f19f5abd0b423 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php @@ -149,6 +149,7 @@ public function modifyMeta(array $meta) $this->specialPriceDataToInline(); $this->customizeTierPrice(); + $this->customizePrice(); if (isset($this->meta['advanced-pricing'])) { $this->addAdvancedPriceLink(); @@ -197,6 +198,29 @@ protected function preparePriceFields($fieldCode) return $this; } + /** + * Customize price field. + * + * @return $this + */ + private function customizePrice(): AdvancedPricing + { + $pathFrom = $this->arrayManager->findPath('price', $this->meta, null, 'children'); + + if ($pathFrom) { + $this->meta = $this->arrayManager->merge( + $this->arrayManager->slicePath($pathFrom, 0, -2) . '/arguments/data/config', + $this->meta, + [ + 'label' => false, + 'required' => false, + ] + ); + } + + return $this; + } + /** * Customize tier price field * @@ -573,12 +597,11 @@ private function specialPriceDataToInline() $this->arrayManager->slicePath($pathFrom, 0, -2) . '/arguments/data/config', $this->meta, [ - 'label' => __('Special Price From'), + 'label' => false, + 'required' => false, 'additionalClasses' => 'admin__control-grouped-date', 'breakLine' => false, 'component' => 'Magento_Ui/js/form/components/group', - 'scopeLabel' => - $this->arrayManager->get($pathFrom . '/arguments/data/config/scopeLabel', $this->meta), ] ); $this->meta = $this->arrayManager->merge( @@ -586,8 +609,9 @@ private function specialPriceDataToInline() $this->meta, [ 'label' => __('Special Price From'), - 'scopeLabel' => null, - 'additionalClasses' => 'admin__field-date' + 'scopeLabel' => + $this->arrayManager->get($pathFrom . '/arguments/data/config/scopeLabel', $this->meta), + 'additionalClasses' => 'admin__field-date', ] ); $this->meta = $this->arrayManager->merge( diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php index c4ca5eca8e880..cd1f8e8e3379b 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Categories.php @@ -243,13 +243,14 @@ protected function customizeCategoriesField(array $meta) 'arguments' => [ 'data' => [ 'config' => [ - 'label' => __('Categories'), + 'label' => false, + 'required' => false, 'dataScope' => '', 'breakLine' => false, 'formElement' => 'container', 'componentType' => 'container', 'component' => 'Magento_Ui/js/form/components/group', - 'scopeLabel' => __('[GLOBAL]'), + 'disabled' => $this->locator->getProduct()->isLockedAttribute($fieldCode), ], ], ], diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php index acebe66f6f5a9..25e816f79639a 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav.php @@ -18,6 +18,7 @@ use Magento\Eav\Api\Data\AttributeGroupInterface; use Magento\Eav\Api\Data\AttributeInterface; use Magento\Eav\Model\Config; +use Magento\Eav\Model\Entity\Attribute\Source\SpecificSourceInterface; use Magento\Eav\Model\ResourceModel\Entity\Attribute\Group\CollectionFactory as GroupCollectionFactory; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Api\SortOrderBuilder; @@ -39,7 +40,7 @@ use Magento\Eav\Model\ResourceModel\Entity\Attribute\CollectionFactory as AttributeCollectionFactory; /** - * Class Eav + * Data provider for eav attributes on product page * * @api * @@ -688,11 +689,17 @@ public function setupAttributeMeta(ProductAttributeInterface $attribute, $groupC '__disableTmpl' => ['label' => true, 'code' => true] ] ); + $product = $this->locator->getProduct(); // TODO: Refactor to $attribute->getOptions() when MAGETWO-48289 is done $attributeModel = $this->getAttributeModel($attribute); if ($attributeModel->usesSource()) { - $options = $attributeModel->getSource()->getAllOptions(true, true); + $source = $attributeModel->getSource(); + if ($source instanceof SpecificSourceInterface) { + $options = $source->getOptionsFor($product); + } else { + $options = $source->getAllOptions(true, true); + } foreach ($options as &$option) { $option['__disableTmpl'] = true; } @@ -719,7 +726,6 @@ public function setupAttributeMeta(ProductAttributeInterface $attribute, $groupC $meta = $this->arrayManager->merge($configPath, $meta, ['componentType' => Field::NAME]); } - $product = $this->locator->getProduct(); if (in_array($attributeCode, $this->attributesToDisable) || $product->isLockedAttribute($attributeCode)) { $meta = $this->arrayManager->merge($configPath, $meta, ['disabled' => true]); @@ -747,6 +753,9 @@ public function setupAttributeMeta(ProductAttributeInterface $attribute, $groupC // Gallery attribute is being handled by "Images And Videos" section $meta = []; break; + case 'datetime': + $meta = $this->customizeDatetimeAttribute($meta); + break; } //Checking access to design config. @@ -861,7 +870,9 @@ public function setupAttributeContainerMeta(ProductAttributeInterface $attribute 'arguments/data/config', $containerMeta, [ - 'component' => 'Magento_Ui/js/form/components/group' + 'component' => 'Magento_Ui/js/form/components/group', + 'label' => false, + 'required' => false, ] ); } @@ -950,6 +961,19 @@ private function customizeWysiwyg(ProductAttributeInterface $attribute, array $m return $meta; } + /** + * Customize datetime attribute + * + * @param array $meta + * @return array + */ + private function customizeDatetimeAttribute(array $meta): array + { + $meta['arguments']['data']['config']['options']['showsTime'] = 1; + + return $meta; + } + /** * Retrieve form element * diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php index 7c42b881bad3e..ebc0425be0188 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/General.php @@ -224,6 +224,7 @@ protected function customizeWeightField(array $meta) 'validate-zero-or-greater' => true ], 'additionalClasses' => 'admin__field-small', + 'sortOrder' => 0, 'addafter' => $this->locator->getStore()->getConfig('general/locale/weight_unit'), 'imports' => $disabled ? [] : [ 'disabled' => '!${$.provider}:' . self::DATA_SCOPE_PRODUCT @@ -242,6 +243,8 @@ protected function customizeWeightField(array $meta) $containerPath . static::META_CONFIG_PATH, $meta, [ + 'label' => false, + 'required' => false, 'component' => 'Magento_Ui/js/form/components/group', ] ); @@ -269,6 +272,7 @@ protected function customizeWeightField(array $meta) ], ], 'value' => (int)$this->locator->getProduct()->getTypeInstance()->hasWeight(), + 'sortOrder' => 10, 'disabled' => $disabled, ] ); @@ -317,7 +321,8 @@ protected function customizeNewDateRangeField(array $meta) $fromContainerPath . self::META_CONFIG_PATH, $meta, [ - 'label' => __('Set Product as New From'), + 'label' => false, + 'required' => false, 'additionalClasses' => 'admin__control-grouped-date', 'breakLine' => false, 'component' => 'Magento_Ui/js/form/components/group', diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdate.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdate.php new file mode 100644 index 0000000000000..453be0c1a1582 --- /dev/null +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdate.php @@ -0,0 +1,73 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Catalog\Ui\DataProvider\Product\Form\Modifier; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Locator\LocatorInterface; +use Magento\Catalog\Model\Product; +use Magento\Ui\DataProvider\Modifier\ModifierInterface; + +/** + * Additional logic on how to display the layout update field. + */ +class LayoutUpdate implements ModifierInterface +{ + /** + * @var LocatorInterface + */ + private $locator; + + /** + * @param LocatorInterface $locator + */ + public function __construct(LocatorInterface $locator) + { + $this->locator = $locator; + } + + /** + * Extract custom layout value. + * + * @param ProductInterface|Product $product + * @return mixed + */ + private function extractLayoutUpdate(ProductInterface $product) + { + if ($product instanceof Product && !$product->hasData(Product::CUSTOM_ATTRIBUTES)) { + return $product->getData('custom_layout_update'); + } + + $attr = $product->getCustomAttribute('custom_layout_update'); + + return $attr ? $attr->getValue() : null; + } + + /** + * @inheritdoc + * @since 101.1.0 + */ + public function modifyData(array $data) + { + $product = $this->locator->getProduct(); + if ($this->extractLayoutUpdate($product)) { + $data[$product->getId()][AbstractModifier::DATA_SOURCE_DEFAULT]['custom_layout_update_file'] + = \Magento\Catalog\Model\Product\Attribute\Backend\LayoutUpdate::VALUE_USE_UPDATE_XML; + } + + return $data; + } + + /** + * @inheritDoc + */ + public function modifyMeta(array $meta) + { + return $meta; + } +} diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/ScheduleDesignUpdate.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/ScheduleDesignUpdate.php index b2f453e8d8ccb..3b01106619640 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/ScheduleDesignUpdate.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/ScheduleDesignUpdate.php @@ -37,7 +37,8 @@ public function __construct(ArrayManager $arrayManager) } /** - * {@inheritdoc} + * @inheritdoc + * * @since 101.0.0 */ public function modifyMeta(array $meta) @@ -47,7 +48,8 @@ public function modifyMeta(array $meta) } /** - * {@inheritdoc} + * @inheritdoc + * * @since 101.0.0 */ public function modifyData(array $data) @@ -96,7 +98,8 @@ protected function customizeDateRangeField(array $meta) $fromContainerPath . self::META_CONFIG_PATH, $meta, [ - 'label' => __('Schedule Update From'), + 'label' => false, + 'required' => false, 'additionalClasses' => 'admin__control-grouped-date', 'breakLine' => false, 'component' => 'Magento_Ui/js/form/components/group', diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php index a529580e29239..9c5fffc5db9b9 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php @@ -115,7 +115,7 @@ private function getUpdatedTierPriceStructure(array $priceMeta) 'dataType' => Price::NAME, 'component' => 'Magento_Ui/js/form/components/group', 'label' => __('Price'), - 'enableLabel' => true, + 'showLabel' => false, 'dataScope' => '', 'additionalClasses' => 'control-grouped', 'sortOrder' => isset($priceMeta['arguments']['data']['config']['sortOrder']) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductCustomOptionsDataProvider.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductCustomOptionsDataProvider.php index b8e9155b8eab1..6de75e79b56da 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductCustomOptionsDataProvider.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductCustomOptionsDataProvider.php @@ -12,6 +12,9 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Product\Option as ProductOption; use Magento\Framework\DataObject; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\App\ObjectManager; +use Magento\Ui\DataProvider\Modifier\PoolInterface; /** * DataProvider for grid on Import Custom Options modal panel @@ -39,6 +42,11 @@ class ProductCustomOptionsDataProvider extends ProductDataProvider */ protected $productOptionValueModel; + /** + * @var MetadataPool + */ + private $metadataPool; + /** * @param string $name * @param string $primaryFieldName @@ -51,6 +59,8 @@ class ProductCustomOptionsDataProvider extends ProductDataProvider * @param \Magento\Ui\DataProvider\AddFilterToCollectionInterface[] $addFilterStrategies * @param array $meta * @param array $data + * @param PoolInterface|null $modifiersPool + * @param MetadataPool|null $metadataPool * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -64,7 +74,9 @@ public function __construct( array $addFieldStrategies = [], array $addFilterStrategies = [], array $meta = [], - array $data = [] + array $data = [], + PoolInterface $modifiersPool = null, + MetadataPool $metadataPool = null ) { parent::__construct( $name, @@ -74,16 +86,19 @@ public function __construct( $addFieldStrategies, $addFilterStrategies, $meta, - $data + $data, + $modifiersPool ); $this->request = $request; $this->productOptionRepository = $productOptionRepository; $this->productOptionValueModel = $productOptionValueModel; + $this->metadataPool = $metadataPool ?: ObjectManager::getInstance() + ->get(MetadataPool::class); } /** - * {@inheritdoc} + * @inheritdoc * @since 101.0.0 */ public function getData() @@ -95,9 +110,16 @@ public function getData() $this->getCollection()->getSelect()->where('e.entity_id != ?', $currentProductId); } + try { + $entityMetadata = $this->metadataPool->getMetadata(ProductInterface::class); + $linkField = $entityMetadata->getLinkField(); + } catch (\Exception $e) { + $linkField = 'entity_id'; + } + $this->getCollection()->getSelect()->distinct()->join( ['opt' => $this->getCollection()->getTable('catalog_product_option')], - 'opt.product_id = e.entity_id', + 'opt.product_id = e.' . $linkField, null ); $this->getCollection()->load(); diff --git a/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php b/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php index d1424d637937b..1aad46fc1e2f5 100644 --- a/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php +++ b/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php @@ -11,6 +11,7 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\DataObject; use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Serialize\Serializer\JsonHexTag; use Magento\Framework\View\Element\Block\ArgumentInterface; use Magento\Framework\Escaper; @@ -36,24 +37,32 @@ class Breadcrumbs extends DataObject implements ArgumentInterface */ private $escaper; + /** + * @var JsonHexTag + */ + private $jsonSerializer; + /** * @param Data $catalogData * @param ScopeConfigInterface $scopeConfig * @param Json|null $json * @param Escaper|null $escaper + * @param JsonHexTag|null $jsonSerializer * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( Data $catalogData, ScopeConfigInterface $scopeConfig, Json $json = null, - Escaper $escaper = null + Escaper $escaper = null, + JsonHexTag $jsonSerializer = null ) { parent::__construct(); $this->catalogData = $catalogData; $this->scopeConfig = $scopeConfig; $this->escaper = $escaper ?: ObjectManager::getInstance()->get(Escaper::class); + $this->jsonSerializer = $jsonSerializer ?: ObjectManager::getInstance()->get(JsonHexTag::class); } /** @@ -101,15 +110,14 @@ public function getProductName(): string */ public function getJsonConfigurationHtmlEscaped() : string { - return json_encode( + return $this->jsonSerializer->serialize( [ 'breadcrumbs' => [ 'categoryUrlSuffix' => $this->escaper->escapeHtml($this->getCategoryUrlSuffix()), 'useCategoryPathInUrl' => (int)$this->isCategoryUsedInProductUrl(), 'product' => $this->escaper->escapeHtml($this->getProductName()) ] - ], - JSON_HEX_TAG + ] ); } diff --git a/app/code/Magento/Catalog/etc/adminhtml/di.xml b/app/code/Magento/Catalog/etc/adminhtml/di.xml index b3c9230b3cfc6..4f905cd85f3d1 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/di.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/di.xml @@ -158,6 +158,10 @@ <item name="class" xsi:type="string">Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\TierPrice</item> <item name="sortOrder" xsi:type="number">150</item> </item> + <item name="custom_layout_update" xsi:type="array"> + <item name="class" xsi:type="string">Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\LayoutUpdate</item> + <item name="sortOrder" xsi:type="number">160</item> + </item> </argument> </arguments> </virtualType> @@ -249,4 +253,21 @@ </argument> </arguments> </type> + <type name="Magento\Eav\Model\Adminhtml\System\Config\Source\Inputtype"> + <arguments> + <argument name="optionsArray" xsi:type="array"> + <item name="450" xsi:type="array"> + <item name="value" xsi:type="string">datetime</item> + <item name="label" xsi:type="string" translate="true">Date and Time</item> + </item> + </argument> + </arguments> + </type> + <type name="Magento\Ui\DataProvider\Mapper\FormElement"> + <arguments> + <argument name="mappings" xsi:type="array"> + <item name="datetime" xsi:type="string">date</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index b83ed8591047a..c80363038ac60 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -65,7 +65,7 @@ </field> <field id="grid_per_page" translate="label comment" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Products per Page on Grid Default Value</label> - <comment>Must be in the allowed values list</comment> + <comment>Must be in the allowed values list.</comment> <validate>validate-per-page-value</validate> </field> <field id="list_per_page_values" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> @@ -75,7 +75,7 @@ </field> <field id="list_per_page" translate="label comment" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Products per Page on List Default Value</label> - <comment>Must be in the allowed values list</comment> + <comment>Must be in the allowed values list.</comment> <validate>validate-per-page-value</validate> </field> <field id="flat_catalog_category" translate="label" type="select" sortOrder="100" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> @@ -90,12 +90,12 @@ </field> <field id="default_sort_by" translate="label comment" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Product Listing Sort by</label> - <comment>Applies to category pages</comment> + <comment>Applies to category pages.</comment> <source_model>Magento\Catalog\Model\Config\Source\ListSort</source_model> </field> <field id="list_allow_all" translate="label comment" type="select" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Allow All Products per Page</label> - <comment>Whether to show "All" option in the "Show X Per Page" dropdown</comment> + <comment>Whether to show "All" option in the "Show X Per Page" dropdown.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="remember_pagination" translate="label comment" type="select" sortOrder="7" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> diff --git a/app/code/Magento/Catalog/etc/config.xml b/app/code/Magento/Catalog/etc/config.xml index 20511f4ff2295..8506d2ae03032 100644 --- a/app/code/Magento/Catalog/etc/config.xml +++ b/app/code/Magento/Catalog/etc/config.xml @@ -78,5 +78,12 @@ <thumbnail_position>stretch</thumbnail_position> </watermark> </design> + <general> + <validator_data> + <input_types> + <datetime>datetime</datetime> + </input_types> + </validator_data> + </general> </default> </config> diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index ff2fab73e0379..eda6dbd2d9d6f 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -73,6 +73,7 @@ <preference for="Magento\Catalog\Model\Product\Gallery\ImagesConfigFactoryInterface" type="Magento\Catalog\Model\Product\Gallery\ImagesConfigFactory" /> <preference for="Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface" type="Magento\Catalog\Model\Product\Configuration\Item\ItemResolverComposite" /> <preference for="Magento\Catalog\Api\Data\MassActionInterface" type="\Magento\Catalog\Model\MassAction" /> + <preference for="Magento\Catalog\Model\ProductLink\Data\ListCriteriaInterface" type="Magento\Catalog\Model\ProductLink\Data\ListCriteria" /> <type name="Magento\Customer\Model\ResourceModel\Visitor"> <plugin name="catalogLog" type="Magento\Catalog\Model\Plugin\Log" /> </type> @@ -237,6 +238,11 @@ <argument name="imageUploader" xsi:type="object">Magento\Catalog\CategoryImageUpload</argument> </arguments> </type> + <type name="Magento\Catalog\Model\Category\Attribute\Backend\Image"> + <arguments> + <argument name="imageUploader" xsi:type="object">Magento\Catalog\CategoryImageUpload</argument> + </arguments> + </type> <type name="Magento\Catalog\Model\Session"> <arguments> <argument name="storage" xsi:type="object">Magento\Catalog\Model\Session\Storage</argument> @@ -402,6 +408,9 @@ <item name="upsell" xsi:type="object">Magento\Catalog\Model\ProductLink\CollectionProvider\Upsell</item> <item name="related" xsi:type="object">Magento\Catalog\Model\ProductLink\CollectionProvider\Related</item> </argument> + <argument name="mapProviders" xsi:type="array"> + <item name="linked" xsi:type="object">Magento\Catalog\Model\ProductLink\CollectionProvider\LinkedMapProvider</item> + </argument> </arguments> </type> <type name="Magento\Catalog\Model\ProductLink\Converter\ConverterPool"> @@ -411,6 +420,28 @@ </argument> </arguments> </type> + <type name="Magento\Catalog\Model\Product\Option"> + <arguments> + <argument name="optionGroups" xsi:type="array"> + <item name="date" xsi:type="string">Magento\Catalog\Model\Product\Option\Type\Date</item> + <item name="file" xsi:type="string">Magento\Catalog\Model\Product\Option\Type\File</item> + <item name="select" xsi:type="string">Magento\Catalog\Model\Product\Option\Type\Select</item> + <item name="text" xsi:type="string">Magento\Catalog\Model\Product\Option\Type\Text</item> + </argument> + <argument name="optionTypesToGroups" xsi:type="array"> + <item name="field" xsi:type="const">Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_GROUP_TEXT</item> + <item name="area" xsi:type="const">Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_GROUP_TEXT</item> + <item name="file" xsi:type="const">Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_GROUP_FILE</item> + <item name="drop_down" xsi:type="const">Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_GROUP_SELECT</item> + <item name="radio" xsi:type="const">Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_GROUP_SELECT</item> + <item name="checkbox" xsi:type="const">Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_GROUP_SELECT</item> + <item name="multiple" xsi:type="const">Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_GROUP_SELECT</item> + <item name="date" xsi:type="const">Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_GROUP_DATE</item> + <item name="date_time" xsi:type="const">Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_GROUP_DATE</item> + <item name="time" xsi:type="const">Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_GROUP_DATE</item> + </argument> + </arguments> + </type> <type name="Magento\Catalog\Model\Product\Option\Validator\Pool"> <arguments> <argument name="validators" xsi:type="array"> @@ -1176,4 +1207,124 @@ </argument> </arguments> </type> + <type name="Magento\Catalog\Model\ProductLink\Repository"> + <arguments> + <argument name="entityCollectionProvider" xsi:type="object">Magento\Catalog\Model\ProductLink\CollectionProvider\Proxy</argument> + <argument name="linkInitializer" xsi:type="object">Magento\Catalog\Model\Product\Initialization\Helper\ProductLinks\Proxy</argument> + </arguments> + </type> + <type name="Magento\Catalog\Model\ProductLink\ProductLinkQuery"> + <arguments> + <argument name="collectionProvider" xsi:type="object">Magento\Catalog\Model\ProductLink\CollectionProvider\Proxy</argument> + </arguments> + </type> + <type name="Magento\Catalog\Model\Product\Attribute\LayoutUpdateManager"> + <arguments> + <argument name="themeFactory" xsi:type="object">Magento\Framework\View\Design\Theme\FlyweightFactory\Proxy</argument> + </arguments> + </type> + <type name="Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager"> + <arguments> + <argument name="themeFactory" xsi:type="object">Magento\Framework\View\Design\Theme\FlyweightFactory\Proxy</argument> + </arguments> + </type> + <type name="Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate"> + <arguments> + <argument name="manager" xsi:type="object">Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager\Proxy</argument> + </arguments> + </type> + <type name="Magento\Catalog\Model\Category\Attribute\Source\LayoutUpdate"> + <arguments> + <argument name="manager" xsi:type="object">Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager\Proxy</argument> + </arguments> + </type> + <type name="Magento\Catalog\Model\Product\Attribute\Backend\LayoutUpdate"> + <arguments> + <argument name="manager" xsi:type="object">Magento\Catalog\Model\Product\Attribute\LayoutUpdateManager\Proxy</argument> + </arguments> + </type> + <type name="Magento\Catalog\Model\Product\Attribute\Source\LayoutUpdate"> + <arguments> + <argument name="manager" xsi:type="object">Magento\Catalog\Model\Product\Attribute\LayoutUpdateManager\Proxy</argument> + </arguments> + </type> + <type name="Magento\Eav\Model\Config"> + <arguments> + <argument name="attributesForPreload" xsi:type="array"> + <item name="catalog_product" xsi:type="array"> + <item name="category_ids" xsi:type="string">catalog_product</item> + <item name="country_of_manufacture" xsi:type="string">catalog_product</item> + <item name="created_at" xsi:type="string">catalog_product</item> + <item name="custom_design" xsi:type="string">catalog_product</item> + <item name="custom_design_from" xsi:type="string">catalog_product</item> + <item name="custom_design_to" xsi:type="string">catalog_product</item> + <item name="custom_layout" xsi:type="string">catalog_product</item> + <item name="custom_layout_update" xsi:type="string">catalog_product</item> + <item name="description" xsi:type="string">catalog_product</item> + <item name="gallery" xsi:type="string">catalog_product</item> + <item name="has_options" xsi:type="string">catalog_product</item> + <item name="image" xsi:type="string">catalog_product</item> + <item name="image_label" xsi:type="string">catalog_product</item> + <item name="media_gallery" xsi:type="string">catalog_product</item> + <item name="meta_description" xsi:type="string">catalog_product</item> + <item name="meta_keyword" xsi:type="string">catalog_product</item> + <item name="meta_title" xsi:type="string">catalog_product</item> + <item name="minimal_price" xsi:type="string">catalog_product</item> + <item name="name" xsi:type="string">catalog_product</item> + <item name="news_from_date" xsi:type="string">catalog_product</item> + <item name="news_to_date" xsi:type="string">catalog_product</item> + <item name="old_id" xsi:type="string">catalog_product</item> + <item name="options_container" xsi:type="string">catalog_product</item> + <item name="page_layout" xsi:type="string">catalog_product</item> + <item name="price" xsi:type="string">catalog_product</item> + <item name="quantity_and_stock_status" xsi:type="string">catalog_product</item> + <item name="required_options" xsi:type="string">catalog_product</item> + <item name="short_description" xsi:type="string">catalog_product</item> + <item name="sku" xsi:type="string">catalog_product</item> + <item name="small_image" xsi:type="string">catalog_product</item> + <item name="small_image_label" xsi:type="string">catalog_product</item> + <item name="special_from_date" xsi:type="string">catalog_product</item> + <item name="special_price" xsi:type="string">catalog_product</item> + <item name="special_to_date" xsi:type="string">catalog_product</item> + <item name="status" xsi:type="string">catalog_product</item> + <item name="thumbnail" xsi:type="string">catalog_product</item> + <item name="thumbnail_label" xsi:type="string">catalog_product</item> + <item name="tier_price" xsi:type="string">catalog_product</item> + <item name="updated_at" xsi:type="string">catalog_product</item> + <item name="visibility" xsi:type="string">catalog_product</item> + <item name="weight" xsi:type="string">catalog_product</item> + </item> + <item name="catalog_category" xsi:type="array"> + <item name="all_children" xsi:type="string">catalog_category</item> + <item name="available_sort_by" xsi:type="string">catalog_category</item> + <item name="children" xsi:type="string">catalog_category</item> + <item name="children_count" xsi:type="string">catalog_category</item> + <item name="custom_apply_to_products" xsi:type="string">catalog_category</item> + <item name="custom_design" xsi:type="string">catalog_category</item> + <item name="custom_design_from" xsi:type="string">catalog_category</item> + <item name="custom_design_to" xsi:type="string">catalog_category</item> + <item name="custom_layout_update" xsi:type="string">catalog_category</item> + <item name="custom_use_parent_settings" xsi:type="string">catalog_category</item> + <item name="default_sort_by" xsi:type="string">catalog_category</item> + <item name="description" xsi:type="string">catalog_category</item> + <item name="display_mode" xsi:type="string">catalog_category</item> + <item name="filter_price_range" xsi:type="string">catalog_category</item> + <item name="image" xsi:type="string">catalog_category</item> + <item name="include_in_menu" xsi:type="string">catalog_category</item> + <item name="is_active" xsi:type="string">catalog_category</item> + <item name="is_anchor" xsi:type="string">catalog_category</item> + <item name="landing_page" xsi:type="string">catalog_category</item> + <item name="level" xsi:type="string">catalog_category</item> + <item name="meta_description" xsi:type="string">catalog_category</item> + <item name="meta_keywords" xsi:type="string">catalog_category</item> + <item name="meta_title" xsi:type="string">catalog_category</item> + <item name="name" xsi:type="string">catalog_category</item> + <item name="page_layout" xsi:type="string">catalog_category</item> + <item name="path" xsi:type="string">catalog_category</item> + <item name="path_in_store" xsi:type="string">catalog_category</item> + <item name="position" xsi:type="string">catalog_category</item> + </item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Catalog/etc/events.xml b/app/code/Magento/Catalog/etc/events.xml index f4345ce719a19..24186146c56f0 100644 --- a/app/code/Magento/Catalog/etc/events.xml +++ b/app/code/Magento/Catalog/etc/events.xml @@ -64,4 +64,7 @@ <event name="catalog_product_save_commit_after"> <observer name="magento_image_resize" instance="Magento\Catalog\Observer\ImageResizeAfterProductSave" /> </event> + <event name="catalog_category_prepare_save"> + <observer name="additional_authorization" instance="Magento\Catalog\Observer\CategoryDesignAuthorization" /> + </event> </config> diff --git a/app/code/Magento/Catalog/etc/webapi_rest/di.xml b/app/code/Magento/Catalog/etc/webapi_rest/di.xml index 44cdd473bf74e..bfbc05b12079d 100644 --- a/app/code/Magento/Catalog/etc/webapi_rest/di.xml +++ b/app/code/Magento/Catalog/etc/webapi_rest/di.xml @@ -22,4 +22,10 @@ <type name="Magento\Catalog\Api\ProductCustomOptionRepositoryInterface"> <plugin name="updateProductCustomOptionsAttributes" type="Magento\Catalog\Plugin\Model\Product\Option\UpdateProductCustomOptionsAttributes"/> </type> + <type name="Magento\Catalog\Api\ProductRepositoryInterface"> + <plugin name="product_authorization" type="Magento\Catalog\Plugin\ProductAuthorization" /> + </type> + <type name="Magento\Catalog\Api\CategoryRepositoryInterface"> + <plugin name="category_authorization" type="Magento\Catalog\Plugin\CategoryAuthorization" /> + </type> </config> diff --git a/app/code/Magento/Catalog/etc/webapi_soap/di.xml b/app/code/Magento/Catalog/etc/webapi_soap/di.xml index 44cdd473bf74e..bfbc05b12079d 100644 --- a/app/code/Magento/Catalog/etc/webapi_soap/di.xml +++ b/app/code/Magento/Catalog/etc/webapi_soap/di.xml @@ -22,4 +22,10 @@ <type name="Magento\Catalog\Api\ProductCustomOptionRepositoryInterface"> <plugin name="updateProductCustomOptionsAttributes" type="Magento\Catalog\Plugin\Model\Product\Option\UpdateProductCustomOptionsAttributes"/> </type> + <type name="Magento\Catalog\Api\ProductRepositoryInterface"> + <plugin name="product_authorization" type="Magento\Catalog\Plugin\ProductAuthorization" /> + </type> + <type name="Magento\Catalog\Api\CategoryRepositoryInterface"> + <plugin name="category_authorization" type="Magento\Catalog\Plugin\CategoryAuthorization" /> + </type> </config> diff --git a/app/code/Magento/Catalog/i18n/en_US.csv b/app/code/Magento/Catalog/i18n/en_US.csv index 9b7f8a2b07730..555871ef32c26 100644 --- a/app/code/Magento/Catalog/i18n/en_US.csv +++ b/app/code/Magento/Catalog/i18n/en_US.csv @@ -814,4 +814,5 @@ Details,Details "A total of %1 record(s) haven't been deleted. Please see server logs for more details.","A total of %1 record(s) haven't been deleted. Please see server logs for more details." "Are you sure you want to delete this category?","Are you sure you want to delete this category?" "Attribute Set Information","Attribute Set Information" +"Failed to retrieve product links for ""%1""","Failed to retrieve product links for ""%1""" diff --git a/app/code/Magento/Catalog/registration.php b/app/code/Magento/Catalog/registration.php index fb94e04aa2be0..e18d43d9a36d8 100644 --- a/app/code/Magento/Catalog/registration.php +++ b/app/code/Magento/Catalog/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Catalog', __DIR__); diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml index f020eddc35dbd..64384ac391a8d 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +use Magento\Catalog\Helper\Data; // phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> @@ -63,7 +64,12 @@ function bindAttributeInputType() { checkOptionsPanelVisibility(); switchDefaultValueField(); - if($('frontend_input') && ($('frontend_input').value=='boolean' || $('frontend_input').value=='select' || $('frontend_input').value=='multiselect' || $('frontend_input').value=='price')){ + if ($('frontend_input') + && ($('frontend_input').value=='boolean' + || $('frontend_input').value=='select' + || $('frontend_input').value=='multiselect' + || $('frontend_input').value=='price') + ){ if($('is_filterable') && !$('is_filterable').getAttribute('readonly')){ $('is_filterable').disabled = false; } @@ -75,8 +81,7 @@ function bindAttributeInputType() if($('backend_type').options[i].value=='int') $('backend_type').selectedIndex = i; } } - } - else { + } else { if($('is_filterable')){ $('is_filterable').selectedIndex=0; $('is_filterable').disabled = true; @@ -172,6 +177,7 @@ function switchDefaultValueField() break; case 'date': + case 'datetime': defaultValueDateVisibility = true; break; @@ -202,21 +208,22 @@ function switchDefaultValueField() setRowVisibility('frontend_class', false); break; - <?php foreach ($this->helper(Magento\Catalog\Helper\Data::class)->getAttributeHiddenFields() as $type => $fields) :?> + <?php // phpcs:ignore Magento2.Templates.ThisInTemplate ?> + <?php foreach ($this->helper(Data::class)->getAttributeHiddenFields() as $type => $fields): ?> case '<?= $block->escapeJs($type) ?>': var isFrontTabHidden = false; - <?php foreach ($fields as $one) :?> - <?php if ($one == '_front_fieldset') :?> + <?php foreach ($fields as $one): ?> + <?php if ($one == '_front_fieldset'): ?> getFrontTab().hide(); isFrontTabHidden = true; - <?php elseif ($one == '_default_value') :?> + <?php elseif ($one == '_default_value'): ?> defaultValueTextVisibility = defaultValueTextareaVisibility = defaultValueDateVisibility = defaultValueYesnoVisibility = false; - <?php elseif ($one == '_scope') :?> + <?php elseif ($one == '_scope'): ?> scopeVisibility = false; - <?php else :?> + <?php else: ?> setRowVisibility('<?= $block->escapeJs($one) ?>', false); <?php endif; ?> <?php endforeach; ?> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/date.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/date.phtml index 8adffb752187b..68a7a3a69cfd3 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/date.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/date.phtml @@ -7,7 +7,7 @@ <?php /* @var $block \Magento\Catalog\Block\Product\View\Options\Type\Date */ ?> <?php $_option = $block->getOption(); ?> <?php $_optionId = (int)$_option->getId(); ?> -<div class="admin__field field<?= $_option->getIsRequire() ? ' required _required' : '' ?>"> +<div class="admin__field field<?= $_option->getIsRequire() ? ' required' : '' ?>"> <label class="label admin__field-label"> <?= $block->escapeHtml($_option->getTitle()) ?> <?= /* @noEscape */ $block->getFormattedPrice() ?> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/select.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/select.phtml index 2218ce5d29671..243fdb6e603a0 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/select.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/select.phtml @@ -6,7 +6,7 @@ ?> <?php /* @var $block \Magento\Catalog\Block\Product\View\Options\Type\Select */ ?> <?php $_option = $block->getOption(); ?> -<div class="admin__field field<?= $_option->getIsRequire() ? ' required _required' : '' ?>"> +<div class="admin__field field<?= $_option->getIsRequire() ? ' _required' : '' ?>"> <label class="label admin__field-label"> <span><?= $block->escapeHtml($_option->getTitle()) ?></span> </label> diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml index 992093c4a6658..d3689a0db1306 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml @@ -354,7 +354,6 @@ <additionalClasses> <class name="admin__field-small">true</class> </additionalClasses> - <addBefore>$</addBefore> <label translate="true">Layered Navigation Price Step</label> </settings> </field> @@ -489,6 +488,15 @@ </imports> </settings> </field> + <field name="custom_layout_update_file" component="Magento_Catalog/js/components/use-parent-settings/select" sortOrder="205" formElement="select"> + <settings> + <dataType>string</dataType> + <label translate="true">Custom Layout Update</label> + <imports> + <link name="serviceDisabled">${ $.parentName }.custom_use_parent_settings:checked || $.data.serviceDisabled</link> + </imports> + </settings> + </field> <field name="custom_apply_to_products" component="Magento_Catalog/js/components/use-parent-settings/single-checkbox" sortOrder="210" formElement="checkbox"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> @@ -528,10 +536,7 @@ <item name="type" xsi:type="string">group</item> <item name="config" xsi:type="array"> <item name="additionalClasses" xsi:type="string">admin__control-grouped-date</item> - <item name="label" xsi:type="string" translate="true">Schedule Update From</item> - <item name="required" xsi:type="boolean">false</item> <item name="breakLine" xsi:type="boolean">false</item> - <item name="scopeLabel" xsi:type="string">[STORE VIEW]</item> </item> </argument> <field name="custom_design_from" sortOrder="230" formElement="date"> @@ -541,6 +546,7 @@ </additionalClasses> <dataType>string</dataType> <label translate="true">Schedule Update From</label> + <scopeLabel>[STORE VIEW]</scopeLabel> </settings> </field> <field name="custom_design_to" sortOrder="240" formElement="date"> diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml index 6c5d37a92ea4a..3a6621137ed5a 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_attribute_add_form.xml @@ -101,6 +101,7 @@ <item name="textarea" xsi:type="string">textarea</item> <item name="texteditor" xsi:type="string">texteditor</item> <item name="date" xsi:type="string">date</item> + <item name="datetime" xsi:type="string">datetime</item> <item name="boolean" xsi:type="string">boolean</item> <item name="multiselect" xsi:type="string">multiselect</item> <item name="select" xsi:type="string">select</item> @@ -287,6 +288,7 @@ <item name="textarea" xsi:type="string">textarea</item> <item name="texteditor" xsi:type="string">texteditor</item> <item name="date" xsi:type="string">date</item> + <item name="datetime" xsi:type="string">datetime</item> <item name="boolean" xsi:type="string">boolean</item> <item name="multiselect" xsi:type="string">multiselect</item> <item name="select" xsi:type="string">select</item> @@ -376,6 +378,29 @@ <dataScope>default_value_date</dataScope> </settings> </field> + <field name="default_value_datetime" component="Magento_Catalog/js/components/visible-on-option/date" sortOrder="35" formElement="date"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="valuesForOptions" xsi:type="array"> + <item name="datetime" xsi:type="string">datetime</item> + </item> + </item> + </argument> + <settings> + <dataType>text</dataType> + <label translate="true">Default Value</label> + <dataScope>default_value_datetime</dataScope> + </settings> + <formElements> + <date> + <settings> + <options> + <option name="showsTime" xsi:type="boolean">true</option> + </options> + </settings> + </date> + </formElements> + </field> <field name="is_unique" component="Magento_Catalog/js/components/visible-on-option/yesno" sortOrder="40" formElement="checkbox"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> @@ -412,6 +437,7 @@ <item name="textarea" xsi:type="string">textarea</item> <item name="texteditor" xsi:type="string">texteditor</item> <item name="date" xsi:type="string">date</item> + <item name="datetime" xsi:type="string">date</item> <item name="boolean" xsi:type="string">boolean</item> <item name="multiselect" xsi:type="string">multiselect</item> <item name="select" xsi:type="string">select</item> diff --git a/app/code/Magento/Catalog/view/adminhtml/web/catalog/category/assign-products.js b/app/code/Magento/Catalog/view/adminhtml/web/catalog/category/assign-products.js index 829c04c13106d..35b8958f2f8fd 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/catalog/category/assign-products.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/catalog/category/assign-products.js @@ -51,15 +51,29 @@ define([ */ function categoryProductRowClick(grid, event) { var trElement = Event.findElement(event, 'tr'), - isInput = Event.element(event).tagName === 'INPUT', + eventElement = Event.element(event), + isInputCheckbox = eventElement.tagName === 'INPUT' && eventElement.type === 'checkbox', + isInputPosition = grid.targetElement && + grid.targetElement.tagName === 'INPUT' && + grid.targetElement.name === 'position', checked = false, checkbox = null; - if (trElement) { + if (eventElement.tagName === 'LABEL' && + trElement.querySelector('#' + eventElement.htmlFor) && + trElement.querySelector('#' + eventElement.htmlFor).type === 'checkbox' + ) { + event.stopPropagation(); + trElement.querySelector('#' + eventElement.htmlFor).trigger('click'); + + return; + } + + if (trElement && !isInputPosition) { checkbox = Element.getElementsBySelector(trElement, 'input'); if (checkbox[0]) { - checked = isInput ? checkbox[0].checked : !checkbox[0].checked; + checked = isInputCheckbox ? checkbox[0].checked : !checkbox[0].checked; gridJsObject.setCheckboxChecked(checkbox[0], checked); } } diff --git a/app/code/Magento/Catalog/view/base/requirejs-config.js b/app/code/Magento/Catalog/view/base/requirejs-config.js new file mode 100644 index 0000000000000..2fc0bbcb8c00d --- /dev/null +++ b/app/code/Magento/Catalog/view/base/requirejs-config.js @@ -0,0 +1,16 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +var config = { + map: { + '*': { + priceBox: 'Magento_Catalog/js/price-box', + priceOptionDate: 'Magento_Catalog/js/price-option-date', + priceOptionFile: 'Magento_Catalog/js/price-option-file', + priceOptions: 'Magento_Catalog/js/price-options', + priceUtils: 'Magento_Catalog/js/price-utils' + } + } +}; diff --git a/app/code/Magento/Catalog/view/base/templates/product/composite/fieldset/options/view/checkable.phtml b/app/code/Magento/Catalog/view/base/templates/product/composite/fieldset/options/view/checkable.phtml index dbc064665d3fe..78d2883d7401a 100644 --- a/app/code/Magento/Catalog/view/base/templates/product/composite/fieldset/options/view/checkable.phtml +++ b/app/code/Magento/Catalog/view/base/templates/product/composite/fieldset/options/view/checkable.phtml @@ -53,7 +53,7 @@ if ($option) : ?> } ?> - <div class="field choice admin__field admin__field-option <?= /* @noEscape */ $option->getIsRequire() ? 'required': '' ?>"> + <div class="field choice admin__field admin__field-option"> <input type="<?= $block->escapeHtmlAttr($optionType) ?>" class="<?= $optionType === Option::OPTION_TYPE_RADIO ? 'radio admin__control-radio' diff --git a/app/code/Magento/Catalog/view/base/web/js/price-box.js b/app/code/Magento/Catalog/view/base/web/js/price-box.js index 02ae6eadef672..6ca7f24b90c36 100644 --- a/app/code/Magento/Catalog/view/base/web/js/price-box.js +++ b/app/code/Magento/Catalog/view/base/web/js/price-box.js @@ -49,6 +49,7 @@ define([ box.on('reloadPrice', this.reloadPrice.bind(this)); box.on('updatePrice', this.onUpdatePrice.bind(this)); + box.trigger('price-box-initialized'); }, /** diff --git a/app/code/Magento/Catalog/view/frontend/requirejs-config.js b/app/code/Magento/Catalog/view/frontend/requirejs-config.js index 55df18afeb024..3674d54340544 100644 --- a/app/code/Magento/Catalog/view/frontend/requirejs-config.js +++ b/app/code/Magento/Catalog/view/frontend/requirejs-config.js @@ -11,11 +11,6 @@ var config = { upsellProducts: 'Magento_Catalog/js/upsell-products', productListToolbarForm: 'Magento_Catalog/js/product/list/toolbar', catalogGallery: 'Magento_Catalog/js/gallery', - priceBox: 'Magento_Catalog/js/price-box', - priceOptionDate: 'Magento_Catalog/js/price-option-date', - priceOptionFile: 'Magento_Catalog/js/price-option-file', - priceOptions: 'Magento_Catalog/js/price-options', - priceUtils: 'Magento_Catalog/js/price-utils', catalogAddToCart: 'Magento_Catalog/js/catalog-add-to-cart' } }, diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/compare/list.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/compare/list.phtml index 55772388d44bf..0bea3ca03dee8 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/compare/list.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/compare/list.phtml @@ -130,6 +130,8 @@ default :?> <?php if (is_string($block->getProductAttributeValue($item, $attribute))) :?> <?= /* @noEscape */ $helper->productAttribute($item, $block->getProductAttributeValue($item, $attribute), $attribute->getAttributeCode()) ?> + <?php else : ?> + <?= $block->escapeHtml($helper->productAttribute($item, $block->getProductAttributeValue($item, $attribute), $attribute->getAttributeCode())) ?> <?php endif; ?> <?php break; } ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml index 8c32302cf7c29..554caf6026001 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml @@ -13,14 +13,16 @@ use Magento\Framework\App\Action\Action; * Product list template * * @var $block \Magento\Catalog\Block\Product\ListProduct + * @var \Magento\Framework\Escaper $escaper */ ?> <?php $_productCollection = $block->getLoadedProductCollection(); +/** @var \Magento\Catalog\Helper\Output $_helper */ $_helper = $this->helper(Magento\Catalog\Helper\Output::class); ?> <?php if (!$_productCollection->count()) :?> - <div class="message info empty"><div><?= $block->escapeHtml(__('We can\'t find products matching the selection.')) ?></div></div> + <div class="message info empty"><div><?= $escaper->escapeHtml(__('We can\'t find products matching the selection.')) ?></div></div> <?php else :?> <?= $block->getToolbarHtml() ?> <?= $block->getAdditionalHtml() ?> @@ -55,7 +57,7 @@ $_helper = $this->helper(Magento\Catalog\Helper\Output::class); } ?> <?php // Product Image ?> - <a href="<?= $block->escapeUrl($_product->getProductUrl()) ?>" + <a href="<?= $escaper->escapeUrl($_product->getProductUrl()) ?>" class="product photo product-item-photo" tabindex="-1"> <?= $productImage->toHtml() ?> @@ -66,7 +68,7 @@ $_helper = $this->helper(Magento\Catalog\Helper\Output::class); ?> <strong class="product name product-item-name"> <a class="product-item-link" - href="<?= $block->escapeUrl($_product->getProductUrl()) ?>"> + href="<?= $escaper->escapeUrl($_product->getProductUrl()) ?>"> <?= /* @noEscape */ $_helper->productAttribute($_product, $_product->getName(), 'name') ?> </a> </strong> @@ -77,13 +79,13 @@ $_helper = $this->helper(Magento\Catalog\Helper\Output::class); <?php endif; ?> <div class="product-item-inner"> - <div class="product actions product-item-actions"<?= strpos($pos, $viewMode . '-actions') ? $block->escapeHtmlAttr($position) : '' ?>> - <div class="actions-primary"<?= strpos($pos, $viewMode . '-primary') ? $block->escapeHtmlAttr($position) : '' ?>> + <div class="product actions product-item-actions"<?= strpos($pos, $viewMode . '-actions') ? $escaper->escapeHtmlAttr($position) : '' ?>> + <div class="actions-primary"<?= strpos($pos, $viewMode . '-primary') ? $escaper->escapeHtmlAttr($position) : '' ?>> <?php if ($_product->isSaleable()) :?> <?php $postParams = $block->getAddToCartPostParams($_product); ?> <form data-role="tocart-form" - data-product-sku="<?= $block->escapeHtml($_product->getSku()) ?>" - action="<?= $block->escapeUrl($postParams['action']) ?>" + data-product-sku="<?= $escaper->escapeHtml($_product->getSku()) ?>" + action="<?= $escaper->escapeUrl($postParams['action']) ?>" method="post"> <input type="hidden" name="product" @@ -92,20 +94,20 @@ $_helper = $this->helper(Magento\Catalog\Helper\Output::class); value="<?= /* @noEscape */ $postParams['data'][Action::PARAM_NAME_URL_ENCODED] ?>"> <?= $block->getBlockHtml('formkey') ?> <button type="submit" - title="<?= $block->escapeHtmlAttr(__('Add to Cart')) ?>" + title="<?= $escaper->escapeHtmlAttr(__('Add to Cart')) ?>" class="action tocart primary"> - <span><?= $block->escapeHtml(__('Add to Cart')) ?></span> + <span><?= $escaper->escapeHtml(__('Add to Cart')) ?></span> </button> </form> <?php else :?> <?php if ($_product->isAvailable()) :?> - <div class="stock available"><span><?= $block->escapeHtml(__('In stock')) ?></span></div> + <div class="stock available"><span><?= $escaper->escapeHtml(__('In stock')) ?></span></div> <?php else :?> - <div class="stock unavailable"><span><?= $block->escapeHtml(__('Out of stock')) ?></span></div> + <div class="stock unavailable"><span><?= $escaper->escapeHtml(__('Out of stock')) ?></span></div> <?php endif; ?> <?php endif; ?> </div> - <div data-role="add-to-links" class="actions-secondary"<?= strpos($pos, $viewMode . '-secondary') ? $block->escapeHtmlAttr($position) : '' ?>> + <div data-role="add-to-links" class="actions-secondary"<?= strpos($pos, $viewMode . '-secondary') ? $escaper->escapeHtmlAttr($position) : '' ?>> <?php if ($addToBlock = $block->getChildBlock('addto')) :?> <?= $addToBlock->setProduct($_product)->getChildHtml() ?> <?php endif; ?> @@ -114,9 +116,9 @@ $_helper = $this->helper(Magento\Catalog\Helper\Output::class); <?php if ($showDescription) :?> <div class="product description product-item-description"> <?= /* @noEscape */ $_helper->productAttribute($_product, $_product->getShortDescription(), 'short_description') ?> - <a href="<?= $block->escapeUrl($_product->getProductUrl()) ?>" + <a href="<?= $escaper->escapeUrl($_product->getProductUrl()) ?>" title="<?= /* @noEscape */ $_productNameStripped ?>" - class="action more"><?= $block->escapeHtml(__('Learn More')) ?></a> + class="action more"><?= $escaper->escapeHtml(__('Learn More')) ?></a> </div> <?php endif; ?> </div> @@ -132,7 +134,7 @@ $_helper = $this->helper(Magento\Catalog\Helper\Output::class); { "[data-role=tocart-form], .form.map.checkout": { "catalogAddToCart": { - "product_sku": "<?= $block->escapeJs($_product->getSku()) ?>" + "product_sku": "<?= $escaper->escapeJs($_product->getSku()) ?>" } } } diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml index 926e7c78a7df0..fc4d85044a8d6 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml @@ -6,6 +6,8 @@ // phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis // phpcs:disable Generic.WhiteSpace.ScopeIndent.Incorrect +// phpcs:disable Generic.Files.LineLength +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundHelper /* @var $block \Magento\Catalog\Block\Product\AbstractProduct */ ?> @@ -23,6 +25,7 @@ switch ($type = $block->getType()) { $items = $block->getAllItems(); $limit = $block->getPositionLimit(); $shuffle = (int) $block->isShuffled(); + $isWeightedRandom = (int) $block->getRotation()->isWeightedRandom($block->getProductListType()); $canItemsAddToCart = $block->canItemsAddToCart(); $showAddTo = true; @@ -43,6 +46,7 @@ switch ($type = $block->getType()) { $items = $block->getItems(); $limit = 0; $shuffle = 0; + $isWeightedRandom = 0; $canItemsAddToCart = $block->canItemsAddToCart(); $showAddTo = true; @@ -62,6 +66,7 @@ switch ($type = $block->getType()) { $items = $block->getAllItems(); $limit = $block->getPositionLimit(); $shuffle = (int) $block->isShuffled(); + $isWeightedRandom = (int) $block->getRotation()->isWeightedRandom($block->getProductListType()); $showAddTo = false; $showCart = false; @@ -82,6 +87,7 @@ switch ($type = $block->getType()) { $items = $block->getItemCollection()->getItems(); $limit = $block->getItemLimit('upsell'); $shuffle = 0; + $isWeightedRandom = 0; $showAddTo = false; $showCart = false; @@ -152,22 +158,22 @@ switch ($type = $block->getType()) { } ?> -<?php if ($exist) :?> +<?php if ($exist):?> -<?php if ($type == 'related' || $type == 'upsell') :?> -<?php if ($type == 'related') :?> -<div class="block <?= $block->escapeHtmlAttr($class) ?>" data-mage-init='{"relatedProducts":{"relatedCheckbox":".related.checkbox"}}' data-limit="<?= $block->escapeHtmlAttr($limit) ?>" data-shuffle="<?= /* @noEscape */ $shuffle ?>"> - <?php else :?> - <div class="block <?= $block->escapeHtmlAttr($class) ?>" data-mage-init='{"upsellProducts":{}}' data-limit="<?= $block->escapeHtmlAttr($limit) ?>" data-shuffle="<?= /* @noEscape */ $shuffle ?>"> +<?php if ($type == 'related' || $type == 'upsell'):?> +<?php if ($type == 'related'):?> +<div class="block <?= $block->escapeHtmlAttr($class) ?>" data-mage-init='{"relatedProducts":{"relatedCheckbox":".related.checkbox"}}' data-limit="<?= $block->escapeHtmlAttr($limit) ?>" data-shuffle="<?= /* @noEscape */ $shuffle ?>" data-shuffle-weighted="<?= /* @noEscape */ $isWeightedRandom ?>"> + <?php else:?> + <div class="block <?= $block->escapeHtmlAttr($class) ?>" data-mage-init='{"upsellProducts":{}}' data-limit="<?= $block->escapeHtmlAttr($limit) ?>" data-shuffle="<?= /* @noEscape */ $shuffle ?>" data-shuffle-weighted="<?= /* @noEscape */ $isWeightedRandom ?>"> <?php endif; ?> - <?php else :?> + <?php else:?> <div class="block <?= $block->escapeHtmlAttr($class) ?>"> <?php endif; ?> <div class="block-title title"> <strong id="block-<?= $block->escapeHtmlAttr($class) ?>-heading" role="heading" aria-level="2"><?= $block->escapeHtml($title) ?></strong> </div> <div class="block-content content" aria-labelledby="block-<?= $block->escapeHtmlAttr($class) ?>-heading"> - <?php if ($type == 'related' && $canItemsAddToCart) :?> + <?php if ($type == 'related' && $canItemsAddToCart):?> <div class="block-actions"> <?= $block->escapeHtml(__('Check items to add to the cart or')) ?> <button type="button" class="action select" data-role="select-all"><span><?= $block->escapeHtml(__('select all')) ?></span></button> @@ -175,16 +181,16 @@ switch ($type = $block->getType()) { <?php endif; ?> <div class="products wrapper grid products-grid products-<?= $block->escapeHtmlAttr($type) ?>"> <ol class="products list items product-items"> - <?php foreach ($items as $_item) :?> + <?php foreach ($items as $_item):?> <?php $available = ''; ?> - <?php if (!$_item->isComposite() && $_item->isSaleable() && $type == 'related') :?> - <?php if (!$_item->getRequiredOptions()) :?> + <?php if (!$_item->isComposite() && $_item->isSaleable() && $type == 'related'):?> + <?php if (!$_item->getRequiredOptions()):?> <?php $available = 'related-available'; ?> <?php endif; ?> <?php endif; ?> - <?php if ($type == 'related' || $type == 'upsell') :?> - <li class="item product product-item" style="display: none;"> - <?php else :?> + <?php if ($type == 'related' || $type == 'upsell'):?> + <li class="item product product-item" style="display: none;" data-shuffle-group="<?= $block->escapeHtmlAttr($_item->getPriority()) ?>" > + <?php else:?> <li class="item product product-item"> <?php endif; ?> <div class="product-item-info <?= /* @noEscape */ $available ?>"> @@ -199,12 +205,12 @@ switch ($type = $block->getType()) { <?= /* @noEscape */ $block->getProductPrice($_item) ?> - <?php if ($templateType) :?> + <?php if ($templateType):?> <?= $block->getReviewsSummaryHtml($_item, $templateType) ?> <?php endif; ?> - <?php if ($canItemsAddToCart && !$_item->isComposite() && $_item->isSaleable() && $type == 'related') :?> - <?php if (!$_item->getRequiredOptions()) :?> + <?php if ($canItemsAddToCart && !$_item->isComposite() && $_item->isSaleable() && $type == 'related'):?> + <?php if (!$_item->getRequiredOptions()):?> <div class="field choice related"> <input type="checkbox" class="checkbox related" id="related-checkbox<?= $block->escapeHtmlAttr($_item->getId()) ?>" name="related_products[]" value="<?= $block->escapeHtmlAttr($_item->getId()) ?>" /> <label class="label" for="related-checkbox<?= $block->escapeHtmlAttr($_item->getId()) ?>"><span><?= $block->escapeHtml(__('Add to Cart')) ?></span></label> @@ -212,16 +218,16 @@ switch ($type = $block->getType()) { <?php endif; ?> <?php endif; ?> - <?php if ($showAddTo || $showCart) :?> + <?php if ($showAddTo || $showCart):?> <div class="product actions product-item-actions"> - <?php if ($showCart) :?> + <?php if ($showCart):?> <div class="actions-primary"> - <?php if ($_item->isSaleable()) :?> - <?php if ($_item->getTypeInstance()->hasRequiredOptions($_item)) :?> + <?php if ($_item->isSaleable()):?> + <?php if ($_item->getTypeInstance()->hasRequiredOptions($_item)):?> <button class="action tocart primary" data-mage-init='{"redirectUrl": {"url": "<?= $block->escapeUrl($block->getAddToCartUrl($_item)) ?>"}}' type="button" title="<?= $block->escapeHtmlAttr(__('Add to Cart')) ?>"> <span><?= $block->escapeHtml(__('Add to Cart')) ?></span> </button> - <?php else :?> + <?php else:?> <?php $postDataHelper = $this->helper(Magento\Framework\Data\Helper\PostHelper::class); $postData = $postDataHelper->getPostData($block->escapeUrl($block->getAddToCartUrl($_item)), ['product' => $_item->getEntityId()]) ?> @@ -231,19 +237,19 @@ switch ($type = $block->getType()) { <span><?= $block->escapeHtml(__('Add to Cart')) ?></span> </button> <?php endif; ?> - <?php else :?> - <?php if ($_item->getIsSalable()) :?> + <?php else:?> + <?php if ($_item->getIsSalable()):?> <div class="stock available"><span><?= $block->escapeHtml(__('In stock')) ?></span></div> - <?php else :?> + <?php else:?> <div class="stock unavailable"><span><?= $block->escapeHtml(__('Out of stock')) ?></span></div> <?php endif; ?> <?php endif; ?> </div> <?php endif; ?> - <?php if ($showAddTo) :?> + <?php if ($showAddTo):?> <div class="secondary-addto-links actions-secondary" data-role="add-to-links"> - <?php if ($addToBlock = $block->getChildBlock('addto')) :?> + <?php if ($addToBlock = $block->getChildBlock('addto')):?> <?= $addToBlock->setProduct($_item)->getChildHtml() ?> <?php endif; ?> </div> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/listing.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/listing.phtml index b776fd4f7e193..6cebd51284f48 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/listing.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/listing.phtml @@ -16,7 +16,6 @@ */ ?> <?php -$start = microtime(true); $_productCollection = $block->getLoadedProductCollection(); $_helper = $this->helper(Magento\Catalog\Helper\Output::class); ?> @@ -98,4 +97,3 @@ $_helper = $this->helper(Magento\Catalog\Helper\Output::class); </div> <?= $block->getToolbarHtml() ?> <?php endif; ?> -<?= $time_taken = microtime(true) - $start ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml index eb2bde647f9b1..4bfdbb7bc24bc 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml @@ -9,13 +9,16 @@ <meta property="og:type" content="product" /> <meta property="og:title" - content="<?= /* @noEscape */ $block->stripTags($block->getProduct()->getName()) ?>" /> + content="<?= $block->escapeHtmlAttr($block->stripTags($block->getProduct()->getName())) ?>" /> <meta property="og:image" content="<?= $block->escapeUrl($block->getImage($block->getProduct(), 'product_base_image')->getImageUrl()) ?>" /> <meta property="og:description" - content="<?= /* @noEscape */ $block->stripTags($block->getProduct()->getShortDescription()) ?>" /> + content="<?= $block->escapeHtmlAttr($block->stripTags($block->getProduct()->getShortDescription())) ?>" /> <meta property="og:url" content="<?= $block->escapeUrl($block->getProduct()->getProductUrl()) ?>" /> -<?php if ($priceAmount = $block->getProduct()->getPriceInfo()->getPrice(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE)->getAmount()) :?> +<?php if ($priceAmount = $block->getProduct() + ->getPriceInfo() + ->getPrice(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE) + ->getAmount()):?> <meta property="product:price:amount" content="<?= $block->escapeHtmlAttr($priceAmount) ?>"/> <?= $block->getChildHtml('meta.currency') ?> <?php endif;?> diff --git a/app/code/Magento/Catalog/view/frontend/web/js/related-products.js b/app/code/Magento/Catalog/view/frontend/web/js/related-products.js index c875dd8f5d2c7..bc54fdeac5c28 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/related-products.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/related-products.js @@ -28,10 +28,14 @@ define([ _create: function () { $(this.options.selectAllLink, this.element).on('click', $.proxy(this._selectAllRelated, this)); $(this.options.relatedCheckbox, this.element).on('click', $.proxy(this._addRelatedToProduct, this)); + + if (this.element.data('shuffle')) { + this._shuffle(this.element.find(this.options.elementsSelector)); + } this._showRelatedProducts( this.element.find(this.options.elementsSelector), this.element.data('limit'), - this.element.data('shuffle') + this.element.data('shuffle-weighted') ); }, @@ -69,24 +73,66 @@ define([ ); }, + /* jscs:disable */ + /* eslint-disable */ /** * Show related products according to limit. Shuffle if needed. * @param {*} elements * @param {*} limit - * @param {*} shuffle + * @param weightedRandom * @private */ - _showRelatedProducts: function (elements, limit, shuffle) { - var index; - - if (shuffle) { - this._shuffle(elements); - } + _showRelatedProducts: function (elements, limit, weightedRandom) { + var index, weights = [], random = [], weight = 2, shown = 0, $element, currentGroup, prevGroup; if (limit === 0) { limit = elements.length; } + if (weightedRandom && limit > 0 && limit < elements.length) { + for (index = 0; index < limit; index++) { + $element = $(elements[index]); + if ($element.data('shuffle-group') !== '') { + break; + } + $element.show(); + shown++; + } + limit -= shown; + for (index = elements.length - 1; index >= 0; index--) { + $element = $(elements[index]); + currentGroup = $element.data('shuffle-group'); + if (currentGroup !== '') { + weights.push([index, Math.log(weight)]); + if (typeof prevGroup !== 'undefined' && prevGroup !== currentGroup) { + weight += 2; + } + prevGroup = currentGroup; + } + } + + if (weights.length === 0) { + return; + } + + for (index = 0; index < weights.length; index++) { + random.push([weights[index][0], Math.pow(Math.random(), 1 / weights[index][1])]); + } + + random.sort(function(a, b) { + a = a[1]; + b = b[1]; + return a < b ? 1 : (a > b ? -1 : 0); + }); + index = 0; + while (limit) { + $(elements[random[index][0]]).show(); + limit--; + index++ + } + return; + } + for (index = 0; index < limit; index++) { $(elements[index]).show(); } @@ -96,12 +142,19 @@ define([ /* eslint-disable */ /** * Shuffle an array - * @param {Array} o + * @param {Array} elements * @returns {*} */ - _shuffle: function shuffle(o) { //v1.0 - for (var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x); - return o; + _shuffle: function shuffle(elements) { + var parent, child, lastSibling; + if (elements.length) { + parent = $(elements[0]).parent(); + } + while (elements.length) { + child = elements.splice(Math.floor(Math.random() * elements.length), 1)[0]; + lastSibling = parent.find('[data-shuffle-group="' + $(child).data('shuffle-group') + '"]').last(); + lastSibling.after(child); + } } /* jscs:disable */ diff --git a/app/code/Magento/Catalog/view/frontend/web/js/upsell-products.js b/app/code/Magento/Catalog/view/frontend/web/js/upsell-products.js index da2526d5679c8..867011d3d1646 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/upsell-products.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/upsell-products.js @@ -19,33 +19,78 @@ define([ * @private */ _create: function () { + if (this.element.data('shuffle')) { + this._shuffle(this.element.find(this.options.elementsSelector)); + } this._showUpsellProducts( this.element.find(this.options.elementsSelector), this.element.data('limit'), - this.element.data('shuffle') + this.element.data('shuffle-weighted') ); }, + /* jscs:disable */ + /* eslint-disable */ /** * Show upsell products according to limit. Shuffle if needed. * @param {*} elements * @param {Number} limit - * @param {Boolean} shuffle + * @param {Boolean} weightedRandom * @private */ - _showUpsellProducts: function (elements, limit, shuffle) { - var index; - - if (shuffle) { - this._shuffle(elements); - } + _showUpsellProducts: function (elements, limit, weightedRandom) { + var index, weights = [], random = [], weight = 2, shown = 0, $element, currentGroup, prevGroup; if (limit === 0) { limit = elements.length; } + if (weightedRandom && limit > 0 && limit < elements.length) { + for (index = 0; index < limit; index++) { + $element = $(elements[index]); + if ($element.data('shuffle-group') !== '') { + break; + } + $element.show(); + shown++; + } + limit -= shown; + for (index = elements.length - 1; index >= 0; index--) { + $element = $(elements[index]); + currentGroup = $element.data('shuffle-group'); + if (currentGroup !== '') { + weights.push([index, Math.log(weight)]); + if (typeof prevGroup !== 'undefined' && prevGroup !== currentGroup) { + weight += 2; + } + prevGroup = currentGroup; + } + } + + if (weights.length === 0) { + return; + } + + for (index = 0; index < weights.length; index++) { + random.push([weights[index][0], Math.pow(Math.random(), 1 / weights[index][1])]); + } + + random.sort(function(a, b) { + a = a[1]; + b = b[1]; + return a < b ? 1 : (a > b ? -1 : 0); + }); + index = 0; + while (limit) { + $(elements[random[index][0]]).show(); + limit--; + index++ + } + return; + } + for (index = 0; index < limit; index++) { - $(this.element).find(elements[index]).show(); + $(elements[index]).show(); } }, @@ -53,12 +98,19 @@ define([ /* eslint-disable */ /** * Shuffle an array - * @param o + * @param elements * @returns {*} */ - _shuffle: function shuffle(o){ //v1.0 - for (var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x); - return o; + _shuffle: function shuffle(elements){ //v1.0 + var parent, child, lastSibling; + if (elements.length) { + parent = $(elements[0]).parent(); + } + while (elements.length) { + child = elements.splice(Math.floor(Math.random() * elements.length), 1)[0]; + lastSibling = parent.find('[data-shuffle-group="' + $(child).data('shuffle-group') + '"]').last(); + lastSibling.after(child); + } } /* jscs:disable */ diff --git a/app/code/Magento/Catalog/view/frontend/web/template/product/image_with_borders.html b/app/code/Magento/Catalog/view/frontend/web/template/product/image_with_borders.html index d59237c190f71..f8b8ede792566 100644 --- a/app/code/Magento/Catalog/view/frontend/web/template/product/image_with_borders.html +++ b/app/code/Magento/Catalog/view/frontend/web/template/product/image_with_borders.html @@ -6,6 +6,6 @@ --> <span class="product-image-container" data-bind="style: {width: width + 'px'}"> <span class="product-image-wrapper" data-bind="style: {'padding-bottom': height/width*100 + '%'}"> - <img class="product-image-photo" data-bind="attr: {src: src, alt: alt}, style: {width: width + 'px', height: height + 'px'}" /> + <img class="product-image-photo" data-bind="attr: {src: src, alt: alt}, style: {width: 'auto', height: 'auto'}" /> </span> </span> diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/AttributeOptionProvider.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/AttributeOptionProvider.php index 7781473128754..320e0adc29b9f 100644 --- a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/AttributeOptionProvider.php +++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/AttributeOptionProvider.php @@ -41,10 +41,11 @@ public function __construct(ResourceConnection $resourceConnection) * Get option data. Return list of attributes with option data * * @param array $optionIds + * @param array $attributeCodes * @return array * @throws \Zend_Db_Statement_Exception */ - public function getOptions(array $optionIds): array + public function getOptions(array $optionIds, array $attributeCodes = []): array { if (!$optionIds) { return []; @@ -60,20 +61,28 @@ public function getOptions(array $optionIds): array 'attribute_label' => 'a.frontend_label', ] ) - ->joinInner( + ->joinLeft( ['options' => $this->resourceConnection->getTableName('eav_attribute_option')], 'a.attribute_id = options.attribute_id', [] ) - ->joinInner( + ->joinLeft( ['option_value' => $this->resourceConnection->getTableName('eav_attribute_option_value')], 'options.option_id = option_value.option_id', [ 'option_label' => 'option_value.value', 'option_id' => 'option_value.option_id', ] - ) - ->where('option_value.option_id IN (?)', $optionIds); + ); + + $select->where('option_value.option_id IN (?)', $optionIds); + + if (!empty($attributeCodes)) { + $select->orWhere( + 'a.attribute_code in (?) AND a.frontend_input = \'boolean\'', + $attributeCodes + ); + } return $this->formatResult($select); } diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Attribute.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Attribute.php index b70c9f6165fc6..0ec65c88024f2 100644 --- a/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Attribute.php +++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Attribute.php @@ -139,7 +139,9 @@ private function isBucketEmpty(?BucketInterface $bucket): bool private function getAttributeOptions(AggregationInterface $aggregation): array { $attributeOptionIds = []; + $attributes = []; foreach ($this->getAttributeBuckets($aggregation) as $bucket) { + $attributes[] = \preg_replace('~_bucket$~', '', $bucket->getName()); $attributeOptionIds[] = \array_map( function (AggregationValueInterface $value) { return $value->getValue(); @@ -152,6 +154,6 @@ function (AggregationValueInterface $value) { return []; } - return $this->attributeOptionProvider->getOptions(\array_merge(...$attributeOptionIds)); + return $this->attributeOptionProvider->getOptions(\array_merge(...$attributeOptionIds), $attributes); } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/CanonicalUrl.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/CanonicalUrl.php new file mode 100644 index 0000000000000..3cce413ff93f3 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/CanonicalUrl.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Resolver\Category; + +use Magento\Catalog\Model\Category; +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\Store\Api\Data\StoreInterface; +use Magento\Catalog\Helper\Category as CategoryHelper; + +/** + * Resolve data for category canonical URL + */ +class CanonicalUrl implements ResolverInterface +{ + /** @var CategoryHelper */ + private $categoryHelper; + + /** + * CanonicalUrl constructor. + * @param CategoryHelper $categoryHelper + */ + public function __construct(CategoryHelper $categoryHelper) + { + $this->categoryHelper = $categoryHelper; + } + + /** + * @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 Category $category */ + $category = $value['model']; + /** @var StoreInterface $store */ + $store = $context->getExtensionAttributes()->getStore(); + if ($this->categoryHelper->canUseCanonicalTag($store)) { + $baseUrl = $category->getUrlInstance()->getBaseUrl(); + return str_replace($baseUrl, '', $category->getUrl()); + } + return null; + } +} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php index 786d4f1ab867c..f6d8edf1fe9b5 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Layer/DataProvider/Filters.php @@ -9,6 +9,7 @@ use Magento\Catalog\Model\Layer\Filter\AbstractFilter; use Magento\CatalogGraphQl\Model\Resolver\Layer\FiltersProvider; +use Magento\Catalog\Model\Layer\Filter\Item; /** * Layered navigation filters data provider. @@ -20,6 +21,11 @@ class Filters */ private $filtersProvider; + /** + * @var array + */ + private $mappings; + /** * Filters constructor. * @param FiltersProvider $filtersProvider @@ -28,26 +34,31 @@ public function __construct( FiltersProvider $filtersProvider ) { $this->filtersProvider = $filtersProvider; + $this->mappings = [ + 'Category' => 'category' + ]; } /** * Get layered navigation filters data * * @param string $layerType + * @param array|null $attributesToFilter * @return array + * @throws \Magento\Framework\Exception\LocalizedException */ - public function getData(string $layerType) : array + public function getData(string $layerType, array $attributesToFilter = null) : array { $filtersData = []; /** @var AbstractFilter $filter */ foreach ($this->filtersProvider->getFilters($layerType) as $filter) { - if ($filter->getItemsCount()) { + if ($this->isNeedToAddFilter($filter, $attributesToFilter)) { $filterGroup = [ 'name' => (string)$filter->getName(), 'filter_items_count' => $filter->getItemsCount(), 'request_var' => $filter->getRequestVar(), ]; - /** @var \Magento\Catalog\Model\Layer\Filter\Item $filterItem */ + /** @var Item $filterItem */ foreach ($filter->getItems() as $filterItem) { $filterGroup['filter_items'][] = [ 'label' => (string)$filterItem->getLabel(), @@ -60,4 +71,32 @@ public function getData(string $layerType) : array } return $filtersData; } + + /** + * Check for adding filter to the list + * + * @param AbstractFilter $filter + * @param array $attributesToFilter + * @return bool + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function isNeedToAddFilter(AbstractFilter $filter, array $attributesToFilter): bool + { + if ($attributesToFilter === null) { + $result = (bool)$filter->getItemsCount(); + } else { + if ($filter->hasAttributeModel()) { + $filterAttribute = $filter->getAttributeModel(); + $result = in_array($filterAttribute->getAttributeCode(), $attributesToFilter); + } else { + $name = (string)$filter->getName(); + if (array_key_exists($name, $this->mappings)) { + $result = in_array($this->mappings[$name], $attributesToFilter); + } else { + $result = true; + } + } + } + return $result; + } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php index 0ec7e12e42d55..78ac45a1ad001 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/LayerFilters.php @@ -44,6 +44,29 @@ public function resolve( return null; } - return $this->filtersDataProvider->getData($value['layer_type']); + $attributes = $this->prepareAttributesResults($value); + return $this->filtersDataProvider->getData($value['layer_type'], $attributes); + } + + /** + * Get attributes available to filtering from the search result + * + * @param array $value + * @return array|null + */ + private function prepareAttributesResults(array $value): ?array + { + $attributes = []; + if (!empty($value['search_result'])) { + $buckets = $value['search_result']->getSearchAggregation()->getBuckets(); + foreach ($buckets as $bucket) { + if (!empty($bucket->getValues())) { + $attributes[] = str_replace('_bucket', '', $bucket->getName()); + } + } + } else { + $attributes = null; + } + return $attributes; } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product.php index 86137990cc57d..889735a5f4d88 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product.php @@ -14,6 +14,7 @@ use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\Exception\LocalizedException; /** * @inheritdoc @@ -63,10 +64,13 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $this->productDataProvider->addEavAttributes($fields); $result = function () use ($value) { - $data = $this->productDataProvider->getProductBySku($value['sku']); + $data = $value['product'] ?? $this->productDataProvider->getProductBySku($value['sku']); if (empty($data)) { return null; } + if (!isset($data['model'])) { + throw new LocalizedException(__('"model" value should be specified')); + } $productModel = $data['model']; /** @var \Magento\Catalog\Model\Product $productModel */ $data = $productModel->getData(); @@ -79,10 +83,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value } } } - return array_replace($value, $data); }; - return $this->valueFactory->create($result); } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/BatchProductLinks.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/BatchProductLinks.php new file mode 100644 index 0000000000000..14732ecf37c63 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/BatchProductLinks.php @@ -0,0 +1,81 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Resolver\Product; + +use Magento\Catalog\Model\ProductLink\Data\ListCriteria; +use Magento\Catalog\Model\ProductLink\ProductLinkQuery; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Query\Resolver\BatchServiceContractResolverInterface; +use Magento\Framework\GraphQl\Query\Resolver\ResolveRequestInterface; +use Magento\Catalog\Api\Data\ProductLinkInterface; + +/** + * Format the product links information to conform to GraphQL schema representation + */ +class BatchProductLinks implements BatchServiceContractResolverInterface +{ + /** + * @var string[] + */ + private static $linkTypes = ['related', 'upsell', 'crosssell']; + + /** + * @inheritDoc + */ + public function getServiceContract(): array + { + return [ProductLinkQuery::class, 'search']; + } + + /** + * @inheritDoc + */ + public function convertToServiceArgument(ResolveRequestInterface $request) + { + $value = $request->getValue(); + if (empty($value['model'])) { + throw new LocalizedException(__('"model" value should be specified')); + } + /** @var \Magento\Catalog\Model\Product $product */ + $product = $value['model']; + + return new ListCriteria((string)$product->getId(), self::$linkTypes, $product); + } + + /** + * @inheritDoc + */ + public function convertFromServiceResult($result, ResolveRequestInterface $request) + { + /** @var \Magento\Catalog\Model\ProductLink\Data\ListResultInterface $result */ + if ($result->getError()) { + //If model isn't there previous method would've thrown an exception. + /** @var \Magento\Catalog\Model\Product $product */ + $product = $request->getValue()['model']; + throw new LocalizedException( + __('Failed to retrieve product links for "%1"', $product->getSku()), + $result->getError() + ); + } + + return array_filter( + array_map( + function (ProductLinkInterface $link) { + return [ + 'sku' => $link->getSku(), + 'link_type' => $link->getLinkType(), + 'linked_product_sku' => $link->getLinkedProductSku(), + 'linked_product_type' => $link->getLinkedProductType(), + 'position' => $link->getPosition() + ]; + }, + $result->getResult() + ) + ); + } +} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php index 9047eaee4b568..0f764d1daa5e7 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php @@ -12,12 +12,25 @@ use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Catalog\Helper\Product as ProductHelper; +use Magento\Store\Api\Data\StoreInterface; /** * Resolve data for product canonical URL */ class CanonicalUrl implements ResolverInterface { + /** @var ProductHelper */ + private $productHelper; + + /** + * @param Product $productHelper + */ + public function __construct(ProductHelper $productHelper) + { + $this->productHelper = $productHelper; + } + /** * @inheritdoc */ @@ -32,10 +45,14 @@ public function resolve( throw new LocalizedException(__('"model" value should be specified')); } - /* @var $product Product */ + /* @var Product $product */ $product = $value['model']; - $url = $product->getUrlModel()->getUrl($product, ['_ignore_category' => true]); - - return $url; + /** @var StoreInterface $store */ + $store = $context->getExtensionAttributes()->getStore(); + if ($this->productHelper->canUseCanonicalTag($store)) { + $product->getUrlModel()->getUrl($product, ['_ignore_category' => true]); + return $product->getRequestPath(); + } + return null; } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php index ada3caad5f9f8..701ee70204486 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php @@ -53,7 +53,7 @@ public function resolve( $product = $value['model']; $productId = $product->getData( - $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField() + $this->metadataPool->getMetadata(ProductInterface::class)->getIdentifierField() ); return $productId; diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGallery/Label.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGallery/Label.php deleted file mode 100644 index 4ec76fe59ca88..0000000000000 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGallery/Label.php +++ /dev/null @@ -1,87 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\CatalogGraphQl\Model\Resolver\Product\MediaGallery; - -use Magento\Catalog\Model\Product; -use Magento\Catalog\Model\ResourceModel\Product as ProductResourceModel; -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\Store\Api\Data\StoreInterface; - -/** - * Return media label - */ -class Label implements ResolverInterface -{ - /** - * @var ProductResourceModel - */ - private $productResource; - - /** - * @param ProductResourceModel $productResource - */ - public function __construct( - ProductResourceModel $productResource - ) { - $this->productResource = $productResource; - } - - /** - * @inheritdoc - */ - public function resolve( - Field $field, - $context, - ResolveInfo $info, - array $value = null, - array $args = null - ) { - - if (isset($value['label'])) { - return $value['label']; - } - - if (!isset($value['model'])) { - throw new LocalizedException(__('"model" value should be specified')); - } - - /** @var Product $product */ - $product = $value['model']; - $productId = (int)$product->getEntityId(); - /** @var StoreInterface $store */ - $store = $context->getExtensionAttributes()->getStore(); - $storeId = (int)$store->getId(); - if (!isset($value['image_type'])) { - return $this->getAttributeValue($productId, 'name', $storeId); - } - $imageType = $value['image_type']; - $imageLabel = $this->getAttributeValue($productId, $imageType . '_label', $storeId); - if ($imageLabel == null) { - $imageLabel = $this->getAttributeValue($productId, 'name', $storeId); - } - - return $imageLabel; - } - - /** - * Get attribute value - * - * @param int $productId - * @param string $attributeCode - * @param int $storeId - * @return null|string Null if attribute value is not exists - */ - private function getAttributeValue(int $productId, string $attributeCode, int $storeId): ?string - { - $value = $this->productResource->getAttributeRawValue($productId, $attributeCode, $storeId); - return is_array($value) && empty($value) ? null : $value; - } -} diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGallery/Url.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGallery/Url.php index eaab159cddae6..359d295095667 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGallery/Url.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/MediaGallery/Url.php @@ -24,11 +24,17 @@ class Url implements ResolverInterface * @var ImageFactory */ private $productImageFactory; + /** * @var PlaceholderProvider */ private $placeholderProvider; + /** + * @var string[] + */ + private $placeholderCache = []; + /** * @param ImageFactory $productImageFactory * @param PlaceholderProvider $placeholderProvider @@ -64,12 +70,8 @@ public function resolve( if (isset($value['image_type'])) { $imagePath = $product->getData($value['image_type']); return $this->getImageUrl($value['image_type'], $imagePath); - } - if (isset($value['file'])) { - $image = $this->productImageFactory->create(); - $image->setDestinationSubdir('image')->setBaseFile($value['file']); - $imageUrl = $image->getUrl(); - return $imageUrl; + } elseif (isset($value['file'])) { + return $this->getImageUrl('image', $value['file']); } return []; } @@ -84,12 +86,16 @@ public function resolve( */ private function getImageUrl(string $imageType, ?string $imagePath): string { + if (empty($imagePath) && !empty($this->placeholderCache[$imageType])) { + return $this->placeholderCache[$imageType]; + } $image = $this->productImageFactory->create(); $image->setDestinationSubdir($imageType) ->setBaseFile($imagePath); if ($image->isBaseFilePlaceholder()) { - return $this->placeholderProvider->getPlaceholder($imageType); + $this->placeholderCache[$imageType] = $this->placeholderProvider->getPlaceholder($imageType); + return $this->placeholderCache[$imageType]; } return $image->getUrl(); diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php index d1566162472b0..7c08f91c922bd 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage.php @@ -18,6 +18,25 @@ */ class ProductImage implements ResolverInterface { + /** @var array */ + private static $catalogImageLabelTypes = [ + 'image' => 'image_label', + 'small_image' => 'small_image_label', + 'thumbnail' => 'thumbnail_label' + ]; + + /** @var array */ + private $imageTypeLabels; + + /** + * @param array $imageTypeLabels + */ + public function __construct( + array $imageTypeLabels = [] + ) { + $this->imageTypeLabels = array_replace(self::$catalogImageLabelTypes, $imageTypeLabels); + } + /** * @inheritdoc */ @@ -34,11 +53,16 @@ public function resolve( /** @var Product $product */ $product = $value['model']; - $imageType = $field->getName(); + $label = $value['name'] ?? null; + if (isset($this->imageTypeLabels[$info->fieldName]) + && !empty($value[$this->imageTypeLabels[$info->fieldName]])) { + $label = $value[$this->imageTypeLabels[$info->fieldName]]; + } return [ 'model' => $product, - 'image_type' => $imageType, + 'image_type' => $field->getName(), + 'label' => $label ]; } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ExtractDataFromCategoryTree.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ExtractDataFromCategoryTree.php index 3525ccbb6a2d1..b38a2c9bb04d9 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ExtractDataFromCategoryTree.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ExtractDataFromCategoryTree.php @@ -48,24 +48,57 @@ public function __construct( public function execute(\Iterator $iterator): array { $tree = []; + /** @var CategoryInterface $rootCategory */ + $rootCategory = $iterator->current(); while ($iterator->valid()) { - /** @var CategoryInterface $category */ - $category = $iterator->current(); + /** @var CategoryInterface $currentCategory */ + $currentCategory = $iterator->current(); $iterator->next(); - $pathElements = explode("/", $category->getPath()); - if (empty($tree)) { - $this->startCategoryFetchLevel = count($pathElements) - 1; + if ($this->areParentsActive($currentCategory, $rootCategory, (array)$iterator)) { + $pathElements = explode("/", $currentCategory->getPath()); + if (empty($tree)) { + $this->startCategoryFetchLevel = count($pathElements) - 1; + } + $this->iteratingCategory = $currentCategory; + $currentLevelTree = $this->explodePathToArray($pathElements, $this->startCategoryFetchLevel); + if (empty($tree)) { + $tree = $currentLevelTree; + } + $tree = $this->mergeCategoriesTrees($currentLevelTree, $tree); } - $this->iteratingCategory = $category; - $currentLevelTree = $this->explodePathToArray($pathElements, $this->startCategoryFetchLevel); - if (empty($tree)) { - $tree = $currentLevelTree; - } - $tree = $this->mergeCategoriesTrees($currentLevelTree, $tree); } return $tree; } + /** + * Test that all parents of the current category are active. + * + * Assumes that $categoriesArray are key-pair values and key is the ID of the category and + * all categories in this list are queried as active. + * + * @param CategoryInterface $currentCategory + * @param CategoryInterface $rootCategory + * @param array $categoriesArray + * @return bool + */ + private function areParentsActive( + CategoryInterface $currentCategory, + CategoryInterface $rootCategory, + array $categoriesArray + ): bool { + if ($currentCategory === $rootCategory) { + return true; + } elseif (array_key_exists($currentCategory->getParentId(), $categoriesArray)) { + return $this->areParentsActive( + $categoriesArray[$currentCategory->getParentId()], + $rootCategory, + $categoriesArray + ); + } else { + return false; + } + } + /** * Merge together complex categories trees * diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/AttributeProcessor.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/AttributeProcessor.php index 2ad05fbfa1e08..fef224b12acfc 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/AttributeProcessor.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Product/CollectionProcessor/AttributeProcessor.php @@ -51,6 +51,27 @@ public function process( /** * Add attribute to collection select * + * Add attributes to the collection where graphql fields names don't match attributes names, or if attributes exist + * on a nested level and they need to be loaded. + * + * Format of the attribute can be string or array while array can have different formats. + * Example: [ + * 'price_range' => + * [ + * 'price' => 'price', + * 'price_type' => 'price_type', + * ], + * 'thumbnail' => //complex array where more than one attribute is needed to compute a value + * [ + * 'label' => + * [ + * 'attribute' => 'thumbnail_label', // the actual attribute + * 'fallback_attribute' => 'name', //used as default value in case attribute value is null + * ], + * 'url' => 'thumbnail', + * ] + * ] + * * @param Collection $collection * @param string $attribute */ @@ -59,9 +80,7 @@ private function addAttribute(Collection $collection, string $attribute): void if (isset($this->fieldToAttributeMap[$attribute])) { $attributeMap = $this->fieldToAttributeMap[$attribute]; if (is_array($attributeMap)) { - foreach ($attributeMap as $attributeName) { - $collection->addAttributeToSelect($attributeName); - } + $this->addAttributeAsArray($collection, $attributeMap); } else { $collection->addAttributeToSelect($attributeMap); } @@ -70,4 +89,39 @@ private function addAttribute(Collection $collection, string $attribute): void $collection->addAttributeToSelect($attribute); } } + + /** + * Add an array defined attribute to the collection + * + * @param Collection $collection + * @param array $attributeMap + * @return void + */ + private function addAttributeAsArray(Collection $collection, array $attributeMap): void + { + foreach ($attributeMap as $attribute) { + if (is_array($attribute)) { + $this->addAttributeComplexArrayToCollection($collection, $attribute); + } else { + $collection->addAttributeToSelect($attribute); + } + } + } + + /** + * Add a complex array defined attribute to the collection + * + * @param Collection $collection + * @param array $attribute + * @return void + */ + private function addAttributeComplexArrayToCollection(Collection $collection, array $attribute): void + { + if (isset($attribute['attribute'])) { + $collection->addAttributeToSelect($attribute['attribute']); + } + if (isset($attribute['fallback_attribute'])) { + $collection->addAttributeToSelect($attribute['fallback_attribute']); + } + } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/FieldSelection.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/FieldSelection.php index ae4f2e911a5b0..64ab128b22ab4 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/FieldSelection.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/FieldSelection.php @@ -7,7 +7,6 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Products\Query; -use GraphQL\Language\AST\SelectionNode; use Magento\Framework\GraphQl\Query\FieldTranslator; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; @@ -37,57 +36,18 @@ public function __construct(FieldTranslator $fieldTranslator) */ public function getProductsFieldSelection(ResolveInfo $resolveInfo): array { - return $this->getProductFields($resolveInfo); - } + $productFields = $resolveInfo->getFieldSelection(1); + $sectionNames = ['items', 'product']; - /** - * Return field names for all requested product fields. - * - * @param ResolveInfo $info - * @return string[] - */ - private function getProductFields(ResolveInfo $info): array - { $fieldNames = []; - foreach ($info->fieldNodes as $node) { - if ($node->name->value !== 'products') { - continue; - } - foreach ($node->selectionSet->selections as $selection) { - if ($selection->name->value !== 'items') { - continue; - } - $fieldNames[] = $this->collectProductFieldNames($selection, $fieldNames); - } - } - if (!empty($fieldNames)) { - $fieldNames = array_merge(...$fieldNames); - } - return $fieldNames; - } - - /** - * Collect field names for each node in selection - * - * @param SelectionNode $selection - * @param array $fieldNames - * @return array - */ - private function collectProductFieldNames(SelectionNode $selection, array $fieldNames = []): array - { - foreach ($selection->selectionSet->selections as $itemSelection) { - if ($itemSelection->kind === 'InlineFragment') { - foreach ($itemSelection->selectionSet->selections as $inlineSelection) { - if ($inlineSelection->kind === 'InlineFragment') { - continue; - } - $fieldNames[] = $this->fieldTranslator->translate($inlineSelection->name->value); + foreach ($sectionNames as $sectionName) { + if (isset($productFields[$sectionName])) { + foreach (array_keys($productFields[$sectionName]) as $fieldName) { + $fieldNames[] = $this->fieldTranslator->translate($fieldName); } - continue; } - $fieldNames[] = $this->fieldTranslator->translate($itemSelection->name->value); } - return $fieldNames; + return array_unique($fieldNames); } } diff --git a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml index 76456166ded30..ed548efc896f8 100644 --- a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml @@ -134,6 +134,33 @@ <item name="price_range" xsi:type="array"> <item name="price" xsi:type="string">price</item> </item> + <item name="thumbnail" xsi:type="array"> + <item name="label" xsi:type="array"> + <item name="attribute" xsi:type="string">thumbnail_label</item> + <item name="fallback_attribute" xsi:type="string">name</item> + </item> + <item name="url" xsi:type="string">thumbnail</item> + </item> + <item name="small_image" xsi:type="array"> + <item name="label" xsi:type="array"> + <item name="attribute" xsi:type="string">small_image_label</item> + <item name="fallback_attribute" xsi:type="string">name</item> + </item> + <item name="url" xsi:type="string">small_image</item> + </item> + <item name="image" xsi:type="array"> + <item name="label" xsi:type="array"> + <item name="attribute" xsi:type="string">image_label</item> + <item name="fallback_attribute" xsi:type="string">name</item> + </item> + <item name="url" xsi:type="string">image</item> + </item> + <item name="media_gallery" xsi:type="array"> + <item name="label" xsi:type="array"> + <item name="attribute" xsi:type="string">image_label</item> + <item name="fallback_attribute" xsi:type="string">name</item> + </item> + </item> </argument> </arguments> </type> @@ -149,4 +176,10 @@ </argument> </arguments> </type> + + <type name="Magento\Catalog\Helper\Data"> + <arguments> + <argument name="templateFilterModel" xsi:type="string">Magento\Widget\Model\Template\FilterEmulate</argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index 536992d3fca82..8da50beacb2fe 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -100,16 +100,16 @@ interface ProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\ created_at: String @doc(description: "Timestamp indicating when the product was created.") updated_at: String @doc(description: "Timestamp indicating when the product was updated.") country_of_manufacture: String @doc(description: "The product's country of origin.") - type_id: String @doc(description: "One of simple, virtual, bundle, downloadable, grouped, or configurable.") - websites: [Website] @doc(description: "An array of websites in which the product is available.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Websites") - product_links: [ProductLinksInterface] @doc(description: "An array of ProductLinks objects.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductLinks") + type_id: String @doc(description: "One of simple, virtual, bundle, downloadable, grouped, or configurable.") @deprecated(reason: "Use __typename instead.") + websites: [Website] @doc(description: "An array of websites in which the product is available.") @deprecated(reason: "The field should not be used on the storefront.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Websites") + product_links: [ProductLinksInterface] @doc(description: "An array of ProductLinks objects.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\BatchProductLinks") media_gallery_entries: [MediaGalleryEntry] @deprecated(reason: "Use product's `media_gallery` instead") @doc(description: "An array of MediaGalleryEntry objects.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\MediaGalleryEntries") price: ProductPrices @deprecated(reason: "Use price_range for product price information.") @doc(description: "A ProductPrices object, indicating the price of an item.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Price") price_range: PriceRange! @doc(description: "A PriceRange object, indicating the range of prices for the product") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\PriceRange") gift_message_available: String @doc(description: "Indicates whether a gift message is available.") manufacturer: Int @doc(description: "A number representing the product's manufacturer.") categories: [CategoryInterface] @doc(description: "The categories assigned to a product.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Categories") @cache(cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\CategoriesIdentity") - canonical_url: String @doc(description: "Canonical URL.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\CanonicalUrl") + canonical_url: String @doc(description: "Relative canonical URL. This value is returned only if the system setting 'Use Canonical Link Meta Tag For Products' is enabled") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\CanonicalUrl") media_gallery: [MediaGalleryInterface] @doc(description: "An array of Media Gallery objects.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\MediaGallery") } @@ -198,7 +198,7 @@ type CustomizableFileValue @doc(description: "CustomizableFileValue defines the interface MediaGalleryInterface @doc(description: "Contains basic information about a product image or video.") @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\MediaGalleryTypeResolver") { url: String @doc(description: "The URL of the product image or video.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\MediaGallery\\Url") - label: String @doc(description: "The label of the product image or video.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\MediaGallery\\Label") + label: String @doc(description: "The label of the product image or video.") } type ProductImage implements MediaGalleryInterface @doc(description: "Product image information. Contains the image URL and label.") { @@ -223,6 +223,7 @@ interface CategoryInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model path_in_store: String @doc(description: "Category path in store.") url_key: String @doc(description: "The url key assigned to the category.") url_path: String @doc(description: "The url path assigned to the category.") + canonical_url: String @doc(description: "Relative canonical URL. This value is returned only if the system setting 'Use Canonical Link Meta Tag For Categories' is enabled") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\CanonicalUrl") position: Int @doc(description: "The position of the category relative to other categories at the same level in tree.") level: Int @doc(description: "Indicates the depth of the category within the tree.") created_at: String @doc(description: "Timestamp indicating when the category was created.") diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 9ff2edaf2708f..7ebc397cbe650 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -1198,7 +1198,7 @@ protected function _initTypeModels() // phpcs:disable Magento2.Performance.ForeachArrayMerge.ForeachArrayMerge $this->_fieldsMap = array_merge($this->_fieldsMap, $model->getCustomFieldsMapping()); $this->_specialAttributes = array_merge($this->_specialAttributes, $model->getParticularAttributes()); - // phpcs:enable + // phpcs:enable } $this->_initErrorTemplates(); // remove doubles @@ -1569,6 +1569,13 @@ protected function _saveProducts() continue; } + $storeId = !empty($rowData[self::COL_STORE]) + ? $this->getStoreIdByCode($rowData[self::COL_STORE]) + : Store::DEFAULT_STORE_ID; + $rowExistingImages = $existingImages[$storeId][$rowSku] ?? []; + $rowStoreMediaGalleryValues = $rowExistingImages; + $rowExistingImages += $existingImages[Store::DEFAULT_STORE_ID][$rowSku] ?? []; + if (self::SCOPE_STORE == $rowScope) { // set necessary data from SCOPE_DEFAULT row $rowData[self::COL_TYPE] = $this->skuProcessor->getNewSku($rowSku)['type_id']; @@ -1672,19 +1679,16 @@ protected function _saveProducts() // 5. Media gallery phase list($rowImages, $rowLabels) = $this->getImagesFromRow($rowData); - $storeId = !empty($rowData[self::COL_STORE]) - ? $this->getStoreIdByCode($rowData[self::COL_STORE]) - : Store::DEFAULT_STORE_ID; $imageHiddenStates = $this->getImagesHiddenStates($rowData); foreach (array_keys($imageHiddenStates) as $image) { - if (array_key_exists($rowSku, $existingImages) - && array_key_exists($image, $existingImages[$rowSku]) - ) { - $rowImages[self::COL_MEDIA_IMAGE][] = $image; + //Mark image as uploaded if it exists + if (array_key_exists($image, $rowExistingImages)) { $uploadedImages[$image] = $image; } - - if (empty($rowImages)) { + //Add image to hide to images list if it does not exist + if (empty($rowImages[self::COL_MEDIA_IMAGE]) + || !in_array($image, $rowImages[self::COL_MEDIA_IMAGE]) + ) { $rowImages[self::COL_MEDIA_IMAGE][] = $image; } } @@ -1725,24 +1729,29 @@ protected function _saveProducts() continue; } - if (isset($existingImages[$rowSku][$uploadedFile])) { - $currentFileData = $existingImages[$rowSku][$uploadedFile]; + if (isset($rowExistingImages[$uploadedFile])) { + $currentFileData = $rowExistingImages[$uploadedFile]; + $currentFileData['store_id'] = $storeId; + $storeMediaGalleryValueExists = isset($rowStoreMediaGalleryValues[$uploadedFile]); + if (array_key_exists($uploadedFile, $imageHiddenStates) + && $currentFileData['disabled'] != $imageHiddenStates[$uploadedFile] + ) { + $imagesForChangeVisibility[] = [ + 'disabled' => $imageHiddenStates[$uploadedFile], + 'imageData' => $currentFileData, + 'exists' => $storeMediaGalleryValueExists + ]; + $storeMediaGalleryValueExists = true; + } + if (isset($rowLabels[$column][$columnImageKey]) && $rowLabels[$column][$columnImageKey] != $currentFileData['label'] ) { $labelsForUpdate[] = [ 'label' => $rowLabels[$column][$columnImageKey], - 'imageData' => $currentFileData - ]; - } - - if (array_key_exists($uploadedFile, $imageHiddenStates) - && $currentFileData['disabled'] != $imageHiddenStates[$uploadedFile] - ) { - $imagesForChangeVisibility[] = [ - 'disabled' => $imageHiddenStates[$uploadedFile], - 'imageData' => $currentFileData + 'imageData' => $currentFileData, + 'exists' => $storeMediaGalleryValueExists ]; } } else { @@ -2035,9 +2044,9 @@ protected function _saveProductTierPrices(array $tierPriceData) protected function _getUploader() { if ($this->_fileUploader === null) { - $this->_fileUploader = $this->_uploaderFactory->create(); + $fileUploader = $this->_uploaderFactory->create(); - $this->_fileUploader->init(); + $fileUploader->init(); $dirConfig = DirectoryList::getDefaultConfig(); $dirAddon = $dirConfig[DirectoryList::MEDIA][DirectoryList::PATH]; @@ -2048,7 +2057,7 @@ protected function _getUploader() $tmpPath = $dirAddon . '/' . $this->_mediaDirectory->getRelativePath('import'); } - if (!$this->_fileUploader->setTmpDir($tmpPath)) { + if (!$fileUploader->setTmpDir($tmpPath)) { throw new LocalizedException( __('File directory \'%1\' is not readable.', $tmpPath) ); @@ -2057,11 +2066,13 @@ protected function _getUploader() $destinationPath = $dirAddon . '/' . $this->_mediaDirectory->getRelativePath($destinationDir); $this->_mediaDirectory->create($destinationPath); - if (!$this->_fileUploader->setDestDir($destinationPath)) { + if (!$fileUploader->setDestDir($destinationPath)) { throw new LocalizedException( __('File directory \'%1\' is not writable.', $destinationPath) ); } + + $this->_fileUploader = $fileUploader; } return $this->_fileUploader; } @@ -3060,6 +3071,8 @@ private function getValidationErrorLevel($sku): string * @param int $nextLinkId * @param array $positionAttrId * @return void + * @throws LocalizedException + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ private function processLinkBunches( array $bunch, @@ -3070,6 +3083,7 @@ private function processLinkBunches( $productIds = []; $linkRows = []; $positionRows = []; + $linksToDelete = []; $bunch = array_filter($bunch, [$this, 'isRowAllowedToImport'], ARRAY_FILTER_USE_BOTH); foreach ($bunch as $rowData) { @@ -3086,10 +3100,15 @@ function ($linkName) use ($rowData) { ); foreach ($linkNameToId as $linkName => $linkId) { $linkSkus = explode($this->getMultipleValueSeparator(), $rowData[$linkName . 'sku']); + //process empty value + if (!empty($linkSkus[0]) && $linkSkus[0] === $this->getEmptyAttributeValueConstant()) { + $linksToDelete[$linkId][] = $productId; + continue; + } + $linkPositions = !empty($rowData[$linkName . 'position']) ? explode($this->getMultipleValueSeparator(), $rowData[$linkName . 'position']) : []; - $linkSkus = array_filter( $linkSkus, function ($linkedSku) use ($sku) { @@ -3098,6 +3117,7 @@ function ($linkedSku) use ($sku) { && strcasecmp($linkedSku, $sku) !== 0; } ); + foreach ($linkSkus as $linkedKey => $linkedSku) { $linkedId = $this->getProductLinkedId($linkedSku); if ($linkedId == null) { @@ -3129,9 +3149,34 @@ function ($linkedSku) use ($sku) { } } } + $this->deleteProductsLinks($resource, $linksToDelete); $this->saveLinksData($resource, $productIds, $linkRows, $positionRows); } + /** + * Delete links + * + * @param Link $resource + * @param array $linksToDelete + * @return void + * @throws LocalizedException + */ + private function deleteProductsLinks(Link $resource, array $linksToDelete) + { + if (!empty($linksToDelete) && Import::BEHAVIOR_APPEND === $this->getBehavior()) { + foreach ($linksToDelete as $linkTypeId => $productIds) { + if (!empty($productIds)) { + $whereLinkId = $this->_connection->quoteInto('link_type_id', $linkTypeId); + $whereProductId = $this->_connection->quoteInto('product_id IN (?)', array_unique($productIds)); + $this->_connection->delete( + $resource->getMainTable(), + $whereLinkId . ' AND ' . $whereProductId + ); + } + } + } + } + /** * Fetches Product Links * diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php index 00e6da0ebe077..a94a87a44b32a 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php @@ -12,7 +12,6 @@ use Magento\Framework\App\ResourceConnection; use Magento\Framework\EntityManager\MetadataPool; use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface; -use Magento\Store\Model\Store; /** * Process and saves images during import. @@ -109,40 +108,161 @@ public function __construct( public function saveMediaGallery(array $mediaGalleryData) { $this->initMediaGalleryResources(); - $mediaGalleryDataGlobal = array_replace_recursive(...$mediaGalleryData); - $imageNames = []; - $multiInsertData = []; - $valueToProductId = []; - foreach ($mediaGalleryDataGlobal as $productSku => $mediaGalleryRows) { - $productId = $this->skuProcessor->getNewSku($productSku)[$this->getProductEntityLinkField()]; - $insertedGalleryImgs = []; - foreach ($mediaGalleryRows as $insertValue) { - if (!in_array($insertValue['value'], $insertedGalleryImgs)) { - $valueArr = [ - 'attribute_id' => $insertValue['attribute_id'], - 'value' => $insertValue['value'], + $mediaGalleryValues = []; + $mediaGalleryValueData = []; + $productMediaGalleryValueData = []; + $mediaGalleryValueToEntityData = []; + $mediaGalleryValueToStoreData = []; + $productLinkIdField = $this->getProductEntityLinkField(); + foreach ($mediaGalleryData as $storeId => $storeMediaGalleryData) { + foreach ($storeMediaGalleryData as $sku => $productMediaGalleryData) { + $productId = $this->skuProcessor->getNewSku($sku)[$productLinkIdField]; + $productMediaGalleryValueData[$productId] = $productMediaGalleryValueData[$productId] ?? []; + foreach ($productMediaGalleryData as $data) { + if (!in_array($data['value'], $productMediaGalleryValueData[$productId])) { + $productMediaGalleryValueData[$productId][] = $data['value']; + $mediaGalleryValueData[] = [ + 'attribute_id' => $data['attribute_id'], + 'value' => $data['value'], + ]; + $mediaGalleryValueToEntityData[] = [ + 'value' => $data['value'], + $productLinkIdField => $productId, + ]; + } + $mediaGalleryValues[] = $data['value']; + $mediaGalleryValueToStoreData[] = [ + 'value' => $data['value'], + 'store_id' => $storeId, + $productLinkIdField => $productId, + 'label' => $data['label'], + 'position' => $data['position'], + 'disabled' => $data['disabled'], ]; - $valueToProductId[$insertValue['value']][] = $productId; - $imageNames[] = $insertValue['value']; - $multiInsertData[] = $valueArr; - $insertedGalleryImgs[] = $insertValue['value']; } } } - $oldMediaValues = $this->connection->fetchAssoc( - $this->connection->select()->from($this->mediaGalleryTableName, ['value_id', 'value']) - ->where('value IN (?)', $imageNames) - ); - $this->connection->insertOnDuplicate($this->mediaGalleryTableName, $multiInsertData); - $newMediaSelect = $this->connection->select()->from($this->mediaGalleryTableName, ['value_id', 'value']) - ->where('value IN (?)', $imageNames); - if (array_keys($oldMediaValues)) { - $newMediaSelect->where('value_id NOT IN (?)', array_keys($oldMediaValues)); + try { + $mediaValueIdValueMap = []; + $oldMediaValues = $this->connection->fetchCol( + $this->connection->select() + ->from($this->mediaGalleryTableName, ['value_id']) + ->where('value IN (?)', $mediaGalleryValues) + ); + $this->connection->insertOnDuplicate( + $this->mediaGalleryTableName, + $mediaGalleryValueData + ); + $newMediaSelect = $this->connection->select() + ->from($this->mediaGalleryTableName, ['value_id', 'value']) + ->where('value IN (?)', $mediaGalleryValues); + if ($oldMediaValues) { + $newMediaSelect->where('value_id NOT IN (?)', $oldMediaValues); + } + $mediaValueIdValueMap = $this->connection->fetchPairs($newMediaSelect); + $productIdMediaValueIdMap = $this->getProductIdMediaValueIdMap( + $productMediaGalleryValueData, + $mediaValueIdValueMap + ); + $mediaGalleryValueToEntityData = $this->prepareMediaGalleryValueToEntityData( + $mediaGalleryValueToEntityData, + $productIdMediaValueIdMap + ); + $this->connection->insertOnDuplicate( + $this->mediaGalleryEntityToValueTableName, + $mediaGalleryValueToEntityData, + ['value_id'] + ); + $mediaGalleryValueToStoreData = $this->prepareMediaGalleryValueData( + $mediaGalleryValueToStoreData, + $productIdMediaValueIdMap + ); + $this->connection->insertOnDuplicate( + $this->mediaGalleryValueTableName, + $mediaGalleryValueToStoreData, + ['value_id', 'store_id', $productLinkIdField, 'label', 'position', 'disabled'] + ); + } catch (\Throwable $exception) { + if ($mediaValueIdValueMap) { + $this->connection->delete( + $this->mediaGalleryTableName, + $this->connection->quoteInto('value_id IN (?)', array_keys($mediaValueIdValueMap)) + ); + } + throw $exception; } - $newMediaValues = $this->connection->fetchAssoc($newMediaSelect); - foreach ($mediaGalleryData as $storeId => $storeMediaGalleryData) { - $this->processMediaPerStore((int)$storeId, $storeMediaGalleryData, $newMediaValues, $valueToProductId); + } + + /** + * Get media values IDs per products IDs + * + * @param array $productMediaGalleryValueData + * @param array $mediaValueIdValueMap + * @return array + */ + private function getProductIdMediaValueIdMap( + array $productMediaGalleryValueData, + array $mediaValueIdValueMap + ): array { + $productIdMediaValueIdMap = []; + foreach ($productMediaGalleryValueData as $productId => $productMediaGalleryValues) { + foreach ($productMediaGalleryValues as $productMediaGalleryValue) { + foreach ($mediaValueIdValueMap as $valueId => $value) { + if ($productMediaGalleryValue === $value) { + $productIdMediaValueIdMap[$productId][$value] = $valueId; + unset($mediaValueIdValueMap[$valueId]); + break; + } + } + } + } + return $productIdMediaValueIdMap; + } + + /** + * Prepare media entity gallery value to entity data for insert + * + * @param array $mediaGalleryValueToEntityData + * @param array $productIdMediaValueIdMap + * @return array + */ + private function prepareMediaGalleryValueToEntityData( + array $mediaGalleryValueToEntityData, + array $productIdMediaValueIdMap + ): array { + $productLinkIdField = $this->getProductEntityLinkField(); + foreach ($mediaGalleryValueToEntityData as $index => $data) { + $productId = $data[$productLinkIdField]; + $value = $data['value']; + $mediaGalleryValueToEntityData[$index]['value_id'] = $productIdMediaValueIdMap[$productId][$value]; + unset($mediaGalleryValueToEntityData[$index]['value']); } + return $mediaGalleryValueToEntityData; + } + + /** + * Prepare media entity gallery value data for insert + * + * @param array $mediaGalleryValueData + * @param array $productIdMediaValueIdMap + * @return array + */ + private function prepareMediaGalleryValueData( + array $mediaGalleryValueData, + array $productIdMediaValueIdMap + ): array { + $productLinkIdField = $this->getProductEntityLinkField(); + $lastPositions = $this->getLastMediaPositionPerProduct(array_keys($productIdMediaValueIdMap)); + foreach ($mediaGalleryValueData as $index => $data) { + $productId = $data[$productLinkIdField]; + $value = $data['value']; + $position = $data['position']; + $storeId = $data['store_id']; + $mediaGalleryValueData[$index]['value_id'] = $productIdMediaValueIdMap[$productId][$value]; + $mediaGalleryValueData[$index]['position'] = $position + ($lastPositions[$storeId][$productId] ?? 0); + unset($mediaGalleryValueData[$index]['value']); + } + return $mediaGalleryValueData; } /** @@ -179,13 +299,15 @@ private function updateMediaGalleryField(array $data, $field) $insertData = []; foreach ($data as $datum) { $imageData = $datum['imageData']; + $exists = $datum['exists'] ?? true; - if ($imageData[$field] === null) { + if (!$exists) { $insertData[] = [ $field => $datum[$field], $this->getProductEntityLinkField() => $imageData[$this->getProductEntityLinkField()], 'value_id' => $imageData['value_id'], - 'store_id' => Store::DEFAULT_STORE_ID, + 'store_id' => $imageData['store_id'], + 'position' => $imageData['position'], ]; } else { $this->connection->update( @@ -196,7 +318,7 @@ private function updateMediaGalleryField(array $data, $field) [ $this->getProductEntityLinkField() . ' = ?' => $imageData[$this->getProductEntityLinkField()], 'value_id = ?' => $imageData['value_id'], - 'store_id = ?' => Store::DEFAULT_STORE_ID, + 'store_id = ?' => $imageData['store_id'], ] ); } @@ -240,14 +362,15 @@ public function getExistingImages(array $bunch) )->joinLeft( ['mgv' => $this->mediaGalleryValueTableName], sprintf( - '(mg.value_id = mgv.value_id AND mgv.%s = mgvte.%s AND mgv.store_id = %d)', - $this->getProductEntityLinkField(), + '(mgv.%s = mgvte.%s AND mg.value_id = mgv.value_id)', $this->getProductEntityLinkField(), - Store::DEFAULT_STORE_ID + $this->getProductEntityLinkField() ), [ + 'store_id' => 'mgv.store_id', 'label' => 'mgv.label', 'disabled' => 'mgv.disabled', + 'position' => 'mgv.position', ] )->joinInner( ['pe' => $this->productEntityTableName], @@ -259,7 +382,9 @@ public function getExistingImages(array $bunch) ); foreach ($this->connection->fetchAll($select) as $image) { - $result[$image['sku']][$image['value']] = $image; + $storeId = $image['store_id']; + unset($image['store_id']); + $result[$storeId][$image['sku']][$image['value']] = $image; } return $result; @@ -285,13 +410,12 @@ private function initMediaGalleryResources() } /** - * Get the last media position for each product from the given list + * Get the last media position for each product per store from the given list * - * @param int $storeId * @param array $productIds * @return array */ - private function getLastMediaPositionPerProduct(int $storeId, array $productIds): array + private function getLastMediaPositionPerProduct(array $productIds): array { $result = []; if ($productIds) { @@ -301,95 +425,25 @@ private function getLastMediaPositionPerProduct(int $storeId, array $productIds) $positions = $this->connection->fetchAll( $this->connection ->select() - ->from($this->mediaGalleryValueTableName, [$productKeyName, 'position']) + ->from($this->mediaGalleryValueTableName, [$productKeyName, 'store_id', 'position']) ->where("$productKeyName IN (?)", $productIds) - ->where('value_id is not null') - ->where('store_id = ?', $storeId) ); - // Make sure the result contains all product ids even if the product has no media files - $result = array_fill_keys($productIds, 0); // Find the largest position for each product foreach ($positions as $record) { $productId = $record[$productKeyName]; - $result[$productId] = $result[$productId] < $record['position'] + $storeId = $record['store_id']; + if (!isset($result[$storeId][$productId])) { + $result[$storeId][$productId] = 0; + } + $result[$storeId][$productId] = $result[$storeId][$productId] < $record['position'] ? $record['position'] - : $result[$productId]; + : $result[$storeId][$productId]; } } return $result; } - /** - * Save media gallery data per store. - * - * @param int $storeId - * @param array $mediaGalleryData - * @param array $newMediaValues - * @param array $valueToProductId - * @return void - */ - private function processMediaPerStore( - int $storeId, - array $mediaGalleryData, - array $newMediaValues, - array $valueToProductId - ) { - $multiInsertData = []; - $dataForSkinnyTable = []; - $lastMediaPositionPerProduct = $this->getLastMediaPositionPerProduct( - $storeId, - array_unique(array_merge(...array_values($valueToProductId))) - ); - - foreach ($mediaGalleryData as $mediaGalleryRows) { - foreach ($mediaGalleryRows as $insertValue) { - foreach ($newMediaValues as $valueId => $values) { - if ($values['value'] == $insertValue['value']) { - $insertValue['value_id'] = $valueId; - $insertValue[$this->getProductEntityLinkField()] - = array_shift($valueToProductId[$values['value']]); - unset($newMediaValues[$valueId]); - break; - } - } - if (isset($insertValue['value_id'])) { - $productId = $insertValue[$this->getProductEntityLinkField()]; - $valueArr = [ - 'value_id' => $insertValue['value_id'], - 'store_id' => $storeId, - $this->getProductEntityLinkField() => $productId, - 'label' => $insertValue['label'], - 'position' => $lastMediaPositionPerProduct[$productId] + $insertValue['position'], - 'disabled' => $insertValue['disabled'], - ]; - $multiInsertData[] = $valueArr; - $dataForSkinnyTable[] = [ - 'value_id' => $insertValue['value_id'], - $this->getProductEntityLinkField() => $insertValue[$this->getProductEntityLinkField()], - ]; - } - } - } - try { - $this->connection->insertOnDuplicate( - $this->mediaGalleryValueTableName, - $multiInsertData, - ['value_id', 'store_id', $this->getProductEntityLinkField(), 'label', 'position', 'disabled'] - ); - $this->connection->insertOnDuplicate( - $this->mediaGalleryEntityToValueTableName, - $dataForSkinnyTable, - ['value_id'] - ); - } catch (\Exception $e) { - $this->connection->delete( - $this->mediaGalleryTableName, - $this->connection->quoteInto('value_id IN (?)', $newMediaValues) - ); - } - } - /** * Get product entity link field. * diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php index 502ad70d405a9..4c716421b7ae6 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Option.php @@ -2086,7 +2086,9 @@ protected function _parseCustomOptions($rowData) } } } - $options[$name][$k]['_custom_option_store'] = $rowData[Product::COL_STORE_VIEW_CODE]; + if (isset($rowData[Product::COL_STORE_VIEW_CODE])) { + $options[$name][$k][self::COLUMN_STORE] = $rowData[Product::COL_STORE_VIEW_CODE]; + } $k++; } $rowData['custom_options'] = $options; diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php b/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php index 09c3cc4daf1d9..487ffaffa95e9 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Uploader.php @@ -36,13 +36,6 @@ class Uploader extends \Magento\MediaStorage\Model\File\Uploader */ protected $_tmpDir = ''; - /** - * Download directory for url-based resources. - * - * @var string - */ - private $downloadDir; - /** * Destination directory. * @@ -151,7 +144,6 @@ public function __construct( $this->_setUploadFile($filePath); } $this->random = $random ?: ObjectManager::getInstance()->get(\Magento\Framework\Math\Random::class); - $this->downloadDir = DirectoryList::getDefaultConfig()[DirectoryList::TMP][DirectoryList::PATH]; } /** @@ -187,8 +179,7 @@ public function move($fileName, $renameFileOff = false) $driver = ($matches[0] === $this->httpScheme) ? DriverPool::HTTP : DriverPool::HTTPS; $tmpFilePath = $this->downloadFileFromUrl($url, $driver); } else { - $tmpDir = $this->getTmpDir() ? ($this->getTmpDir() . '/') : ''; - $tmpFilePath = $this->_directory->getRelativePath($tmpDir . $fileName); + $tmpFilePath = $this->_directory->getRelativePath($this->getTempFilePath($fileName)); } $this->_setUploadFile($tmpFilePath); @@ -225,8 +216,13 @@ private function downloadFileFromUrl($url, $driver) $tmpFileName = str_replace(".$fileExtension", '', $fileName); $tmpFileName .= '_' . $this->random->getRandomString(16); $tmpFileName .= $fileExtension ? ".$fileExtension" : ''; - $tmpFilePath = $this->_directory->getRelativePath($this->downloadDir . '/' . $tmpFileName); + $tmpFilePath = $this->_directory->getRelativePath($this->getTempFilePath($tmpFileName)); + if (!$this->_directory->isWritable($this->getTmpDir())) { + throw new \Magento\Framework\Exception\LocalizedException( + __('Import images directory must be writable in order to process remote images.') + ); + } $this->_directory->writeFile( $tmpFilePath, $this->_readFactory->create($url, $driver)->readAll() @@ -402,6 +398,19 @@ protected function _moveFile($tmpPath, $destPath) } } + /** + * Append temp path to filename + * + * @param string $filename + * @return string + */ + private function getTempFilePath(string $filename): string + { + return $this->getTmpDir() + ? rtrim($this->getTmpDir(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $filename + : $filename; + } + /** * @inheritdoc */ diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml deleted file mode 100644 index f792b0be2eb6b..0000000000000 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml +++ /dev/null @@ -1,102 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!-- Export products using filtering by attribute --> - <actionGroup name="exportProductsFilterByAttribute"> - <annotations> - <description>Filters Products by the provided Attribute. Exports the filtered Products list. Validates that the Success Message is present.</description> - </annotations> - <arguments> - <argument name="attribute" type="string"/> - <argument name="attributeData" type="string"/> - </arguments> - - <selectOption selector="{{AdminExportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> - <waitForElementVisible selector="{{AdminExportMainSection.entityAttributes}}" stepKey="waitForElementVisible"/> - <scrollTo selector="{{AdminExportAttributeSection.chooseAttribute('attribute')}}" stepKey="scrollToAttribute"/> - <checkOption selector="{{AdminExportAttributeSection.chooseAttribute('attribute')}}" stepKey="selectAttribute"/> - <fillField selector="{{AdminExportAttributeSection.fillFilter('attribute')}}" userInput="{{attributeData}}" stepKey="setDataInField"/> - <waitForPageLoad stepKey="waitForUserInput"/> - <scrollTo selector="{{AdminExportAttributeSection.continueBtn}}" stepKey="scrollToContinue"/> - <click selector="{{AdminExportAttributeSection.continueBtn}}" stepKey="clickContinueButton"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="Message is added to queue, wait to get your file soon" stepKey="seeSuccessMessage"/> - </actionGroup> - - <!-- Export products without filtering --> - <actionGroup name="exportAllProducts"> - <annotations> - <description>Exports the unfiltered Products list. Validates that the Success Message is present.</description> - </annotations> - - <selectOption selector="{{AdminExportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> - <waitForElementVisible selector="{{AdminExportMainSection.entityAttributes}}" stepKey="waitForElementVisible" time="5"/> - <scrollTo selector="{{AdminExportAttributeSection.continueBtn}}" stepKey="scrollToContinue"/> - <wait stepKey="waitForScroll" time="5"/> - <click selector="{{AdminExportAttributeSection.continueBtn}}" stepKey="clickContinueButton"/> - <wait stepKey="waitForClick" time="5"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="Message is added to queue, wait to get your file soon" stepKey="seeSuccessMessage"/> - </actionGroup> - - <!-- Download first file in the grid --> - <actionGroup name="downloadFileByRowIndex"> - <annotations> - <description>Downloads the provided Grid Index on the Exports grid page.</description> - </annotations> - <arguments> - <argument name="rowIndex" type="string"/> - </arguments> - - <reloadPage stepKey="refreshPage"/> - <waitForPageLoad stepKey="waitFormReload"/> - <click stepKey="clickSelectBtn" selector="{{AdminExportAttributeSection.selectByIndex(rowIndex)}}"/> - <click stepKey="clickOnDownload" selector="{{AdminExportAttributeSection.download(rowIndex)}}" after="clickSelectBtn"/> - </actionGroup> - - <!-- Delete exported file --> - <actionGroup name="deleteExportedFile"> - <annotations> - <description>Deletes the provided Grid Index on the Exports grid page.</description> - </annotations> - <arguments> - <argument name="rowIndex" type="string"/> - </arguments> - - <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="goToExportIndexPage"/> - <waitForPageLoad time="30" stepKey="waitFormReload"/> - <click stepKey="clickSelectBtn" selector="{{AdminExportAttributeSection.selectByIndex(rowIndex)}}"/> - <click stepKey="clickOnDelete" selector="{{AdminExportAttributeSection.delete(rowIndex)}}" after="clickSelectBtn"/> - <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/> - <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmDelete"/> - <waitForElementVisible selector="{{AdminDataGridTableSection.dataGridEmpty}}" stepKey="waitDataGridEmptyMessageAppears"/> - <see selector="{{AdminDataGridTableSection.dataGridEmpty}}" userInput="We couldn't find any records." stepKey="assertDataGridEmptyMessage"/> - </actionGroup> - - <actionGroup name="deleteAllExportedFiles"> - <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="goToExportIndexPage"/> - <executeInSelenium - function=" - function ($webdriver) use ($I) { - $buttons = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::xpath('//tr[@data-repeat-index=\'0\']//button')); - while(!empty($buttons)) { - $buttons[0]->click(); - $I->waitForElementVisible('//tr[@data-repeat-index=\'0\']//a[text()=\'Delete\']', 10); - $deleteButton = $webdriver->findElement(\Facebook\WebDriver\WebDriverBy::xpath('//tr[@data-repeat-index=\'0\']//a[text()=\'Delete\']')); - $deleteButton->click(); - $I->waitForElementVisible('.modal-popup.confirm button.action-accept', 10); - $I->click('.modal-popup.confirm button.action-accept'); - $I->waitForPageLoad(60); - $buttons = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::xpath('//tr[@data-repeat-index=\'0\']//button')); - } - }" - stepKey="deleteAllExportedFilesOneByOne"/> - <waitForElementVisible selector="{{AdminDataGridTableSection.dataGridEmpty}}" stepKey="waitDataGridEmptyMessageAppears"/> - <see selector="{{AdminDataGridTableSection.dataGridEmpty}}" userInput="We couldn't find any records." stepKey="assertDataGridEmptyMessage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DeleteAllExportedFilesActionGroup.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DeleteAllExportedFilesActionGroup.xml new file mode 100644 index 0000000000000..aa8fad2a1d575 --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DeleteAllExportedFilesActionGroup.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="DeleteAllExportedFilesActionGroup"> + <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="goToExportIndexPage"/> + <executeInSelenium + function=" + function ($webdriver) use ($I) { + $buttons = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::xpath('//tr[@data-repeat-index=\'0\']//button')); + while(!empty($buttons)) { + $buttons[0]->click(); + $I->waitForElementVisible('//tr[@data-repeat-index=\'0\']//a[text()=\'Delete\']', 10); + $deleteButton = $webdriver->findElement(\Facebook\WebDriver\WebDriverBy::xpath('//tr[@data-repeat-index=\'0\']//a[text()=\'Delete\']')); + $deleteButton->click(); + $I->waitForElementVisible('.modal-popup.confirm button.action-accept', 10); + $I->click('.modal-popup.confirm button.action-accept'); + $I->waitForPageLoad(60); + $buttons = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::xpath('//tr[@data-repeat-index=\'0\']//button')); + } + }" + stepKey="deleteAllExportedFilesOneByOne"/> + <waitForElementVisible selector="{{AdminDataGridTableSection.dataGridEmpty}}" stepKey="waitDataGridEmptyMessageAppears"/> + <see selector="{{AdminDataGridTableSection.dataGridEmpty}}" userInput="We couldn't find any records." stepKey="assertDataGridEmptyMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DeleteExportedFileActionGroup.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DeleteExportedFileActionGroup.xml new file mode 100644 index 0000000000000..78d7293b7437b --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DeleteExportedFileActionGroup.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="DeleteExportedFileActionGroup"> + <annotations> + <description>Deletes the provided Grid Index on the Exports grid page.</description> + </annotations> + <arguments> + <argument name="rowIndex" type="string"/> + </arguments> + + <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="goToExportIndexPage"/> + <waitForPageLoad time="30" stepKey="waitFormReload"/> + <click stepKey="clickSelectBtn" selector="{{AdminExportAttributeSection.selectByIndex(rowIndex)}}"/> + <click stepKey="clickOnDelete" selector="{{AdminExportAttributeSection.delete(rowIndex)}}" after="clickSelectBtn"/> + <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/> + <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmDelete"/> + <waitForElementVisible selector="{{AdminDataGridTableSection.dataGridEmpty}}" stepKey="waitDataGridEmptyMessageAppears"/> + <see selector="{{AdminDataGridTableSection.dataGridEmpty}}" userInput="We couldn't find any records." stepKey="assertDataGridEmptyMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DownloadFileByRowIndexActionGroup.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DownloadFileByRowIndexActionGroup.xml new file mode 100644 index 0000000000000..ec164ff172625 --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/DownloadFileByRowIndexActionGroup.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="DownloadFileByRowIndexActionGroup"> + <annotations> + <description>Downloads the provided Grid Index on the Exports grid page.</description> + </annotations> + <arguments> + <argument name="rowIndex" type="string"/> + </arguments> + + <reloadPage stepKey="refreshPage"/> + <waitForPageLoad stepKey="waitFormReload"/> + <click stepKey="clickSelectBtn" selector="{{AdminExportAttributeSection.selectByIndex(rowIndex)}}"/> + <click stepKey="clickOnDownload" selector="{{AdminExportAttributeSection.download(rowIndex)}}" after="clickSelectBtn"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/ExportAllProductsActionGroup.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/ExportAllProductsActionGroup.xml new file mode 100644 index 0000000000000..3edbb1b821843 --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/ExportAllProductsActionGroup.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="ExportAllProductsActionGroup"> + <annotations> + <description>Exports the unfiltered Products list. Validates that the Success Message is present.</description> + </annotations> + + <selectOption selector="{{AdminExportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> + <waitForElementVisible selector="{{AdminExportMainSection.entityAttributes}}" stepKey="waitForElementVisible" time="5"/> + <scrollTo selector="{{AdminExportAttributeSection.continueBtn}}" stepKey="scrollToContinue"/> + <wait stepKey="waitForScroll" time="5"/> + <click selector="{{AdminExportAttributeSection.continueBtn}}" stepKey="clickContinueButton"/> + <wait stepKey="waitForClick" time="5"/> + <see selector="{{AdminMessagesSection.success}}" userInput="Message is added to queue, wait to get your file soon. Make sure your cron job is running to export the file" stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/ExportProductsFilterByAttributeActionGroup.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/ExportProductsFilterByAttributeActionGroup.xml new file mode 100644 index 0000000000000..f3ca894202893 --- /dev/null +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/ExportProductsFilterByAttributeActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- Export products using filtering by attribute --> + <actionGroup name="ExportProductsFilterByAttributeActionGroup"> + <annotations> + <description>Filters Products by the provided Attribute. Exports the filtered Products list. Validates that the Success Message is present.</description> + </annotations> + <arguments> + <argument name="attribute" type="string"/> + <argument name="attributeData" type="string"/> + </arguments> + + <selectOption selector="{{AdminExportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> + <waitForElementVisible selector="{{AdminExportMainSection.entityAttributes}}" stepKey="waitForElementVisible"/> + <scrollTo selector="{{AdminExportAttributeSection.chooseAttribute('attribute')}}" stepKey="scrollToAttribute"/> + <checkOption selector="{{AdminExportAttributeSection.chooseAttribute('attribute')}}" stepKey="selectAttribute"/> + <fillField selector="{{AdminExportAttributeSection.fillFilter('attribute')}}" userInput="{{attributeData}}" stepKey="setDataInField"/> + <waitForPageLoad stepKey="waitForUserInput"/> + <scrollTo selector="{{AdminExportAttributeSection.continueBtn}}" stepKey="scrollToContinue"/> + <click selector="{{AdminExportAttributeSection.continueBtn}}" stepKey="clickContinueButton"/> + <see selector="{{AdminMessagesSection.success}}" userInput="Message is added to queue, wait to get your file soon" stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml index 74345e64a7c9a..369805a94dd84 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml @@ -86,7 +86,7 @@ <!-- Login as admin --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="deleteAllExportedFiles" stepKey="clearExportedFilesList"/> + <actionGroup ref="DeleteAllExportedFilesActionGroup" stepKey="clearExportedFilesList"/> </before> <after> <!-- Delete products creations --> @@ -102,7 +102,7 @@ <deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/> <!-- Delete exported file --> - <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> + <actionGroup ref="DeleteExportedFileActionGroup" stepKey="deleteExportedFile"> <argument name="rowIndex" value="0"/> </actionGroup> <!-- Log out --> @@ -113,14 +113,14 @@ <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="goToExportIndexPage"/> <!-- Export created below products --> - <actionGroup ref="exportAllProducts" stepKey="exportCreatedProducts"/> + <actionGroup ref="ExportAllProductsActionGroup" stepKey="exportCreatedProducts"/> <!-- Run cron --> <magentoCLI command="cron:run" stepKey="runCron3"/> <magentoCLI command="cron:run" stepKey="runCron4"/> <!-- Download product --> - <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> + <actionGroup ref="DownloadFileByRowIndexActionGroup" stepKey="downloadCreatedProducts"> <argument name="rowIndex" value="0"/> </actionGroup> </test> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml index b0ac6a4bc95ac..d9b93196db060 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml @@ -54,7 +54,7 @@ <!-- Login as admin --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="deleteAllExportedFiles" stepKey="clearExportedFilesList"/> + <actionGroup ref="DeleteAllExportedFilesActionGroup" stepKey="clearExportedFilesList"/> </before> <after> <!-- Deleted created products --> @@ -66,7 +66,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Delete exported file --> - <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> + <actionGroup ref="DeleteExportedFileActionGroup" stepKey="deleteExportedFile"> <argument name="rowIndex" value="0"/> </actionGroup> <!-- Log out --> @@ -78,14 +78,14 @@ <waitForPageLoad stepKey="waitForExportIndexPageLoad"/> <!-- Export created below products --> - <actionGroup ref="exportAllProducts" stepKey="exportCreatedProducts"/> + <actionGroup ref="ExportAllProductsActionGroup" stepKey="exportCreatedProducts"/> <!-- Run cron --> <magentoCLI command="cron:run" stepKey="runCron3"/> <magentoCLI command="cron:run" stepKey="runCron4"/> <!-- Download product --> - <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> + <actionGroup ref="DownloadFileByRowIndexActionGroup" stepKey="downloadCreatedProducts"> <argument name="rowIndex" value="0"/> </actionGroup> </test> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml index f0ec7dbd0706b..642bbfc0453c5 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml @@ -128,7 +128,7 @@ <!-- Login as admin --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="deleteAllExportedFiles" stepKey="clearExportedFilesList"/> + <actionGroup ref="DeleteAllExportedFilesActionGroup" stepKey="clearExportedFilesList"/> </before> <after> <!-- Remove downloadable domains --> @@ -150,7 +150,7 @@ <deleteData createDataKey="createExportImportCategory" stepKey="deleteExportImportCategory"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/> <!-- Admin logout--> <actionGroup ref="logout" stepKey="adminLogout"/> </after> @@ -159,7 +159,7 @@ <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="goToExportIndexPage"/> <!-- Set Export Settings: Entity Type > Products, SKU > ConfProd's sku and press "Continue" --> - <actionGroup ref="exportProductsFilterByAttribute" stepKey="exportProductBySku"> + <actionGroup ref="ExportProductsFilterByAttributeActionGroup" stepKey="exportProductBySku"> <argument name="attribute" value="sku"/> <argument name="attributeData" value="$$createExportImportConfigurableProduct.sku$$"/> </actionGroup> @@ -169,12 +169,12 @@ <magentoCLI command="cron:run" stepKey="runCronSecondTime"/> <!-- Save exported file: file successfully downloaded --> - <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> + <actionGroup ref="DownloadFileByRowIndexActionGroup" stepKey="downloadCreatedProducts"> <argument name="rowIndex" value="0"/> </actionGroup> <!-- Go to Catalog > Products. Find ConfProd and delete it --> - <actionGroup ref="deleteProductBySku" stepKey="deleteConfigurableProductBySku"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteConfigurableProductBySku"> <argument name="sku" value="$$createExportImportConfigurableProduct.sku$$"/> </actionGroup> @@ -187,7 +187,7 @@ </actionGroup> <!-- Go to Catalog > Products: Configurable product exists --> - <actionGroup ref="filterAndSelectProduct" stepKey="openConfigurableProduct"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="openConfigurableProduct"> <argument name="productSku" value="$$createExportImportConfigurableProduct.sku$$"/> </actionGroup> @@ -204,7 +204,7 @@ <!-- Go to "Images and Videos" section: assert image --> <scrollTo selector="{{AdminProductFormConfigurationsSection.sectionHeader}}" stepKey="scrollToProductGalleryTab"/> - <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertProductImageAdminProductPage"> + <actionGroup ref="AssertProductImageAdminProductPageActionGroup" stepKey="assertProductImageAdminProductPage"> <argument name="image" value="MagentoLogo"/> </actionGroup> @@ -214,7 +214,7 @@ <waitForPageLoad stepKey="waitForProductPageLoad"/> <!-- Go to "Images and Videos" section: assert image --> <scrollTo selector="{{AdminProductFormConfigurationsSection.sectionHeader}}" stepKey="scrollToChildProductGalleryTab"/> - <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertChildProductImageAdminProductPage"> + <actionGroup ref="AssertProductImageAdminProductPageActionGroup" stepKey="assertChildProductImageAdminProductPage"> <argument name="image" value="MagentoLogo"/> </actionGroup> <closeTab stepKey="closeConfigChildProductPage"/> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml index 1870cb21bd55b..397c1ee57e7f5 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml @@ -79,7 +79,7 @@ <!-- Login as admin --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="deleteAllExportedFiles" stepKey="clearExportedFilesList"/> + <actionGroup ref="DeleteAllExportedFilesActionGroup" stepKey="clearExportedFilesList"/> </before> <after> <!-- Delete configurable product creation --> @@ -90,7 +90,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Delete exported file --> - <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> + <actionGroup ref="DeleteExportedFileActionGroup" stepKey="deleteExportedFile"> <argument name="rowIndex" value="0"/> </actionGroup> <!-- Log out --> @@ -102,7 +102,7 @@ <waitForPageLoad stepKey="waitForExportIndexPageLoad"/> <!-- Fill entity attributes data --> - <actionGroup ref="exportProductsFilterByAttribute" stepKey="exportProductBySku"> + <actionGroup ref="ExportProductsFilterByAttributeActionGroup" stepKey="exportProductBySku"> <argument name="attribute" value="sku"/> <argument name="attributeData" value="$$createConfigProduct.sku$$"/> </actionGroup> @@ -112,7 +112,7 @@ <magentoCLI command="cron:run" stepKey="runCron4"/> <!-- Download product --> - <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> + <actionGroup ref="DownloadFileByRowIndexActionGroup" stepKey="downloadCreatedProducts"> <argument name="rowIndex" value="0"/> </actionGroup> </test> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml index f6690199d63fe..e00346654ecf4 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml @@ -95,7 +95,7 @@ <!-- Login as admin --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="deleteAllExportedFiles" stepKey="clearExportedFilesList"/> + <actionGroup ref="DeleteAllExportedFilesActionGroup" stepKey="clearExportedFilesList"/> </before> <after> <!-- Delete configurable product creation --> @@ -106,7 +106,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Delete exported file --> - <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> + <actionGroup ref="DeleteExportedFileActionGroup" stepKey="deleteExportedFile"> <argument name="rowIndex" value="0"/> </actionGroup> <!-- Log out --> @@ -117,7 +117,7 @@ <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="goToExportIndexPage"/> <!-- Fill entity attributes data --> - <actionGroup ref="exportProductsFilterByAttribute" stepKey="exportProductBySku"> + <actionGroup ref="ExportProductsFilterByAttributeActionGroup" stepKey="exportProductBySku"> <argument name="attribute" value="sku"/> <argument name="attributeData" value="$$createConfigProduct.sku$$"/> </actionGroup> @@ -127,7 +127,7 @@ <magentoCLI command="cron:run" stepKey="runCron4"/> <!-- Download product --> - <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> + <actionGroup ref="DownloadFileByRowIndexActionGroup" stepKey="downloadCreatedProducts"> <argument name="rowIndex" value="0"/> </actionGroup> </test> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml index 271b4621d1a96..04be8f3ae823e 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml @@ -77,7 +77,7 @@ <!-- Login as admin --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="deleteAllExportedFiles" stepKey="clearExportedFilesList"/> + <actionGroup ref="DeleteAllExportedFilesActionGroup" stepKey="clearExportedFilesList"/> </before> <after> <!-- Delete simple product --> @@ -91,7 +91,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Delete exported file --> - <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> + <actionGroup ref="DeleteExportedFileActionGroup" stepKey="deleteExportedFile"> <argument name="rowIndex" value="0"/> </actionGroup> <!-- Log out --> @@ -103,14 +103,14 @@ <waitForPageLoad stepKey="waitForExportIndexPageLoad"/> <!-- Export created below products --> - <actionGroup ref="exportAllProducts" stepKey="exportCreatedProducts"/> + <actionGroup ref="ExportAllProductsActionGroup" stepKey="exportCreatedProducts"/> <!-- Run cron --> <magentoCLI command="cron:run" stepKey="runCron3"/> <magentoCLI command="cron:run" stepKey="runCron4"/> <!-- Download product --> - <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> + <actionGroup ref="DownloadFileByRowIndexActionGroup" stepKey="downloadCreatedProducts"> <argument name="rowIndex" value="0"/> </actionGroup> </test> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml index 238a3286dc40d..8553fb8a2cf7e 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml @@ -34,7 +34,7 @@ <!-- Login as admin --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="deleteAllExportedFiles" stepKey="clearExportedFilesList"/> + <actionGroup ref="DeleteAllExportedFilesActionGroup" stepKey="clearExportedFilesList"/> </before> <after> <!-- Delete product creations --> @@ -43,7 +43,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Delete exported file --> - <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> + <actionGroup ref="DeleteExportedFileActionGroup" stepKey="deleteExportedFile"> <argument name="rowIndex" value="0"/> </actionGroup> <!-- Log out --> @@ -55,14 +55,14 @@ <waitForPageLoad stepKey="waitForExportIndexPageLoad"/> <!-- Export created below products --> - <actionGroup ref="exportAllProducts" stepKey="exportCreatedProducts"/> + <actionGroup ref="ExportAllProductsActionGroup" stepKey="exportCreatedProducts"/> <!-- Run cron --> <magentoCLI command="cron:run" stepKey="runCron3"/> <magentoCLI command="cron:run" stepKey="runCron4"/> <!-- Download product --> - <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> + <actionGroup ref="DownloadFileByRowIndexActionGroup" stepKey="downloadCreatedProducts"> <argument name="rowIndex" value="0"/> </actionGroup> </test> diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Type/OptionTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Type/OptionTest.php index 9f63decac5ff7..f8b14a471fd9c 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Type/OptionTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/Type/OptionTest.php @@ -776,6 +776,77 @@ public function testValidateAmbiguousData( $this->assertEquals($errors, $resultErrors); } + /** + * Test for row without store view code field + * @param array $rowData + * @param array $responseData + * + * @covers \Magento\CatalogImportExport\Model\Import\Product\Option::_parseCustomOptions + * @dataProvider validateRowStoreViewCodeFieldDataProvider + */ + public function testValidateRowDataForStoreViewCodeField($rowData, $responseData) + { + $reflection = new \ReflectionClass(\Magento\CatalogImportExport\Model\Import\Product\Option::class); + $reflectionMethod = $reflection->getMethod('_parseCustomOptions'); + $reflectionMethod->setAccessible(true); + $result = $reflectionMethod->invoke($this->model, $rowData); + $this->assertEquals($responseData, $result); + } + + /** + * Data provider for test of method _parseCustomOptions + * + * @return array + */ + public function validateRowStoreViewCodeFieldDataProvider() + { + return [ + 'with_store_view_code' => [ + '$rowData' => [ + 'store_view_code' => '', + 'custom_options' => + 'name=Test Field Title,type=field,required=1;sku=1-text,price=0,price_type=fixed' + ], + '$responseData' => [ + 'store_view_code' => '', + 'custom_options' => [ + 'Test Field Title' => [ + [ + 'name' => 'Test Field Title', + 'type' => 'field', + 'required' => '1', + 'sku' => '1-text', + 'price' => '0', + 'price_type' => 'fixed', + '_custom_option_store' => '' + ] + ] + ] + ], + ], + 'without_store_view_code' => [ + '$rowData' => [ + 'custom_options' => + 'name=Test Field Title,type=field,required=1;sku=1-text,price=0,price_type=fixed' + ], + '$responseData' => [ + 'custom_options' => [ + 'Test Field Title' => [ + [ + 'name' => 'Test Field Title', + 'type' => 'field', + 'required' => '1', + 'sku' => '1-text', + 'price' => '0', + 'price_type' => 'fixed' + ] + ] + ] + ], + ] + ]; + } + /** * Data provider of row data and errors * diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php index f85d33edb5d8c..40041fe90db96 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php @@ -284,9 +284,11 @@ protected function setUp() ->getMock(); $this->storeResolver = $this->getMockBuilder(\Magento\CatalogImportExport\Model\Import\Product\StoreResolver::class) - ->setMethods([ - 'getStoreCodeToId', - ]) + ->setMethods( + [ + 'getStoreCodeToId', + ] + ) ->disableOriginalConstructor() ->getMock(); $this->skuProcessor = @@ -410,7 +412,7 @@ protected function _objectConstructor() $this->_filesystem->expects($this->once()) ->method('getDirectoryWrite') ->with(DirectoryList::ROOT) - ->will($this->returnValue(self::MEDIA_DIRECTORY)); + ->willReturn($this->_mediaDirectory); $this->validator->expects($this->any())->method('init'); return $this; @@ -596,9 +598,13 @@ public function testGetMultipleValueSeparatorDefault() public function testGetMultipleValueSeparatorFromParameters() { $expectedSeparator = 'value'; - $this->setPropertyValue($this->importProduct, '_parameters', [ - \Magento\ImportExport\Model\Import::FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR => $expectedSeparator, - ]); + $this->setPropertyValue( + $this->importProduct, + '_parameters', + [ + \Magento\ImportExport\Model\Import::FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR => $expectedSeparator, + ] + ); $this->assertEquals( $expectedSeparator, @@ -618,9 +624,13 @@ public function testGetEmptyAttributeValueConstantDefault() public function testGetEmptyAttributeValueConstantFromParameters() { $expectedSeparator = '__EMPTY__VALUE__TEST__'; - $this->setPropertyValue($this->importProduct, '_parameters', [ - \Magento\ImportExport\Model\Import::FIELD_EMPTY_ATTRIBUTE_VALUE_CONSTANT => $expectedSeparator, - ]); + $this->setPropertyValue( + $this->importProduct, + '_parameters', + [ + \Magento\ImportExport\Model\Import::FIELD_EMPTY_ATTRIBUTE_VALUE_CONSTANT => $expectedSeparator, + ] + ); $this->assertEquals( $expectedSeparator, @@ -632,9 +642,12 @@ public function testDeleteProductsForReplacement() { $importProduct = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() - ->setMethods([ - 'setParameters', '_deleteProducts' - ]) + ->setMethods( + [ + 'setParameters', + '_deleteProducts' + ] + ) ->getMock(); $importProduct->expects($this->once())->method('setParameters')->with( @@ -764,9 +777,13 @@ public function testGetProductWebsites() 'key 3' => 'val', ]; $expectedResult = array_keys($productValue); - $this->setPropertyValue($this->importProduct, 'websitesCache', [ - $productSku => $productValue - ]); + $this->setPropertyValue( + $this->importProduct, + 'websitesCache', + [ + $productSku => $productValue + ] + ); $actualResult = $this->importProduct->getProductWebsites($productSku); @@ -785,9 +802,13 @@ public function testGetProductCategories() 'key 3' => 'val', ]; $expectedResult = array_keys($productValue); - $this->setPropertyValue($this->importProduct, 'categoriesCache', [ - $productSku => $productValue - ]); + $this->setPropertyValue( + $this->importProduct, + 'categoriesCache', + [ + $productSku => $productValue + ] + ); $actualResult = $this->importProduct->getProductCategories($productSku); @@ -1112,9 +1133,13 @@ public function testValidateRowSetAttributeSetCodeIntoRowData() ->disableOriginalConstructor() ->getMock(); $productType->expects($this->once())->method('isRowValid')->with($expectedRowData); - $this->setPropertyValue($importProduct, '_productTypeModels', [ - $newSku['type_id'] => $productType - ]); + $this->setPropertyValue( + $importProduct, + '_productTypeModels', + [ + $newSku['type_id'] => $productType + ] + ); //suppress option validation $this->_rewriteGetOptionEntityInImportProduct($importProduct); @@ -1229,6 +1254,56 @@ public function testParseAttributesWithWrappedValuesWillReturnsLowercasedAttribu $this->assertArrayNotHasKey('PARAM2', $attributes); } + /** + * @param bool $isRead + * @param bool $isWrite + * @param string $message + * @dataProvider fillUploaderObjectDataProvider + */ + public function testFillUploaderObject($isRead, $isWrite, $message) + { + $fileUploaderMock = $this + ->getMockBuilder(\Magento\CatalogImportExport\Model\Import\Uploader::class) + ->disableOriginalConstructor() + ->getMock(); + + $fileUploaderMock + ->method('setTmpDir') + ->with('pub/media/import') + ->willReturn($isRead); + + $fileUploaderMock + ->method('setDestDir') + ->with('pub/media/catalog/product') + ->willReturn($isWrite); + + $this->_mediaDirectory + ->method('getRelativePath') + ->willReturnMap( + [ + ['import', 'import'], + ['catalog/product', 'catalog/product'], + ] + ); + + $this->_mediaDirectory + ->method('create') + ->with('pub/media/catalog/product'); + + $this->_uploaderFactory + ->expects($this->once()) + ->method('create') + ->willReturn($fileUploaderMock); + + try { + $this->importProduct->getUploader(); + $this->assertNotNull($this->getPropertyValue($this->importProduct, '_fileUploader')); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->assertNull($this->getPropertyValue($this->importProduct, '_fileUploader')); + $this->assertEquals($message, $e->getMessage()); + } + } + /** * Test that errors occurred during importing images are logged. * @@ -1275,6 +1350,20 @@ function ($name) use ($throwException, $exception) { ); } + /** + * Data provider for testFillUploaderObject. + * + * @return array + */ + public function fillUploaderObjectDataProvider() + { + return [ + [false, true, 'File directory \'pub/media/import\' is not readable.'], + [true, false, 'File directory \'pub/media/catalog/product\' is not writable.'], + [true, true, ''], + ]; + } + /** * Data provider for testUploadMediaFiles. * diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/UploaderTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/UploaderTest.php index 2c6aa6535c10e..f10cf0364c545 100644 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/UploaderTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/UploaderTest.php @@ -128,6 +128,7 @@ public function testMoveFileUrl($fileUrl, $expectedHost, $expectedFileName, $che { $tmpDir = 'var/tmp'; $destDir = 'var/dest/dir'; + $this->uploader->method('getTmpDir')->willReturn($tmpDir); // Expected invocation to validate file extension $this->uploader->expects($this->exactly($checkAllowedExtension))->method('checkAllowedExtension') @@ -159,9 +160,11 @@ public function testMoveFileUrl($fileUrl, $expectedHost, $expectedFileName, $che $this->directoryMock->expects($this->any())->method('writeFile') ->will($this->returnValue($expectedFileName)); - // Expected invocations to move the temp file to the destination directory - $this->directoryMock->expects($this->once())->method('isWritable') - ->with($destDir) + // Expected invocations save the downloaded file to temp file + // and move the temp file to the destination directory + $this->directoryMock->expects($this->exactly(2)) + ->method('isWritable') + ->withConsecutive([$destDir], [$tmpDir]) ->willReturn(true); $this->directoryMock->expects($this->once())->method('getAbsolutePath') ->with($destDir) @@ -172,9 +175,6 @@ public function testMoveFileUrl($fileUrl, $expectedHost, $expectedFileName, $che ->with($destDir . '/' . $expectedFileName) ->willReturn(['name' => $expectedFileName, 'path' => 'absPath']); - // Do not use configured temp directory - $this->uploader->expects($this->never())->method('getTmpDir'); - $this->uploader->setDestDir($destDir); $result = $this->uploader->move($fileUrl); diff --git a/app/code/Magento/CatalogImportExport/registration.php b/app/code/Magento/CatalogImportExport/registration.php index 144a7cdaeeea0..bd6683f4af4d1 100644 --- a/app/code/Magento/CatalogImportExport/registration.php +++ b/app/code/Magento/CatalogImportExport/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CatalogImportExport', __DIR__); diff --git a/app/code/Magento/CatalogInventory/Block/Adminhtml/Form/Field/Minsaleqty.php b/app/code/Magento/CatalogInventory/Block/Adminhtml/Form/Field/Minsaleqty.php index 6c4f6a0f46a59..ffcb758dcbd66 100644 --- a/app/code/Magento/CatalogInventory/Block/Adminhtml/Form/Field/Minsaleqty.php +++ b/app/code/Magento/CatalogInventory/Block/Adminhtml/Form/Field/Minsaleqty.php @@ -37,7 +37,7 @@ protected function _getGroupRenderer() '', ['data' => ['is_render_to_js_template' => true]] ); - $this->_groupRenderer->setClass('customer_group_select'); + $this->_groupRenderer->setClass('customer_group_select admin__control-select'); } return $this->_groupRenderer; } @@ -57,7 +57,7 @@ protected function _prepareToRender() 'min_sale_qty', [ 'label' => __('Minimum Qty'), - 'class' => 'required-entry validate-number validate-greater-than-zero' + 'class' => 'required-entry validate-number validate-greater-than-zero admin__control-text' ] ); $this->_addAfter = false; diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php index 519466c505539..c5644060c689f 100644 --- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php +++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php @@ -292,6 +292,7 @@ protected function _prepareIndexTable($entityIds = null) */ protected function _updateIndex($entityIds) { + $this->deleteOldRecords($entityIds); $connection = $this->getConnection(); $select = $this->_getStockStatusSelect($entityIds, true); $select = $this->getQueryProcessorComposite()->processQuery($select, $entityIds, true); @@ -314,7 +315,6 @@ protected function _updateIndex($entityIds) } } - $this->deleteOldRecords($entityIds); $this->_updateIndexTable($data); return $this; diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminAssertDisabledQtyActionGroup.xml b/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminAssertDisabledQtyActionGroup.xml deleted file mode 100644 index 27c4a93577a07..0000000000000 --- a/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminAssertDisabledQtyActionGroup.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminAssertDisabledQtyActionGroup"> - <annotations> - <description>Goes to the 'Quantity' field and assert disabled attribute.</description> - </annotations> - - <seeElement selector="{{AdminProductFormSection.productQuantity}}" stepKey="assertProductQty"/> - <assertElementContainsAttribute selector="{{AdminProductFormSection.productQuantity}}" attribute="disabled" expectedValue="true" stepKey="checkIfQtyIsDisabled" /> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminProductActionGroup.xml deleted file mode 100644 index 84dc6b93c885f..0000000000000 --- a/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminProductSetMaxQtyAllowedInShoppingCart"> - <arguments> - <argument name="qty" type="string"/> - </arguments> - <conditionalClick selector="{{AdminProductFormSection.advancedInventoryLink}}" dependentSelector="{{AdminProductFormAdvancedInventorySection.advancedInventoryModal}}" visible="false" stepKey="clickOnAdvancedInventoryLinkIfNeeded"/> - <waitForElementVisible selector="{{AdminProductFormAdvancedInventorySection.maxiQtyConfigSetting}}" stepKey="waitForAdvancedInventoryModalWindowOpen"/> - <uncheckOption selector="{{AdminProductFormAdvancedInventorySection.maxiQtyConfigSetting}}" stepKey="uncheckMaxQtyCheckBox"/> - <fillField selector="{{AdminProductFormAdvancedInventorySection.maxiQtyAllowedInCart}}" userInput="{{qty}}" stepKey="fillMaxAllowedQty"/> - <click selector="{{AdminSlideOutDialogSection.doneButton}}" stepKey="clickDone"/> - </actionGroup> - - <actionGroup name="AdminProductMaxQtyAllowedInShoppingCartValidationActionGroup" extends="AdminProductSetMaxQtyAllowedInShoppingCart"> - <arguments> - <argument name="qty" type="string"/> - <argument name="errorMessage" type="string"/> - </arguments> - - <waitForElementVisible selector="{{AdminProductFormAdvancedInventorySection.maxiQtyAllowedInCartError}}" after="clickDone" stepKey="waitProductValidationErrorMessageAppears"/> - <see selector="{{AdminProductFormAdvancedInventorySection.maxiQtyAllowedInCartError}}" userInput="{{errorMessage}}" after="waitProductValidationErrorMessageAppears" stepKey="checkProductValidationErrorMessage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminProductMaxQtyAllowedInShoppingCartValidationActionGroup.xml b/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminProductMaxQtyAllowedInShoppingCartValidationActionGroup.xml new file mode 100644 index 0000000000000..1dbc0256be7d8 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminProductMaxQtyAllowedInShoppingCartValidationActionGroup.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="AdminProductMaxQtyAllowedInShoppingCartValidationActionGroup" extends="AdminProductSetMaxQtyAllowedInShoppingCartActionGroup"> + <arguments> + <argument name="qty" type="string"/> + <argument name="errorMessage" type="string"/> + </arguments> + + <waitForElementVisible selector="{{AdminProductFormAdvancedInventorySection.maxiQtyAllowedInCartError}}" after="clickDone" stepKey="waitProductValidationErrorMessageAppears"/> + <see selector="{{AdminProductFormAdvancedInventorySection.maxiQtyAllowedInCartError}}" userInput="{{errorMessage}}" after="waitProductValidationErrorMessageAppears" stepKey="checkProductValidationErrorMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminProductSetMaxQtyAllowedInShoppingCartActionGroup.xml b/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminProductSetMaxQtyAllowedInShoppingCartActionGroup.xml new file mode 100644 index 0000000000000..a5e4d3e9c2af7 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminProductSetMaxQtyAllowedInShoppingCartActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminProductSetMaxQtyAllowedInShoppingCartActionGroup"> + <arguments> + <argument name="qty" type="string"/> + </arguments> + <conditionalClick selector="{{AdminProductFormSection.advancedInventoryLink}}" dependentSelector="{{AdminProductFormAdvancedInventorySection.advancedInventoryModal}}" visible="false" stepKey="clickOnAdvancedInventoryLinkIfNeeded"/> + <waitForElementVisible selector="{{AdminProductFormAdvancedInventorySection.maxiQtyConfigSetting}}" stepKey="waitForAdvancedInventoryModalWindowOpen"/> + <uncheckOption selector="{{AdminProductFormAdvancedInventorySection.maxiQtyConfigSetting}}" stepKey="uncheckMaxQtyCheckBox"/> + <fillField selector="{{AdminProductFormAdvancedInventorySection.maxiQtyAllowedInCart}}" userInput="{{qty}}" stepKey="fillMaxAllowedQty"/> + <click selector="{{AdminSlideOutDialogSection.doneButton}}" stepKey="clickDone"/> + </actionGroup> + + +</actionGroups> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/DisplayOutOfStockProductActionGroup.xml b/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/DisplayOutOfStockProductActionGroup.xml index 13cb9089bf920..a6fe2b49508ff 100644 --- a/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/DisplayOutOfStockProductActionGroup.xml +++ b/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/DisplayOutOfStockProductActionGroup.xml @@ -8,11 +8,11 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="displayOutOfStockProduct"> + <actionGroup name="DisplayOutOfStockProductActionGroup"> <annotations> <description>Goes to the 'Configuration' page for 'Inventory'. Enables 'Display Out of Stock Products'. Clicks on the Save button.</description> </annotations> - + <amOnPage url="{{InventoryConfigurationPage.url}}" stepKey="navigateToInventoryConfigurationPage"/> <waitForPageLoad stepKey="waitForConfigPageToLoad"/> <conditionalClick stepKey="expandProductStockOptions" selector="{{InventoryConfigSection.ProductStockOptionsTab}}" dependentSelector="{{InventoryConfigSection.CheckIfProductStockOptionsTabExpanded}}" visible="true"/> @@ -22,18 +22,4 @@ <selectOption selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" userInput="Yes" stepKey="switchToYes"/> <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig"/> </actionGroup> - - <actionGroup name="noDisplayOutOfStockProduct"> - <annotations> - <description>Goes to the 'Configuration' page for 'Inventory'. Disables 'Display Out of Stock Products'. Clicks on the Save button.</description> - </annotations> - - <amOnPage url="{{InventoryConfigurationPage.url}}" stepKey="navigateToInventoryConfigurationPage"/> - <waitForPageLoad stepKey="waitForConfigPageToLoad"/> - <conditionalClick stepKey="expandProductStockOptions" selector="{{InventoryConfigSection.ProductStockOptionsTab}}" dependentSelector="{{InventoryConfigSection.CheckIfProductStockOptionsTabExpanded}}" visible="true"/> - <uncheckOption selector="{{InventoryConfigSection.DisplayOutOfStockSystemValue}}" stepKey="uncheckUseSystemValue"/> - <waitForElementVisible selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" stepKey="waitForSwitcherDropdown"/> - <selectOption selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" userInput="No" stepKey="switchToNo"/> - <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/NoDisplayOutOfStockProductActionGroup.xml b/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/NoDisplayOutOfStockProductActionGroup.xml new file mode 100644 index 0000000000000..6f6b7ae20987f --- /dev/null +++ b/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/NoDisplayOutOfStockProductActionGroup.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="NoDisplayOutOfStockProductActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page for 'Inventory'. Disables 'Display Out of Stock Products'. Clicks on the Save button.</description> + </annotations> + + <amOnPage url="{{InventoryConfigurationPage.url}}" stepKey="navigateToInventoryConfigurationPage"/> + <waitForPageLoad stepKey="waitForConfigPageToLoad"/> + <conditionalClick stepKey="expandProductStockOptions" selector="{{InventoryConfigSection.ProductStockOptionsTab}}" dependentSelector="{{InventoryConfigSection.CheckIfProductStockOptionsTabExpanded}}" visible="true"/> + <uncheckOption selector="{{InventoryConfigSection.DisplayOutOfStockSystemValue}}" stepKey="uncheckUseSystemValue"/> + <waitForElementVisible selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" stepKey="waitForSwitcherDropdown"/> + <selectOption selector="{{InventoryConfigSection.DisplayOutOfStockDropdown}}" userInput="No" stepKey="switchToNo"/> + <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Section/AdminProductFormSection.xml index f4b79b17b3fc3..945613ee753d6 100644 --- a/app/code/Magento/CatalogInventory/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/CatalogInventory/Test/Mftf/Section/AdminProductFormSection.xml @@ -9,6 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductFormSection"> - <element name="advancedInventoryLink" type="button" selector="button[data-index='advanced_inventory_button']" timeout="30"/> + <element name="advancedInventoryLink" type="button" selector="button[data-index='advanced_inventory_button'].action-additional" timeout="30"/> + <element name="advancedInventoryButton" type="button" selector="button[data-index='advanced_inventory_button'].action-basic" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AdminCreateProductWithZeroMaximumQtyAllowedInShoppingCartTest.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AdminCreateProductWithZeroMaximumQtyAllowedInShoppingCartTest.xml index f7cf0a4deba4b..74b24209682ff 100644 --- a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AdminCreateProductWithZeroMaximumQtyAllowedInShoppingCartTest.xml +++ b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AdminCreateProductWithZeroMaximumQtyAllowedInShoppingCartTest.xml @@ -71,10 +71,10 @@ <argument name="errorMessage" value="Please enter a valid number in this field."/> </actionGroup> <!-- Fill correct value --> - <actionGroup ref="AdminProductSetMaxQtyAllowedInShoppingCart" stepKey="setProductMaxQtyAllowedInShoppingCartToCorrectNumber"> + <actionGroup ref="AdminProductSetMaxQtyAllowedInShoppingCartActionGroup" stepKey="setProductMaxQtyAllowedInShoppingCartToCorrectNumber"> <argument name="qty" value="50"/> </actionGroup> <waitForElementNotVisible selector="{{AdminProductFormAdvancedInventorySection.advancedInventoryModal}}" stepKey="waitForModalFormToDisappear"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> </test> </tests> diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Observer/InvalidatePriceIndexUponConfigChangeObserverTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Observer/InvalidatePriceIndexUponConfigChangeObserverTest.php new file mode 100644 index 0000000000000..1dd7df8952473 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Test/Unit/Observer/InvalidatePriceIndexUponConfigChangeObserverTest.php @@ -0,0 +1,118 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogInventory\Test\Unit\Observer; + +use Magento\Catalog\Model\Indexer\Product\Price\Processor; +use Magento\CatalogInventory\Model\Configuration; +use Magento\CatalogInventory\Observer\InvalidatePriceIndexUponConfigChangeObserver; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Framework\Indexer\IndexerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Class InvalidatePriceIndexUponConfigChangeObserverTest + * + * Testing invalidating product price index onn config changing + */ +class InvalidatePriceIndexUponConfigChangeObserverTest extends TestCase +{ + /** + * @var InvalidatePriceIndexUponConfigChangeObserver + */ + private $observer; + + /** + * @var Processor|MockObject + */ + private $priceIndexProcessorMock; + + /** + * @var Observer|MockObject + */ + private $observerMock; + + /** + * @var Event|MockObject + */ + private $eventMock; + + /** + * @var IndexerInterface|MockObject + */ + private $indexerMock; + + /** + * Set Up + */ + public function setUp() + { + $objectManager = new ObjectManager($this); + $this->priceIndexProcessorMock = $this->createMock(Processor::class); + $this->indexerMock = $this->getMockBuilder(IndexerInterface::class) + ->getMockForAbstractClass(); + $this->observerMock = $this->createMock(Observer::class); + $this->eventMock = $this->getMockBuilder(Event::class) + ->disableOriginalConstructor() + ->setMethods(['getChangedPaths']) + ->getMock(); + + $this->observer = $objectManager->getObject( + InvalidatePriceIndexUponConfigChangeObserver::class, + [ + 'priceIndexProcessor' => $this->priceIndexProcessorMock + ] + ); + } + + /** + * Testing invalidating product price index on catalog inventory config changes + */ + public function testInvalidatingPriceOnChangingOutOfStockConfig() + { + $changedPaths = [Configuration::XML_PATH_SHOW_OUT_OF_STOCK]; + + $this->eventMock->expects($this->once()) + ->method('getChangedPaths') + ->willReturn($changedPaths); + $this->observerMock->expects($this->once()) + ->method('getEvent') + ->willReturn($this->eventMock); + $this->indexerMock->expects($this->once()) + ->method('invalidate'); + $this->priceIndexProcessorMock->expects($this->once()) + ->method('getIndexer') + ->willReturn($this->indexerMock); + + $this->observer->execute($this->observerMock); + } + + /** + * Testing invalidating product price index on changing any other config + */ + public function testInvalidatingPriceOnChangingAnyOtherConfig() + { + $changedPaths = [Configuration::XML_PATH_ITEM_AUTO_RETURN]; + + $this->eventMock->expects($this->once()) + ->method('getChangedPaths') + ->willReturn($changedPaths); + $this->observerMock->expects($this->once()) + ->method('getEvent') + ->willReturn($this->eventMock); + $this->indexerMock->expects($this->never()) + ->method('invalidate'); + $this->priceIndexProcessorMock->expects($this->never()) + ->method('getIndexer') + ->willReturn($this->indexerMock); + + $this->observer->execute($this->observerMock); + } +} diff --git a/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventory.php b/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventory.php index 789befcfec8b7..aafde14a28584 100644 --- a/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventory.php +++ b/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/Form/Modifier/AdvancedInventory.php @@ -3,8 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); - namespace Magento\CatalogInventory\Ui\DataProvider\Product\Form\Modifier; use Magento\Catalog\Controller\Adminhtml\Product\Initialization\StockDataFilter; @@ -177,7 +175,7 @@ public function modifyMeta(array $meta) } /** - * Prepare Meta + * Modify UI Quantity and Stock status attribute meta. * * @return void */ @@ -187,10 +185,6 @@ private function prepareMeta() $pathField = $this->arrayManager->findPath($fieldCode, $this->meta, null, 'children'); if ($pathField) { - $labelField = $this->arrayManager->get( - $this->arrayManager->slicePath($pathField, 0, -2) . '/arguments/data/config/label', - $this->meta - ); $fieldsetPath = $this->arrayManager->slicePath($pathField, 0, -4); $this->meta = $this->arrayManager->merge( @@ -218,10 +212,9 @@ private function prepareMeta() 'formElement' => 'container', 'componentType' => 'container', 'component' => "Magento_Ui/js/form/components/group", - 'label' => $labelField, + 'label' => false, 'breakLine' => false, 'dataScope' => $fieldCode, - 'scopeLabel' => '[GLOBAL]', 'source' => 'product_details', 'sortOrder' => (int) $this->arrayManager->get( $this->arrayManager->slicePath($pathField, 0, -2) . '/arguments/data/config/sortOrder', @@ -229,9 +222,51 @@ private function prepareMeta() ) - 1, 'disabled' => $this->locator->getProduct()->isLockedAttribute($fieldCode), ]; + $qty['arguments']['data']['config'] = [ + 'component' => 'Magento_CatalogInventory/js/components/qty-validator-changer', + 'group' => 'quantity_and_stock_status_qty', + 'dataType' => 'number', + 'formElement' => 'input', + 'componentType' => 'field', + 'visible' => '1', + 'require' => '0', + 'additionalClasses' => 'admin__field-small', + 'label' => __('Quantity'), + 'scopeLabel' => '[GLOBAL]', + 'dataScope' => 'qty', + 'validation' => [ + 'validate-number' => true, + 'less-than-equals-to' => StockDataFilter::MAX_QTY_VALUE, + ], + 'imports' => [ + 'handleChanges' => '${$.provider}:data.product.stock_data.is_qty_decimal', + ], + 'sortOrder' => 10, + ]; + $advancedInventoryButton['arguments']['data']['config'] = [ + 'displayAsLink' => true, + 'formElement' => 'container', + 'componentType' => 'container', + 'component' => 'Magento_Ui/js/form/components/button', + 'template' => 'ui/form/components/button/container', + 'actions' => [ + [ + 'targetName' => 'product_form.product_form.advanced_inventory_modal', + 'actionName' => 'toggleModal', + ], + ], + 'imports' => [ + 'childError' => 'product_form.product_form.advanced_inventory_modal.stock_data:error', + ], + 'title' => __('Advanced Inventory'), + 'provider' => false, + 'additionalForGroup' => true, + 'source' => 'product_details', + 'sortOrder' => 20, + ]; $container['children'] = [ - 'qty' => $this->getQtyMetaStructure(), - 'advanced_inventory_button' => $this->getAdvancedInventoryButtonMetaStructure(), + 'qty' => $qty, + 'advanced_inventory_button' => $advancedInventoryButton, ]; $this->meta = $this->arrayManager->merge( @@ -241,74 +276,4 @@ private function prepareMeta() ); } } - - /** - * Get Qty meta structure - * - * @return array - */ - private function getQtyMetaStructure() - { - return [ - 'arguments' => [ - 'data' => [ - 'config' => [ - 'component' => 'Magento_CatalogInventory/js/components/qty-validator-changer', - 'group' => 'quantity_and_stock_status_qty', - 'dataType' => 'number', - 'formElement' => 'input', - 'componentType' => 'field', - 'visible' => '1', - 'require' => '0', - 'additionalClasses' => 'admin__field-small', - 'label' => __('Quantity'), - 'scopeLabel' => '[GLOBAL]', - 'dataScope' => 'qty', - 'validation' => [ - 'validate-number' => true, - 'less-than-equals-to' => StockDataFilter::MAX_QTY_VALUE, - ], - 'imports' => [ - 'handleChanges' => '${$.provider}:data.product.stock_data.is_qty_decimal', - ], - 'sortOrder' => 10, - 'disabled' => $this->locator->getProduct()->isLockedAttribute('quantity_and_stock_status'), - ] - ] - ] - ]; - } - - /** - * Get advances inventory button meta structure - * - * @return array - */ - private function getAdvancedInventoryButtonMetaStructure() - { - return [ - 'arguments' => [ - 'data' => [ - 'config' => [ - 'displayAsLink' => true, - 'formElement' => 'container', - 'componentType' => 'container', - 'component' => 'Magento_Ui/js/form/components/button', - 'template' => 'ui/form/components/button/container', - 'actions' => [ - [ - 'targetName' => 'product_form.product_form.advanced_inventory_modal', - 'actionName' => 'toggleModal', - ], - ], - 'title' => __('Advanced Inventory'), - 'provider' => false, - 'additionalForGroup' => true, - 'source' => 'product_details', - 'sortOrder' => 20, - ] - ] - ] - ]; - } } diff --git a/app/code/Magento/CatalogInventory/registration.php b/app/code/Magento/CatalogInventory/registration.php index da5cb708ea17f..d39cc2074d890 100644 --- a/app/code/Magento/CatalogInventory/registration.php +++ b/app/code/Magento/CatalogInventory/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CatalogInventory', __DIR__); diff --git a/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml b/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml index b813aa5d356cb..c77c77a5183d0 100644 --- a/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml +++ b/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml @@ -35,9 +35,7 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="formElement" xsi:type="string">container</item> - <item name="label" xsi:type="string" translate="true">Manage Stock</item> <item name="dataScope" xsi:type="string">stock_data</item> - <item name="scopeLabel" xsi:type="string">[GLOBAL]</item> </item> </argument> <field name="manage_stock" formElement="select"> @@ -74,12 +72,8 @@ <link name="linkedValue">${$.provider}:data.product.stock_data.manage_stock</link> </links> <exports> - <link name="disabled">${$.parentName}.manage_stock:disabled</link> <link name="checked">${$.parentName}.manage_stock:disabled</link> </exports> - <imports> - <link name="disabled">ns = ${ $.ns }, index = qty, group = quantity_and_stock_status_qty:disabled</link> - </imports> </settings> <formElements> <checkbox class="Magento\CatalogInventory\Ui\Component\Product\Form\Element\UseConfigSettings"> @@ -105,7 +99,6 @@ <dataScope>quantity_and_stock_status.qty</dataScope> <links> <link name="value">ns = ${ $.ns }, index = qty, group = quantity_and_stock_status_qty:value</link> - <link name="disabled">ns = ${ $.ns }, index = qty, group = quantity_and_stock_status_qty:disabled</link> </links> <imports> <link name="handleChanges">${$.provider}:data.product.stock_data.is_qty_decimal</link> @@ -117,9 +110,7 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="formElement" xsi:type="string">container</item> - <item name="label" xsi:type="string" translate="true">Out-of-Stock Threshold</item> <item name="dataScope" xsi:type="string">stock_data</item> - <item name="scopeLabel" xsi:type="string">[GLOBAL]</item> <item name="imports" xsi:type="array"> <item name="visible" xsi:type="string">${$.provider}:data.product.stock_data.manage_stock</item> </item> @@ -154,12 +145,8 @@ <link name="linkedValue">${$.provider}:data.product.stock_data.min_qty</link> </links> <exports> - <link name="disabled">${$.parentName}.min_qty:disabled</link> <link name="checked">${$.parentName}.min_qty:disabled</link> </exports> - <imports> - <link name="disabled">ns = ${ $.ns }, index = qty, group = quantity_and_stock_status_qty:disabled</link> - </imports> </settings> <formElements> <checkbox class="Magento\CatalogInventory\Ui\Component\Product\Form\Element\UseConfigSettings"> @@ -220,13 +207,6 @@ <class name="admin__field-no-label">true</class> </additionalClasses> <dataScope>use_config_min_sale_qty</dataScope> - <exports> - <link name="disabled">${$.parentName}.min_sale_qty:disabled</link> - <link name="checked">${$.parentName}.min_sale_qty:disabled</link> - </exports> - <imports> - <link name="disabled">ns = ${ $.ns }, index = qty, group = quantity_and_stock_status_qty:disabled</link> - </imports> </settings> <formElements> <checkbox class="Magento\CatalogInventory\Ui\Component\Product\Form\Element\UseConfigSettings"> @@ -290,9 +270,7 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="formElement" xsi:type="string">container</item> - <item name="label" xsi:type="string" translate="true">Maximum Qty Allowed in Shopping Cart</item> <item name="dataScope" xsi:type="string">stock_data</item> - <item name="scopeLabel" xsi:type="string">[GLOBAL]</item> </item> </argument> <field name="max_sale_qty" formElement="input"> @@ -325,12 +303,8 @@ <link name="linkedValue">${$.provider}:data.product.stock_data.max_sale_qty</link> </links> <exports> - <link name="disabled">${$.parentName}.max_sale_qty:disabled</link> <link name="checked">${$.parentName}.max_sale_qty:disabled</link> </exports> - <imports> - <link name="disabled">ns = ${ $.ns }, index = qty, group = quantity_and_stock_status_qty:disabled</link> - </imports> </settings> <formElements> <checkbox class="Magento\CatalogInventory\Ui\Component\Product\Form\Element\UseConfigSettings"> @@ -358,7 +332,6 @@ <dataScope>stock_data.is_qty_decimal</dataScope> <imports> <link name="visible">${$.provider}:data.product.stock_data.manage_stock</link> - <link name="disabled">ns = ${ $.ns }, index = qty, group = quantity_and_stock_status_qty:disabled</link> </imports> </settings> <formElements> @@ -381,7 +354,6 @@ <dataScope>stock_data.is_decimal_divided</dataScope> <imports> <link name="visible">${$.provider}:data.product.stock_data.manage_stock</link> - <link name="disabled">ns = ${ $.ns }, index = qty, group = quantity_and_stock_status_qty:disabled</link> </imports> </settings> <formElements> @@ -396,9 +368,7 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="formElement" xsi:type="string">container</item> - <item name="label" xsi:type="string" translate="true">Backorders</item> <item name="dataScope" xsi:type="string">stock_data</item> - <item name="scopeLabel" xsi:type="string">[GLOBAL]</item> <item name="imports" xsi:type="array"> <item name="visible" xsi:type="string">${$.provider}:data.product.stock_data.manage_stock</item> </item> @@ -441,12 +411,8 @@ <link name="linkedValue">${$.provider}:data.product.stock_data.backorders</link> </links> <exports> - <link name="disabled">${$.parentName}.backorders:disabled</link> <link name="checked">${$.parentName}.backorders:disabled</link> </exports> - <imports> - <link name="disabled">ns = ${ $.ns }, index = qty, group = quantity_and_stock_status_qty:disabled</link> - </imports> </settings> <formElements> <checkbox class="Magento\CatalogInventory\Ui\Component\Product\Form\Element\UseConfigSettings"> @@ -465,9 +431,7 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="formElement" xsi:type="string">container</item> - <item name="label" xsi:type="string" translate="true">Notify for Quantity Below</item> <item name="dataScope" xsi:type="string">stock_data</item> - <item name="scopeLabel" xsi:type="string">[GLOBAL]</item> <item name="imports" xsi:type="array"> <item name="visible" xsi:type="string">${$.provider}:data.product.stock_data.manage_stock</item> </item> @@ -502,12 +466,8 @@ <link name="linkedValue">${$.provider}:data.product.stock_data.notify_stock_qty</link> </links> <exports> - <link name="disabled">${$.parentName}.notify_stock_qty:disabled</link> <link name="checked">${$.parentName}.notify_stock_qty:disabled</link> </exports> - <imports> - <link name="disabled">ns = ${ $.ns }, index = qty, group = quantity_and_stock_status_qty:disabled</link> - </imports> </settings> <formElements> <checkbox class="Magento\CatalogInventory\Ui\Component\Product\Form\Element\UseConfigSettings"> @@ -526,9 +486,7 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="formElement" xsi:type="string">container</item> - <item name="label" xsi:type="string" translate="true">Enable Qty Increments</item> <item name="dataScope" xsi:type="string">stock_data</item> - <item name="scopeLabel" xsi:type="string">[GLOBAL]</item> </item> </argument> <field name="enable_qty_increments" formElement="select"> @@ -565,12 +523,8 @@ <link name="linkedValue">${$.provider}:data.product.stock_data.enable_qty_increments</link> </links> <exports> - <link name="disabled">${$.parentName}.enable_qty_increments:disabled</link> <link name="checked">${$.parentName}.enable_qty_increments:disabled</link> </exports> - <imports> - <link name="disabled">ns = ${ $.ns }, index = qty, group = quantity_and_stock_status_qty:disabled</link> - </imports> </settings> <formElements> <checkbox class="Magento\CatalogInventory\Ui\Component\Product\Form\Element\UseConfigSettings"> @@ -589,9 +543,7 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="formElement" xsi:type="string">container</item> - <item name="label" xsi:type="string" translate="true">Qty Increments</item> <item name="dataScope" xsi:type="string">stock_data</item> - <item name="scopeLabel" xsi:type="string">[GLOBAL]</item> <item name="imports" xsi:type="array"> <item name="visible" xsi:type="string">${$.provider}:data.product.stock_data.enable_qty_increments</item> </item> @@ -630,12 +582,8 @@ <link name="linkedValue">${$.provider}:data.product.stock_data.qty_increments</link> </links> <exports> - <link name="disabled">${$.parentName}.qty_increments:disabled</link> <link name="checked">${$.parentName}.qty_increments:disabled</link> </exports> - <imports> - <link name="disabled">ns = ${ $.ns }, index = qty, group = quantity_and_stock_status_qty:disabled</link> - </imports> </settings> <formElements> <checkbox class="Magento\CatalogInventory\Ui\Component\Product\Form\Element\UseConfigSettings"> @@ -654,9 +602,7 @@ <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="formElement" xsi:type="string">container</item> - <item name="label" xsi:type="string" translate="true">Stock Status</item> <item name="dataScope" xsi:type="string">quantity_and_stock_status</item> - <item name="scopeLabel" xsi:type="string">[GLOBAL]</item> <item name="imports" xsi:type="array"> <item name="visible" xsi:type="string">${$.provider}:data.product.stock_data.manage_stock</item> </item> @@ -672,9 +618,6 @@ <scopeLabel>[GLOBAL]</scopeLabel> <label translate="true">Stock Status</label> <dataScope>is_in_stock</dataScope> - <imports> - <link name="disabled">ns = ${ $.ns }, index = qty, group = quantity_and_stock_status_qty:disabled</link> - </imports> </settings> <formElements> <select> diff --git a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php index 4f58293d53359..6d499b93e411f 100644 --- a/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php +++ b/app/code/Magento/CatalogRule/Controller/Adminhtml/Promo/Catalog/Save.php @@ -12,6 +12,7 @@ use Magento\Framework\Registry; use Magento\Framework\Stdlib\DateTime\Filter\Date; use Magento\Framework\App\Request\DataPersistorInterface; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; /** * Save action for catalog rule @@ -25,19 +26,27 @@ class Save extends \Magento\CatalogRule\Controller\Adminhtml\Promo\Catalog imple */ protected $dataPersistor; + /** + * @var TimezoneInterface + */ + private $localeDate; + /** * @param Context $context * @param Registry $coreRegistry * @param Date $dateFilter * @param DataPersistorInterface $dataPersistor + * @param TimezoneInterface $localeDate */ public function __construct( Context $context, Registry $coreRegistry, Date $dateFilter, - DataPersistorInterface $dataPersistor + DataPersistorInterface $dataPersistor, + TimezoneInterface $localeDate ) { $this->dataPersistor = $dataPersistor; + $this->localeDate = $localeDate; parent::__construct($context, $coreRegistry, $dateFilter); } @@ -46,16 +55,15 @@ public function __construct( * * @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface|void * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) */ public function execute() { if ($this->getRequest()->getPostValue()) { - /** @var \Magento\CatalogRule\Api\CatalogRuleRepositoryInterface $ruleRepository */ $ruleRepository = $this->_objectManager->get( \Magento\CatalogRule\Api\CatalogRuleRepositoryInterface::class ); - /** @var \Magento\CatalogRule\Model\Rule $model */ $model = $this->_objectManager->create(\Magento\CatalogRule\Model\Rule::class); @@ -65,7 +73,9 @@ public function execute() ['request' => $this->getRequest()] ); $data = $this->getRequest()->getPostValue(); - + if (!$this->getRequest()->getParam('from_date')) { + $data['from_date'] = $this->localeDate->formatDate(); + } $filterValues = ['from_date' => $this->_dateFilter]; if ($this->getRequest()->getParam('to_date')) { $filterValues['to_date'] = $this->_dateFilter; diff --git a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php index e12eabba76401..1fc53c78985fb 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php @@ -242,7 +242,22 @@ public function __construct( */ public function reindexById($id) { - $this->reindexByIds([$id]); + try { + $this->cleanProductIndex([$id]); + + $products = $this->productLoader->getProducts([$id]); + $activeRules = $this->getActiveRules(); + foreach ($products as $product) { + $this->applyRules($activeRules, $product); + } + + $this->reindexRuleGroupWebsite->execute(); + } catch (\Exception $e) { + $this->critical($e); + throw new \Magento\Framework\Exception\LocalizedException( + __('Catalog rule indexing failed. See details in exception log.') + ); + } } /** @@ -275,11 +290,18 @@ protected function doReindexByIds($ids) { $this->cleanProductIndex($ids); - $products = $this->productLoader->getProducts($ids); - $activeRules = $this->getActiveRules(); - foreach ($products as $product) { - $this->applyRules($activeRules, $product); + /** @var Rule[] $activeRules */ + $activeRules = $this->getActiveRules()->getItems(); + foreach ($activeRules as $rule) { + $rule->setProductsFilter($ids); + $this->reindexRuleProduct->execute($rule, $this->batchCount); } + + foreach ($ids as $productId) { + $this->cleanProductPriceIndex([$productId]); + $this->reindexRuleProductPrice->execute($this->batchCount, $productId); + } + $this->reindexRuleGroupWebsite->execute(); } @@ -365,17 +387,13 @@ protected function cleanByIds($productIds) * Assign product to rule * * @param Rule $rule - * @param Product $product + * @param int $productEntityId + * @param array $websiteIds * @return void */ - private function assignProductToRule(Rule $rule, Product $product): void + private function assignProductToRule(Rule $rule, int $productEntityId, array $websiteIds): void { - if (!$rule->validate($product)) { - return; - } - $ruleId = (int) $rule->getId(); - $productEntityId = (int) $product->getId(); $ruleProductTable = $this->getTable('catalogrule_product'); $this->connection->delete( $ruleProductTable, @@ -385,7 +403,6 @@ private function assignProductToRule(Rule $rule, Product $product): void ] ); - $websiteIds = array_intersect($product->getWebsiteIds(), $rule->getWebsiteIds()); $customerGroupIds = $rule->getCustomerGroupIds(); $fromTime = strtotime($rule->getFromDate()); $toTime = strtotime($rule->getToDate()); @@ -429,12 +446,17 @@ private function assignProductToRule(Rule $rule, Product $product): void * @param Product $product * @return $this * @throws \Exception + * @deprecated + * @see ReindexRuleProduct::execute * @SuppressWarnings(PHPMD.NPathComplexity) */ protected function applyRule(Rule $rule, $product) { - $this->assignProductToRule($rule, $product); - $this->reindexRuleProductPrice->execute($this->batchCount, $product); + if ($rule->validate($product)) { + $websiteIds = array_intersect($product->getWebsiteIds(), $rule->getWebsiteIds()); + $this->assignProductToRule($rule, $product->getId(), $websiteIds); + } + $this->reindexRuleProductPrice->execute($this->batchCount, $product->getId()); $this->reindexRuleGroupWebsite->execute(); return $this; @@ -450,11 +472,16 @@ protected function applyRule(Rule $rule, $product) private function applyRules(RuleCollection $ruleCollection, Product $product): void { foreach ($ruleCollection as $rule) { - $this->assignProductToRule($rule, $product); + if (!$rule->validate($product)) { + continue; + } + + $websiteIds = array_intersect($product->getWebsiteIds(), $rule->getWebsiteIds()); + $this->assignProductToRule($rule, $product->getId(), $websiteIds); } $this->cleanProductPriceIndex([$product->getId()]); - $this->reindexRuleProductPrice->execute($this->batchCount, $product); + $this->reindexRuleProductPrice->execute($this->batchCount, $product->getId()); } /** @@ -507,7 +534,7 @@ protected function updateRuleProductData(Rule $rule) */ protected function applyAllRules(Product $product = null) { - $this->reindexRuleProductPrice->execute($this->batchCount, $product); + $this->reindexRuleProductPrice->execute($this->batchCount, $product->getId()); $this->reindexRuleGroupWebsite->execute(); return $this; } @@ -562,7 +589,7 @@ protected function calcRuleProductPrice($ruleData, $productData = null) */ protected function getRuleProductsStmt($websiteId, Product $product = null) { - return $this->ruleProductsSelectBuilder->build($websiteId, $product); + return $this->ruleProductsSelectBuilder->build((int) $websiteId, (int) $product->getId()); } /** diff --git a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php index e589c8595ce2c..944710773123f 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProduct.php @@ -101,7 +101,9 @@ public function execute(Rule $rule, $batchCount, $useAdditionalTable = false) $scopeTz = new \DateTimeZone( $this->localeDate->getConfigTimezone(ScopeInterface::SCOPE_WEBSITE, $websiteId) ); - $fromTime = (new \DateTime($rule->getFromDate(), $scopeTz))->getTimestamp(); + $fromTime = $rule->getFromDate() + ? (new \DateTime($rule->getFromDate(), $scopeTz))->getTimestamp() + : 0; $toTime = $rule->getToDate() ? (new \DateTime($rule->getToDate(), $scopeTz))->getTimestamp() + IndexBuilder::SECONDS_IN_DAY - 1 : 0; diff --git a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProductPrice.php b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProductPrice.php index 11ba87730bec1..51869f1accbb3 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProductPrice.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/ReindexRuleProductPrice.php @@ -6,7 +6,6 @@ namespace Magento\CatalogRule\Model\Indexer; -use Magento\Catalog\Model\Product; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; use Magento\Store\Model\StoreManagerInterface; @@ -65,19 +64,19 @@ public function __construct( * Reindex product prices. * * @param int $batchCount - * @param Product|null $product + * @param int|null $productId * @param bool $useAdditionalTable * @return bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - public function execute($batchCount, Product $product = null, $useAdditionalTable = false) + public function execute(int $batchCount, ?int $productId = null, bool $useAdditionalTable = false) { /** * Update products rules prices per each website separately * because for each website date in website's timezone should be used */ foreach ($this->storeManager->getWebsites() as $website) { - $productsStmt = $this->ruleProductsSelectBuilder->build($website->getId(), $product, $useAdditionalTable); + $productsStmt = $this->ruleProductsSelectBuilder->build($website->getId(), $productId, $useAdditionalTable); $dayPrices = []; $stopFlags = []; $prevKey = null; diff --git a/app/code/Magento/CatalogRule/Model/Indexer/RuleProductsSelectBuilder.php b/app/code/Magento/CatalogRule/Model/Indexer/RuleProductsSelectBuilder.php index 6989a33535ad8..e15bf6b3b1faa 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/RuleProductsSelectBuilder.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/RuleProductsSelectBuilder.php @@ -74,15 +74,12 @@ public function __construct( * Build select for indexer according passed parameters. * * @param int $websiteId - * @param \Magento\Catalog\Model\Product|null $product + * @param int|null $productId * @param bool $useAdditionalTable * @return \Zend_Db_Statement_Interface */ - public function build( - $websiteId, - \Magento\Catalog\Model\Product $product = null, - $useAdditionalTable = false - ) { + public function build(int $websiteId, ?int $productId = null, bool $useAdditionalTable = false) + { $connection = $this->resource->getConnection(); $indexTable = $this->resource->getTableName('catalogrule_product'); if ($useAdditionalTable) { @@ -107,8 +104,8 @@ public function build( ['rp.website_id', 'rp.customer_group_id', 'rp.product_id', 'rp.sort_order', 'rp.rule_id'] ); - if ($product && $product->getEntityId()) { - $select->where('rp.product_id=?', $product->getEntityId()); + if ($productId) { + $select->where('rp.product_id=?', $productId); } /** @@ -159,9 +156,11 @@ public function build( sprintf($joinCondition, $tableAlias, $storeId), [] ); - $select->columns([ - 'default_price' => $connection->getIfNullSql($tableAlias . '.value', 'pp_default.value'), - ]); + $select->columns( + [ + 'default_price' => $connection->getIfNullSql($tableAlias . '.value', 'pp_default.value'), + ] + ); return $connection->query($select); } diff --git a/app/code/Magento/CatalogRule/Model/Rule.php b/app/code/Magento/CatalogRule/Model/Rule.php index f2e8e54d34665..cd24201963f25 100644 --- a/app/code/Magento/CatalogRule/Model/Rule.php +++ b/app/code/Magento/CatalogRule/Model/Rule.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\CatalogRule\Model; use Magento\Catalog\Model\Product; @@ -13,6 +15,7 @@ use Magento\CatalogRule\Helper\Data; use Magento\CatalogRule\Model\Data\Condition\Converter; use Magento\CatalogRule\Model\Indexer\Rule\RuleProductProcessor; +use Magento\CatalogRule\Model\ResourceModel\Product\ConditionsToCollectionApplier; use Magento\CatalogRule\Model\ResourceModel\Rule as RuleResourceModel; use Magento\CatalogRule\Model\Rule\Action\CollectionFactory as RuleCollectionFactory; use Magento\CatalogRule\Model\Rule\Condition\CombineFactory; @@ -33,7 +36,6 @@ use Magento\Framework\Stdlib\DateTime; use Magento\Framework\Stdlib\DateTime\TimezoneInterface; use Magento\Store\Model\StoreManagerInterface; -use Magento\CatalogRule\Model\ResourceModel\Product\ConditionsToCollectionApplier; /** * Catalog Rule data model @@ -499,7 +501,8 @@ public function calcProductPriceRule(Product $product, $price) } else { $customerGroupId = $this->_customerSession->getCustomerGroupId(); } - $dateTs = $this->_localeDate->scopeTimeStamp($storeId); + $currentDateTime = new \DateTime(); + $dateTs = $currentDateTime->getTimestamp(); $cacheKey = date('Y-m-d', $dateTs) . "|{$websiteId}|{$customerGroupId}|{$productId}|{$price}"; if (!array_key_exists($cacheKey, self::$_priceRulesData)) { @@ -895,4 +898,12 @@ public function getIdentities() { return ['price']; } + + /** + * Clear price rules cache. + */ + public function clearPriceRulesData(): void + { + self::$_priceRulesData = []; + } } diff --git a/app/code/Magento/CatalogRule/Observer/ProcessAdminFinalPriceObserver.php b/app/code/Magento/CatalogRule/Observer/ProcessAdminFinalPriceObserver.php index 89ed519cfb8c8..0add936467471 100644 --- a/app/code/Magento/CatalogRule/Observer/ProcessAdminFinalPriceObserver.php +++ b/app/code/Magento/CatalogRule/Observer/ProcessAdminFinalPriceObserver.php @@ -7,9 +7,10 @@ namespace Magento\CatalogRule\Observer; -use Magento\Framework\Stdlib\DateTime\TimezoneInterface; -use Magento\Framework\Registry; use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Registry; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Store\Model\StoreManagerInterface; /** * Observer for applying catalog rules on product for admin area @@ -23,6 +24,11 @@ class ProcessAdminFinalPriceObserver implements ObserverInterface */ protected $coreRegistry; + /** + * @var StoreManagerInterface + */ + protected $storeManager; + /** * @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface */ @@ -41,17 +47,20 @@ class ProcessAdminFinalPriceObserver implements ObserverInterface /** * @param RulePricesStorage $rulePricesStorage * @param Registry $coreRegistry + * @param StoreManagerInterface $storeManager * @param \Magento\CatalogRule\Model\ResourceModel\RuleFactory $resourceRuleFactory * @param TimezoneInterface $localeDate */ public function __construct( RulePricesStorage $rulePricesStorage, Registry $coreRegistry, + StoreManagerInterface $storeManager, \Magento\CatalogRule\Model\ResourceModel\RuleFactory $resourceRuleFactory, TimezoneInterface $localeDate ) { $this->rulePricesStorage = $rulePricesStorage; $this->coreRegistry = $coreRegistry; + $this->storeManager = $storeManager; $this->resourceRuleFactory = $resourceRuleFactory; $this->localeDate = $localeDate; } @@ -61,6 +70,7 @@ public function __construct( * * @param \Magento\Framework\Event\Observer $observer * @return $this + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function execute(\Magento\Framework\Event\Observer $observer) { @@ -74,13 +84,17 @@ public function execute(\Magento\Framework\Event\Observer $observer) $wId = $ruleData->getWebsiteId(); $gId = $ruleData->getCustomerGroupId(); $pId = $product->getId(); - $key = "{$date->format('Y-m-d H:i:s')}|{$wId}|{$gId}|{$pId}"; } elseif ($product->getWebsiteId() !== null && $product->getCustomerGroupId() !== null) { $wId = $product->getWebsiteId(); $gId = $product->getCustomerGroupId(); $pId = $product->getId(); $key = "{$date->format('Y-m-d H:i:s')}|{$wId}|{$gId}|{$pId}"; + } elseif ($product->getWebsiteId() === null && $product->getCustomerGroupId() !== null) { + $wId = $this->storeManager->getStore($storeId)->getWebsiteId(); + $gId = $product->getCustomerGroupId(); + $pId = $product->getId(); + $key = "{$date->format('Y-m-d H:i:s')}|{$wId}|{$gId}|{$pId}"; } if ($key) { diff --git a/app/code/Magento/CatalogRule/Pricing/Price/CatalogRulePrice.php b/app/code/Magento/CatalogRule/Pricing/Price/CatalogRulePrice.php index 7cbbc547571ab..ec63d70d55abf 100644 --- a/app/code/Magento/CatalogRule/Pricing/Price/CatalogRulePrice.php +++ b/app/code/Magento/CatalogRule/Pricing/Price/CatalogRulePrice.php @@ -85,7 +85,8 @@ public function getValue() { if (null === $this->value) { if ($this->product->hasData(self::PRICE_CODE)) { - $this->value = (float)$this->product->getData(self::PRICE_CODE) ?: false; + $value = $this->product->getData(self::PRICE_CODE); + $this->value = $value ? (float)$value : false; } else { $this->value = $this->ruleResource->getRulePrice( $this->dateTime->scopeDate($this->storeManager->getStore()->getId()), diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCreateCatalogPriceRuleWithConditionActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCreateCatalogPriceRuleWithConditionActionGroup.xml new file mode 100644 index 0000000000000..eebc1175f1894 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCreateCatalogPriceRuleWithConditionActionGroup.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="AdminCreateCatalogPriceRuleWithConditionActionGroup" extends="CreateCatalogPriceRuleActionGroup"> + <arguments> + <argument name="catalogRuleType" type="entity" defaultValue="PriceRuleWithCondition"/> + </arguments> + <waitForPageLoad stepKey="waitForPageLoad" after="addNewRule"/> + <click selector="{{AdminNewCatalogPriceRule.conditionsTab}}" stepKey="expandConditions" before="openActionDropdown"/> + <scrollTo selector="{{AdminNewCatalogPriceRule.conditionsTab}}" stepKey="scrollToConditionsTab" after="expandConditions"/> + <waitForElementVisible selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="waitForNewRule" after="scrollToConditionsTab"/> + <click selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="clickNewRule" after="waitForNewRule"/> + <selectOption selector="{{AdminNewCatalogPriceRuleConditions.conditionsDropdown}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="selectProductAttribute" after="clickNewRule"/> + <waitForPageLoad stepKey="waitForAttributeLoad" after="selectProductAttribute"/> + <!--Assert that attribute contains today date without time--> + <comment userInput="Assert that attribute contains today date without time" stepKey="assertDate" after="waitForAttributeLoad"/> + <generateDate date="now" format="Y-m-d" stepKey="today" after="assertDate"/> + <grabTextFrom selector="{{PriceRuleConditionsSection.firstProductAttributeSelected}}" stepKey="grabTextFromSelectedAttribute" after="today"/> + <assertEquals expected="$today" actual="$grabTextFromSelectedAttribute" stepKey="assertTodayDate" after="grabTextFromSelectedAttribute"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCreateMultipleWebsiteCatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCreateMultipleWebsiteCatalogPriceRuleActionGroup.xml new file mode 100644 index 0000000000000..06887a6aaba96 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCreateMultipleWebsiteCatalogPriceRuleActionGroup.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="AdminCreateMultipleWebsiteCatalogPriceRuleActionGroup" extends="CreateCatalogPriceRuleActionGroup"> + <remove keyForRemoval="selectSite"/> + <selectOption selector="{{AdminNewCatalogPriceRule.websites}}" parameterArray="['FirstWebsite', 'SecondWebsite']" stepKey="selectWebsite"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminFillCatalogRuleConditionActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminFillCatalogRuleConditionActionGroup.xml new file mode 100644 index 0000000000000..fd173acfa434c --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminFillCatalogRuleConditionActionGroup.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminFillCatalogRuleConditionActionGroup"> + <annotations> + <description>Clicks on the Conditions tab. Fills in the provided condition for Catalog Price Rule.</description> + </annotations> + <arguments> + <argument name="condition" type="string" defaultValue="{{CatalogRuleProductConditions.categoryIds}}"/> + <argument name="conditionOperator" type="string" defaultValue="is"/> + <argument name="conditionValue" type="string" defaultValue="2"/> + </arguments> + + <conditionalClick selector="{{AdminNewCatalogPriceRule.conditionsTab}}" dependentSelector="{{AdminNewCatalogPriceRuleConditions.newCondition}}" visible="false" stepKey="openConditionsTab"/> + <waitForElementVisible selector="{{AdminNewCatalogPriceRuleConditions.newCondition}}" stepKey="waitForAddConditionButton"/> + <click selector="{{AdminNewCatalogPriceRuleConditions.newCondition}}" stepKey="addNewCondition"/> + <selectOption selector="{{AdminNewCatalogPriceRuleConditions.conditionSelect('1')}}" userInput="{{condition}}" stepKey="selectTypeCondition"/> + <click selector="{{AdminNewCatalogPriceRuleConditions.condition('is')}}" stepKey="clickOnOperator"/> + <selectOption selector="{{AdminNewCatalogPriceRuleConditions.activeOperatorSelect}}" userInput="{{conditionOperator}}" stepKey="selectCondition"/> + <!-- In case we are choosing already selected value - select is not closed automatically --> + <conditionalClick selector="{{AdminNewCatalogPriceRuleConditions.condition('...')}}" dependentSelector="{{AdminNewCatalogPriceRuleConditions.activeOperatorSelect}}" visible="true" stepKey="closeSelect"/> + <click selector="{{AdminNewCatalogPriceRuleConditions.condition('...')}}" stepKey="clickToChooseOption3"/> + <waitForElementVisible selector="{{AdminNewCatalogPriceRuleConditions.activeValueInput}}" stepKey="waitForValueInput"/> + <fillField selector="{{AdminNewCatalogPriceRuleConditions.activeValueInput}}" userInput="{{conditionValue}}" stepKey="fillConditionValue"/> + <click selector="{{AdminNewCatalogPriceRuleConditions.activeConditionApplyButton}}" stepKey="clickApply"/> + <waitForElementNotVisible selector="{{AdminNewCatalogPriceRuleConditions.activeConditionApplyButton}}" stepKey="waitForApplyButtonInvisibility"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/ApplyCatalogPriceRulesActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/ApplyCatalogPriceRulesActionGroup.xml new file mode 100644 index 0000000000000..0b74558599fbd --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/ApplyCatalogPriceRulesActionGroup.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="ApplyCatalogPriceRulesActionGroup"> + <annotations> + <description>Goes to the Catalog Price Rule grid page. Clicks on Apply Rules. Validates that the Success Message is present and correct.</description> + </annotations> + + <amOnPage stepKey="goToPriceRulePage" url="{{CatalogRulePage.url}}"/> + <waitForPageLoad stepKey="waitForPriceRulePage"/> + <click stepKey="applyRules" selector="{{AdminCatalogPriceRuleGrid.applyRules}}"/> + <see stepKey="assertSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="Updated rules applied."/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml index 09053b5ad14a3..e0d02a280bf6c 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml @@ -9,7 +9,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <!-- action group to create a new catalog price rule giving a catalogRule entity --> - <actionGroup name="newCatalogPriceRuleByUI"> + <actionGroup name="NewCatalogPriceRuleByUIActionGroup"> <annotations> <description>Goes to the Catalog Price Rule grid. Clicks on Add. Fills in the provided Catalog Rule details.</description> </annotations> @@ -40,172 +40,4 @@ <scrollToTopOfPage stepKey="scrollToTop"/> <waitForPageLoad stepKey="waitForApplied"/> </actionGroup> - - <actionGroup name="createCatalogPriceRule"> - <annotations> - <description>Clicks on the Add button. Fills Rule Name, Description, Website and Discount Value.</description> - </annotations> - <arguments> - <argument name="catalogRule" defaultValue="_defaultCatalogRule"/> - </arguments> - <click stepKey="addNewRule" selector="{{AdminGridMainControls.add}}"/> - <fillField selector="{{AdminNewCatalogPriceRule.ruleName}}" userInput="{{catalogRule.name}}" stepKey="fillName" /> - <click stepKey="selectActive" selector="{{AdminCategoryBasicFieldSection.enableCategoryLabel}}"/> - <fillField selector="{{AdminNewCatalogPriceRule.description}}" userInput="{{catalogRule.description}}" stepKey="fillDescription" /> - <selectOption selector="{{AdminNewCatalogPriceRule.websites}}" parameterArray="{{catalogRule.website_ids}}" stepKey="selectSite" /> - <click stepKey="openActionDropdown" selector="{{AdminNewCatalogPriceRule.actionsTab}}"/> - <fillField stepKey="fillDiscountValue" selector="{{AdminNewCatalogPriceRuleActions.discountAmount}}" userInput="{{catalogRule.discount_amount}}"/> - <scrollToTopOfPage stepKey="scrollToTop"/> - <click selector="{{AdminNewCatalogPriceRule.save}}" stepKey="clickSave"/> - <waitForPageLoad stepKey="waitForApplied"/> - </actionGroup> - - <actionGroup name="AdminCreateCatalogPriceRuleWithConditionActionGroup" extends="createCatalogPriceRule"> - <arguments> - <argument name="catalogRuleType" type="entity" defaultValue="PriceRuleWithCondition"/> - </arguments> - <waitForPageLoad stepKey="waitForPageLoad" after="addNewRule"/> - <click selector="{{AdminNewCatalogPriceRule.conditionsTab}}" stepKey="expandConditions" before="openActionDropdown"/> - <scrollTo selector="{{AdminNewCatalogPriceRule.conditionsTab}}" stepKey="scrollToConditionsTab" after="expandConditions"/> - <waitForElementVisible selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="waitForNewRule" after="scrollToConditionsTab"/> - <click selector="{{PriceRuleConditionsSection.createNewRule}}" stepKey="clickNewRule" after="waitForNewRule"/> - <selectOption selector="{{AdminNewCatalogPriceRuleConditions.conditionsDropdown}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="selectProductAttribute" after="clickNewRule"/> - <waitForPageLoad stepKey="waitForAttributeLoad" after="selectProductAttribute"/> - <!--Assert that attribute contains today date without time--> - <comment userInput="Assert that attribute contains today date without time" stepKey="assertDate" after="waitForAttributeLoad"/> - <generateDate date="now" format="Y-m-d" stepKey="today" after="assertDate"/> - <grabTextFrom selector="{{PriceRuleConditionsSection.firstProductAttributeSelected}}" stepKey="grabTextFromSelectedAttribute" after="today"/> - <assertEquals expected="$today" actual="$grabTextFromSelectedAttribute" stepKey="assertTodayDate" after="grabTextFromSelectedAttribute"/> - </actionGroup> - <actionGroup name="AdminCreateMultipleWebsiteCatalogPriceRule" extends="createCatalogPriceRule"> - <remove keyForRemoval="selectSite"/> - <selectOption selector="{{AdminNewCatalogPriceRule.websites}}" parameterArray="['FirstWebsite', 'SecondWebsite']" stepKey="selectWebsite"/> - </actionGroup> - <actionGroup name="CreateCatalogPriceRuleViaTheUi"> - <arguments> - <argument name="catalogRule" defaultValue="_defaultCatalogRule"/> - <argument name="customerGroup" defaultValue="General" type="string"/> - <argument name="disregardRules" defaultValue="Yes" type="string"/> - </arguments> - - <fillField selector="{{AdminNewCatalogPriceRule.ruleName}}" userInput="{{catalogRule.name}}" stepKey="fillName1"/> - <fillField selector="{{AdminNewCatalogPriceRule.description}}" userInput="{{catalogRule.description}}" stepKey="fillDescription1"/> - <selectOption selector="{{AdminNewCatalogPriceRule.websites}}" userInput="{{catalogRule.website_ids[0]}}" stepKey="selectWebSite1"/> - <selectOption selector="{{AdminNewCatalogPriceRule.customerGroups}}" userInput="{{customerGroup}}" stepKey="selectCustomerGroup1"/> - <scrollTo selector="{{AdminNewCatalogPriceRule.actionsTab}}" stepKey="scrollToActionTab1"/> - <click selector="{{AdminNewCatalogPriceRule.actionsTab}}" stepKey="openActionDropdown1"/> - <selectOption selector="{{AdminNewCatalogPriceRuleActions.apply}}" userInput="{{catalogRule.simple_action}}" stepKey="discountType1"/> - <fillField selector="{{AdminNewCatalogPriceRuleActions.discountAmount}}" userInput="{{catalogRule.discount_amount}}" stepKey="fillDiscountValue1"/> - <selectOption selector="{{AdminNewCatalogPriceRuleActions.disregardRules}}" userInput="{{disregardRules}}" stepKey="discardSubsequentRules1"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> - <scrollToTopOfPage stepKey="scrollToTop1"/> - </actionGroup> - - <actionGroup name="CreateCatalogPriceRuleConditionWithAttribute"> - <annotations> - <description>Add Conditional Requirements to a Catalog Price Rule from the creation/edit page.</description> - </annotations> - <arguments> - <argument name="attributeName" type="string"/> - <argument name="targetValue" type="string"/> - <argument name="targetSelectValue" type="string"/> - </arguments> - - <click selector="{{AdminNewCatalogPriceRule.conditionsTab}}" stepKey="openConditionsTab"/> - <waitForPageLoad stepKey="waitForConditionTabOpened"/> - <click selector="{{AdminNewCatalogPriceRuleConditions.newCondition}}" stepKey="addNewCondition"/> - <selectOption selector="{{AdminNewCatalogPriceRuleConditions.conditionSelect('1')}}" userInput="{{attributeName}}" stepKey="selectTypeCondition"/> - <waitForElement selector="{{AdminNewCatalogPriceRuleConditions.targetEllipsisValue('1', targetValue)}}" stepKey="waitForIsTarget"/> - <click selector="{{AdminNewCatalogPriceRuleConditions.targetEllipsisValue('1', 'is')}}" stepKey="clickOnIs"/> - <selectOption selector="{{AdminNewCatalogPriceRuleConditions.targetSelect('1')}}" userInput="{{targetSelectValue}}" stepKey="selectTargetCondition"/> - <click selector="{{AdminNewCatalogPriceRule.fromDateButton}}" stepKey="clickFromCalender"/> - <click selector="{{AdminNewCatalogPriceRule.todayDate}}" stepKey="clickFromToday"/> - </actionGroup> - - <!-- Apply all of the saved catalog price rules --> - <actionGroup name="applyCatalogPriceRules"> - <annotations> - <description>Goes to the Catalog Price Rule grid page. Clicks on Apply Rules. Validates that the Success Message is present and correct.</description> - </annotations> - - <amOnPage stepKey="goToPriceRulePage" url="{{CatalogRulePage.url}}"/> - <waitForPageLoad stepKey="waitForPriceRulePage"/> - <click stepKey="applyRules" selector="{{AdminCatalogPriceRuleGrid.applyRules}}"/> - <see stepKey="assertSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="Updated rules applied."/> - </actionGroup> - - <!--Add Catalog Rule Condition With product SKU--> - <actionGroup name="newCatalogPriceRuleByUIWithConditionIsSKU" extends="newCatalogPriceRuleByUI"> - <annotations> - <description>EXTENDS: newCatalogPriceRuleByUI. Add a Catalog Price Rule Condition based on the provided SKU.</description> - </annotations> - <arguments> - <argument name="productSku"/> - </arguments> - - <click selector="{{AdminNewCatalogPriceRule.conditionsTab}}" after="discardSubsequentRules" stepKey="openConditionsTab"/> - <waitForPageLoad after="openConditionsTab" stepKey="waitForConditionTabOpened"/> - <click selector="{{AdminNewCatalogPriceRuleConditions.newCondition}}" after="waitForConditionTabOpened" stepKey="addNewCondition"/> - <selectOption selector="{{AdminNewCatalogPriceRuleConditions.conditionSelect('1')}}" userInput="Magento\CatalogRule\Model\Rule\Condition\Product|sku" after="addNewCondition" stepKey="selectTypeCondition"/> - <waitForPageLoad after="selectTypeCondition" stepKey="waitForConditionChosed"/> - <click selector="{{AdminNewCatalogPriceRuleConditions.targetEllipsis('1')}}" after="waitForConditionChosed" stepKey="clickEllipsis"/> - <fillField selector="{{AdminNewCatalogPriceRuleConditions.targetInput('1', '1')}}" userInput="{{productSku}}" after="clickEllipsis" stepKey="fillProductSku"/> - <click selector="{{AdminNewCatalogPriceRuleConditions.applyButton('1', '1')}}" after="fillProductSku" stepKey="clickApply"/> - </actionGroup> - - <!--Add Catalog Rule Condition With Category--> - <actionGroup name="newCatalogPriceRuleByUIWithConditionIsCategory" extends="newCatalogPriceRuleByUI"> - <annotations> - <description>EXTENDS: newCatalogPriceRuleByUI. Add a Catalog Price Rule Condition based on the provided Category ID.</description> - </annotations> - <arguments> - <argument name="categoryId"/> - </arguments> - - <click selector="{{AdminNewCatalogPriceRule.conditionsTab}}" after="discardSubsequentRules" stepKey="openConditionsTab"/> - <waitForPageLoad after="openConditionsTab" stepKey="waitForConditionTabOpened"/> - <click selector="{{AdminNewCatalogPriceRuleConditions.newCondition}}" after="waitForConditionTabOpened" stepKey="addNewCondition"/> - <selectOption selector="{{AdminNewCatalogPriceRuleConditions.conditionSelect('1')}}" userInput="Magento\CatalogRule\Model\Rule\Condition\Product|category_ids" after="addNewCondition" stepKey="selectTypeCondition"/> - <waitForPageLoad after="selectTypeCondition" stepKey="waitForConditionChosed"/> - <click selector="{{AdminNewCatalogPriceRuleConditions.targetEllipsis('1')}}" after="waitForConditionChosed" stepKey="clickEllipsis"/> - <fillField selector="{{AdminNewCatalogPriceRuleConditions.targetInput('1', '1')}}" userInput="{{categoryId}}" after="clickEllipsis" stepKey="fillCategoryId"/> - <click selector="{{AdminNewCatalogPriceRuleConditions.applyButton('1', '1')}}" after="fillCategoryId" stepKey="clickApply"/> - </actionGroup> - - <actionGroup name="selectGeneralCustomerGroupActionGroup"> - <annotations> - <description>Selects the 'General' Customer Group for a Catalog Price Rule on the creation/edit page.</description> - </annotations> - - <selectOption selector="{{AdminNewCatalogPriceRule.customerGroups}}" userInput="General" stepKey="selectCustomerGroup"/> - </actionGroup> - - <actionGroup name="selectNotLoggedInCustomerGroupActionGroup"> - <annotations> - <description>Selects the 'NOT LOGGED IN' Customer Group for a Catalog Price Rule on the creation/edit page.</description> - </annotations> - - <selectOption selector="{{AdminNewCatalogPriceRule.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> - </actionGroup> - - <!-- Open rule for Edit --> - <actionGroup name="OpenCatalogPriceRule"> - <arguments> - <argument name="ruleName" type="string" defaultValue="CustomCatalogRule.name"/> - </arguments> - <amOnPage url="{{AdminCatalogPriceRuleGridPage.url}}" stepKey="goToAdminCatalogPriceRuleGridPage"/> - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> - <fillField selector="{{AdminCatalogPriceRuleGridSection.filterByRuleName}}" userInput="{{ruleName}}" stepKey="filterByRuleName"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearch"/> - <click selector="{{AdminGridTableSection.row('1')}}" stepKey="clickEdit"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - </actionGroup> - - <actionGroup name="RemoveCatalogPriceRule" extends="OpenCatalogPriceRule"> - <click selector="{{AdminMainActionsSection.delete}}" after="waitForPageLoad" stepKey="clickToDelete"/> - <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" after="clickToDelete" stepKey="waitForElementVisible"/> - <click selector="{{AdminConfirmationModalSection.ok}}" after="waitForElementVisible" stepKey="clickToConfirm"/> - <waitForElementVisible selector="{{AdminMessagesSection.success}}" after="clickToConfirm" stepKey="waitForSuccessMessage"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the rule." after="waitForSuccessMessage" stepKey="verifyRuleIsDeleted"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CreateCatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CreateCatalogPriceRuleActionGroup.xml new file mode 100644 index 0000000000000..f43cd44c50de2 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CreateCatalogPriceRuleActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateCatalogPriceRuleActionGroup"> + <annotations> + <description>Clicks on the Add button. Fills Rule Name, Description, Website and Discount Value.</description> + </annotations> + <arguments> + <argument name="catalogRule" defaultValue="_defaultCatalogRule"/> + </arguments> + <click stepKey="addNewRule" selector="{{AdminGridMainControls.add}}"/> + <fillField selector="{{AdminNewCatalogPriceRule.ruleName}}" userInput="{{catalogRule.name}}" stepKey="fillName" /> + <click stepKey="selectActive" selector="{{AdminCategoryBasicFieldSection.enableCategoryLabel}}"/> + <fillField selector="{{AdminNewCatalogPriceRule.description}}" userInput="{{catalogRule.description}}" stepKey="fillDescription" /> + <selectOption selector="{{AdminNewCatalogPriceRule.websites}}" parameterArray="{{catalogRule.website_ids}}" stepKey="selectSite" /> + <click stepKey="openActionDropdown" selector="{{AdminNewCatalogPriceRule.actionsTab}}"/> + <fillField stepKey="fillDiscountValue" selector="{{AdminNewCatalogPriceRuleActions.discountAmount}}" userInput="{{catalogRule.discount_amount}}"/> + <scrollToTopOfPage stepKey="scrollToTop"/> + <click selector="{{AdminNewCatalogPriceRule.save}}" stepKey="clickSave"/> + <waitForPageLoad stepKey="waitForApplied"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CreateCatalogPriceRuleConditionWithAttributeActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CreateCatalogPriceRuleConditionWithAttributeActionGroup.xml new file mode 100644 index 0000000000000..97d1324f7c4fa --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CreateCatalogPriceRuleConditionWithAttributeActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateCatalogPriceRuleConditionWithAttributeActionGroup"> + <annotations> + <description>Add Conditional Requirements to a Catalog Price Rule from the creation/edit page.</description> + </annotations> + <arguments> + <argument name="attributeName" type="string"/> + <argument name="targetValue" type="string"/> + <argument name="targetSelectValue" type="string"/> + </arguments> + + <click selector="{{AdminNewCatalogPriceRule.conditionsTab}}" stepKey="openConditionsTab"/> + <waitForPageLoad stepKey="waitForConditionTabOpened"/> + <click selector="{{AdminNewCatalogPriceRuleConditions.newCondition}}" stepKey="addNewCondition"/> + <selectOption selector="{{AdminNewCatalogPriceRuleConditions.conditionSelect('1')}}" userInput="{{attributeName}}" stepKey="selectTypeCondition"/> + <waitForElement selector="{{AdminNewCatalogPriceRuleConditions.targetEllipsisValue('1', targetValue)}}" stepKey="waitForIsTarget"/> + <click selector="{{AdminNewCatalogPriceRuleConditions.targetEllipsisValue('1', 'is')}}" stepKey="clickOnIs"/> + <selectOption selector="{{AdminNewCatalogPriceRuleConditions.targetSelect('1')}}" userInput="{{targetSelectValue}}" stepKey="selectTargetCondition"/> + <click selector="{{AdminNewCatalogPriceRule.fromDateButton}}" stepKey="clickFromCalender"/> + <click selector="{{AdminNewCatalogPriceRule.todayDate}}" stepKey="clickFromToday"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CreateCatalogPriceRuleViaTheUiActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CreateCatalogPriceRuleViaTheUiActionGroup.xml new file mode 100644 index 0000000000000..f549102ecd140 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CreateCatalogPriceRuleViaTheUiActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateCatalogPriceRuleViaTheUiActionGroup"> + <arguments> + <argument name="catalogRule" defaultValue="_defaultCatalogRule"/> + <argument name="customerGroup" defaultValue="General" type="string"/> + <argument name="disregardRules" defaultValue="Yes" type="string"/> + </arguments> + + <fillField selector="{{AdminNewCatalogPriceRule.ruleName}}" userInput="{{catalogRule.name}}" stepKey="fillName1"/> + <fillField selector="{{AdminNewCatalogPriceRule.description}}" userInput="{{catalogRule.description}}" stepKey="fillDescription1"/> + <selectOption selector="{{AdminNewCatalogPriceRule.websites}}" userInput="{{catalogRule.website_ids[0]}}" stepKey="selectWebSite1"/> + <selectOption selector="{{AdminNewCatalogPriceRule.customerGroups}}" userInput="{{customerGroup}}" stepKey="selectCustomerGroup1"/> + <scrollTo selector="{{AdminNewCatalogPriceRule.actionsTab}}" stepKey="scrollToActionTab1"/> + <click selector="{{AdminNewCatalogPriceRule.actionsTab}}" stepKey="openActionDropdown1"/> + <selectOption selector="{{AdminNewCatalogPriceRuleActions.apply}}" userInput="{{catalogRule.simple_action}}" stepKey="discountType1"/> + <fillField selector="{{AdminNewCatalogPriceRuleActions.discountAmount}}" userInput="{{catalogRule.discount_amount}}" stepKey="fillDiscountValue1"/> + <selectOption selector="{{AdminNewCatalogPriceRuleActions.disregardRules}}" userInput="{{disregardRules}}" stepKey="discardSubsequentRules1"/> + <waitForPageLoad stepKey="waitForPageToLoad1"/> + <scrollToTopOfPage stepKey="scrollToTop1"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/NewCatalogPriceRuleByUIWithConditionIsCategoryActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/NewCatalogPriceRuleByUIWithConditionIsCategoryActionGroup.xml new file mode 100644 index 0000000000000..9b5a482c214d4 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/NewCatalogPriceRuleByUIWithConditionIsCategoryActionGroup.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="NewCatalogPriceRuleByUIWithConditionIsCategoryActionGroup" extends="NewCatalogPriceRuleByUIActionGroup"> + <annotations> + <description>EXTENDS: newCatalogPriceRuleByUI. Add a Catalog Price Rule Condition based on the provided Category ID.</description> + </annotations> + <arguments> + <argument name="categoryId"/> + </arguments> + + <click selector="{{AdminNewCatalogPriceRule.conditionsTab}}" after="discardSubsequentRules" stepKey="openConditionsTab"/> + <waitForPageLoad after="openConditionsTab" stepKey="waitForConditionTabOpened"/> + <click selector="{{AdminNewCatalogPriceRuleConditions.newCondition}}" after="waitForConditionTabOpened" stepKey="addNewCondition"/> + <selectOption selector="{{AdminNewCatalogPriceRuleConditions.conditionSelect('1')}}" userInput="Magento\CatalogRule\Model\Rule\Condition\Product|category_ids" after="addNewCondition" stepKey="selectTypeCondition"/> + <waitForPageLoad after="selectTypeCondition" stepKey="waitForConditionChosed"/> + <click selector="{{AdminNewCatalogPriceRuleConditions.targetEllipsis('1')}}" after="waitForConditionChosed" stepKey="clickEllipsis"/> + <fillField selector="{{AdminNewCatalogPriceRuleConditions.targetInput('1', '1')}}" userInput="{{categoryId}}" after="clickEllipsis" stepKey="fillCategoryId"/> + <click selector="{{AdminNewCatalogPriceRuleConditions.applyButton('1', '1')}}" after="fillCategoryId" stepKey="clickApply"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/NewCatalogPriceRuleByUIWithConditionIsSKUActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/NewCatalogPriceRuleByUIWithConditionIsSKUActionGroup.xml new file mode 100644 index 0000000000000..9d25ffa948ad1 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/NewCatalogPriceRuleByUIWithConditionIsSKUActionGroup.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="NewCatalogPriceRuleByUIWithConditionIsSKUActionGroup" extends="NewCatalogPriceRuleByUIActionGroup"> + <annotations> + <description>EXTENDS: newCatalogPriceRuleByUI. Add a Catalog Price Rule Condition based on the provided SKU.</description> + </annotations> + <arguments> + <argument name="productSku"/> + </arguments> + + <click selector="{{AdminNewCatalogPriceRule.conditionsTab}}" after="discardSubsequentRules" stepKey="openConditionsTab"/> + <waitForPageLoad after="openConditionsTab" stepKey="waitForConditionTabOpened"/> + <click selector="{{AdminNewCatalogPriceRuleConditions.newCondition}}" after="waitForConditionTabOpened" stepKey="addNewCondition"/> + <selectOption selector="{{AdminNewCatalogPriceRuleConditions.conditionSelect('1')}}" userInput="Magento\CatalogRule\Model\Rule\Condition\Product|sku" after="addNewCondition" stepKey="selectTypeCondition"/> + <waitForPageLoad after="selectTypeCondition" stepKey="waitForConditionChosed"/> + <click selector="{{AdminNewCatalogPriceRuleConditions.targetEllipsis('1')}}" after="waitForConditionChosed" stepKey="clickEllipsis"/> + <fillField selector="{{AdminNewCatalogPriceRuleConditions.targetInput('1', '1')}}" userInput="{{productSku}}" after="clickEllipsis" stepKey="fillProductSku"/> + <click selector="{{AdminNewCatalogPriceRuleConditions.applyButton('1', '1')}}" after="fillProductSku" stepKey="clickApply"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/NewCatalogPriceRuleWithInvalidDataActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/NewCatalogPriceRuleWithInvalidDataActionGroup.xml new file mode 100644 index 0000000000000..5fd56c81734b6 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/NewCatalogPriceRuleWithInvalidDataActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="NewCatalogPriceRuleWithInvalidDataActionGroup"> + <annotations> + <description>Goes to the Catalog Price Rule grid. Clicks on Add. Fills in the provided Catalog Rule details with invalid data.</description> + </annotations> + <arguments> + <argument name="catalogRule" defaultValue="catalogRuleWithInvalid"/> + </arguments> + + <!-- Go to the admin Catalog rule grid and add a new one --> + <amOnPage stepKey="goToPriceRulePage" url="{{CatalogRulePage.url}}"/> + <waitForPageLoad stepKey="waitForPriceRulePage"/> + + <click stepKey="addNewRule" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillPriority" selector="{{AdminNewCatalogPriceRule.priority}}" userInput="{{catalogRule.priority}}"/> + <scrollToTopOfPage stepKey="scrollToTop"/> + <click selector="{{AdminNewCatalogPriceRule.save}}" stepKey="clickSave"/> + <waitForPageLoad stepKey="waitForApplied"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/OpenCatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/OpenCatalogPriceRuleActionGroup.xml new file mode 100644 index 0000000000000..c810d9579df92 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/OpenCatalogPriceRuleActionGroup.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="OpenCatalogPriceRuleActionGroup"> + <arguments> + <argument name="ruleName" type="string" defaultValue="CustomCatalogRule.name"/> + </arguments> + <amOnPage url="{{AdminCatalogPriceRuleGridPage.url}}" stepKey="goToAdminCatalogPriceRuleGridPage"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> + <fillField selector="{{AdminCatalogPriceRuleGridSection.filterByRuleName}}" userInput="{{ruleName}}" stepKey="filterByRuleName"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearch"/> + <click selector="{{AdminGridTableSection.row('1')}}" stepKey="clickEdit"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/RemoveCatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/RemoveCatalogPriceRuleActionGroup.xml new file mode 100644 index 0000000000000..7c7ecba3ff74f --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/RemoveCatalogPriceRuleActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="RemoveCatalogPriceRuleActionGroup" extends="OpenCatalogPriceRuleActionGroup"> + <click selector="{{AdminMainActionsSection.delete}}" after="waitForPageLoad" stepKey="clickToDelete"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" after="clickToDelete" stepKey="waitForElementVisible"/> + <click selector="{{AdminConfirmationModalSection.ok}}" after="waitForElementVisible" stepKey="clickToConfirm"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" after="clickToConfirm" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the rule." after="waitForSuccessMessage" stepKey="verifyRuleIsDeleted"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/SelectGeneralCustomerGroupActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/SelectGeneralCustomerGroupActionGroup.xml new file mode 100644 index 0000000000000..cc4981f73f3f8 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/SelectGeneralCustomerGroupActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SelectGeneralCustomerGroupActionGroup"> + <annotations> + <description>Selects the 'General' Customer Group for a Catalog Price Rule on the creation/edit page.</description> + </annotations> + + <selectOption selector="{{AdminNewCatalogPriceRule.customerGroups}}" userInput="General" stepKey="selectCustomerGroup"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml b/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml index 75a7484324576..2920a895f607d 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleData.xml @@ -173,4 +173,19 @@ <data key="defaultRuleLabelAllStoreViews">Free Shipping in conditions</data> <data key="defaultStoreView">Free Shipping in conditions</data> </entity> + + <entity name="catalogRuleWithInvalid" type="catalogRule"> + <data key="name" unique="suffix">CatalogPriceRule</data> + <data key="description">Catalog Price Rule Description</data> + <data key="is_active">1</data> + <array key="customer_group_ids"> + <item>0</item> + </array> + <array key="website_ids"> + <item>1</item> + </array> + <data key="simple_action">by_percent</data> + <data key="discount_amount">10</data> + <data key="priority">ten</data> + </entity> </entities> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleProductConditionData.xml b/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleProductConditionData.xml new file mode 100644 index 0000000000000..510ea25c3f566 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Data/CatalogRuleProductConditionData.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="CatalogRuleProductConditions"> + <data key="categoryIds">Magento\CatalogRule\Model\Rule\Condition\Product|category_ids</data> + </entity> +</entities> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Metadata/catalog-rule-meta.xml b/app/code/Magento/CatalogRule/Test/Mftf/Metadata/catalog-rule-meta.xml index 0d89c7970b852..5b6b610508fef 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Metadata/catalog-rule-meta.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Metadata/catalog-rule-meta.xml @@ -9,8 +9,9 @@ <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> - <operation name="createCatalogRule" dataType="catalogRule" - type="create" auth="adminFormKey" url="/catalog_rule/promo_catalog/save/" method="POST"> + <operation name="createCatalogRule" dataType="catalogRule" type="create" auth="adminFormKey" + url="/catalog_rule/promo_catalog/save/back/edit" method="POST" + returnRegex="~\/id\/(?'id'\d+)\/~" returnIndex="id" successRegex="/messages-message-success/"> <contentType>application/x-www-form-urlencoded</contentType> <field key="name">string</field> <field key="description">string</field> @@ -24,4 +25,7 @@ <field key="simple_action">string</field> <field key="discount_amount">string</field> </operation> + <operation name="DeleteCatalogRule" dataType="catalogRule" type="delete" auth="adminFormKey" + url="/catalog_rule/promo_catalog/delete/id/{return}" method="POST" successRegex="/messages-message-success/"> + </operation> </operations> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Page/AdminNewCatalogPriceRulePage.xml b/app/code/Magento/CatalogRule/Test/Mftf/Page/AdminNewCatalogPriceRulePage.xml index fded4f5e5f322..71372481490e8 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Page/AdminNewCatalogPriceRulePage.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Page/AdminNewCatalogPriceRulePage.xml @@ -10,5 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="AdminNewCatalogPriceRulePage" url="catalog_rule/promo_catalog/new/" module="Magento_CatalogRule" area="admin"> <section name="AdminNewCatalogPriceRule"/> + <section name="AdminNewCatalogPriceRuleConditions"/> </page> </pages> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleStagingSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleStagingSection.xml deleted file mode 100644 index 7a92829e2371e..0000000000000 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleStagingSection.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="AdminCatalogPriceRuleStagingSection"> - <element name="status" type="select" selector=".modal-component [data-index='is_active'] select"/> - <element name="isActive" type="select" selector=".modals-wrapper input[name='is_active']+label"/> - </section> -</sections> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml index c736dd8dde2cb..7d375da6dfb65 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml @@ -37,6 +37,8 @@ <element name="priority" type="input" selector="[name='sort_order']"/> <element name="conditionsTab" type="block" selector="[data-index='block_promo_catalog_edit_tab_conditions']"/> <element name="actionsTab" type="block" selector="[data-index='actions']"/> + + <element name="fieldError" type="text" selector="//input[@name='{{fieldName}}']/following-sibling::label[@class='admin__field-error']" parameterized="true"/> </section> <section name="AdminNewCatalogPriceRuleActions"> @@ -46,9 +48,9 @@ </section> <section name="AdminNewCatalogPriceRuleConditions"> - <element name="newCondition" type="button" selector=".rule-param.rule-param-new-child"/> + <element name="newCondition" type="button" selector=".rule-param.rule-param-new-child" timeout="30"/> <element name="conditionsDropdown" type="select" selector="select[data-form-part='catalog_rule_form'][data-ui-id='newchild-0-select-rule-conditions-1-new-child']"/> - <element name="conditionSelect" type="select" selector="select#conditions__{{var}}__new_child" parameterized="true"/> + <element name="conditionSelect" type="select" selector="select#conditions__{{var}}__new_child" parameterized="true" timeout="30"/> <element name="targetEllipsis" type="button" selector="//li[{{var}}]//a[@class='label'][text() = '...']" parameterized="true"/> <element name="targetEllipsisValue" type="button" selector="//ul[@id='conditions__{{var}}__children']//a[contains(text(), '{{var1}}')]" parameterized="true" timeout="30"/> <element name="ellipsisValue" type="button" selector="//ul[@id='conditions__{{var}}__children']//a[contains(text(), '...')]" parameterized="true" timeout="30"/> @@ -56,6 +58,10 @@ <element name="targetSelect" type="select" selector="//ul[@id='conditions__{{var}}__children']//select" parameterized="true" timeout="30"/> <element name="targetInput" type="input" selector="input#conditions__{{var1}}--{{var2}}__value" parameterized="true"/> <element name="applyButton" type="button" selector="#conditions__{{var1}}__children li:nth-of-type({{var2}}) a.rule-param-apply" parameterized="true"/> + <element name="condition" type="text" selector="//span[@class='rule-param']/a[text()='{{condition}}']" parameterized="true"/> + <element name="activeOperatorSelect" type="select" selector=".rule-param-edit select[name*='[operator]']"/> + <element name="activeValueInput" type="input" selector=".rule-param-edit [name*='[value]']"/> + <element name="activeConditionApplyButton" type="button" selector=".rule-param-edit .rule-param-apply" timeout="30"/> </section> <section name="AdminCatalogPriceRuleGrid"> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml index ca534ec7f5375..d7d7da58c27fc 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleByCategoryTest.xml @@ -74,7 +74,7 @@ <selectOption selector="{{AdminNewCatalogPriceRuleActions.disregardRules}}" userInput="Yes" stepKey="discardSubsequentRules"/> <scrollToTopOfPage stepKey="scrollToTop"/> <waitForPageLoad stepKey="waitForApplied"/> - <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> <!-- 3. Save and apply the new catalog price rule --> <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApply"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithSpecialPricesTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithSpecialPricesTest.xml index 4df08fcca696b..d99ebac94fbec 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithSpecialPricesTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithSpecialPricesTest.xml @@ -95,29 +95,29 @@ </after> <!-- Add special prices for products --> - <actionGroup ref="goToProductPageViaID" stepKey="goToFirstChildProduct"> + <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToFirstChildProduct"> <argument name="productId" value="$$createFirstConfigChildProduct.id$$"/> </actionGroup> <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="addSpecialPriceForFirstProduct"> <argument name="price" value="{{specialProductPrice.price}}"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveFirstProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveFirstProduct"/> - <actionGroup ref="goToProductPageViaID" stepKey="goToSecondChildProduct"> + <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToSecondChildProduct"> <argument name="productId" value="$$createSecondConfigChildProduct.id$$"/> </actionGroup> <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="addSpecialPriceForSecondProduct"> <argument name="price" value="{{specialProductPrice.price}}"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveSecondProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSecondProduct"/> <!-- Create a new catalog price rule --> - <actionGroup ref="newCatalogPriceRuleByUIWithConditionIsCategory" stepKey="newCatalogPriceRuleByUIWithConditionIsCategory"> + <actionGroup ref="NewCatalogPriceRuleByUIWithConditionIsCategoryActionGroup" stepKey="newCatalogPriceRuleByUIWithConditionIsCategory"> <argument name="categoryId" value="$createCategory.id$"/> </actionGroup> <!-- Select not logged in customer group --> - <actionGroup ref="selectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> <!-- Save and apply the new catalog price rule --> <actionGroup ref="SaveAndApplyCatalogPriceRuleActionGroup" stepKey="saveAndApplyCatalogPriceRule"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml index 09b924603c54a..10689b415b291 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminCreateCatalogPriceRuleTest.xml @@ -31,8 +31,8 @@ <!-- log in and create the price rule --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"/> - <actionGroup stepKey="selectNotLoggedInCustomerGroup" ref="selectNotLoggedInCustomerGroup"/> + <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"/> + <actionGroup stepKey="selectNotLoggedInCustomerGroup" ref="SelectNotLoggedInCustomerGroupActionGroup"/> <click stepKey="saveAndApply" selector="{{AdminNewCatalogPriceRule.saveAndApply}}"/> <see stepKey="assertSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule."/> </before> @@ -80,7 +80,7 @@ <group value="CatalogRule"/> </annotations> <before> - <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"> + <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"> <argument name="catalogRule" value="CatalogRuleByFixed"/> </actionGroup> </before> @@ -103,7 +103,7 @@ <group value="CatalogRule"/> </annotations> <before> - <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"> + <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"> <argument name="catalogRule" value="CatalogRuleToPercent"/> </actionGroup> </before> @@ -126,7 +126,7 @@ <group value="CatalogRule"/> </annotations> <before> - <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"> + <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"> <argument name="catalogRule" value="CatalogRuleToFixed"/> </actionGroup> </before> @@ -172,8 +172,8 @@ </after> <!-- Create a catalog rule for the NOT LOGGED IN customer group --> - <actionGroup ref="newCatalogPriceRuleByUI" stepKey="createNewPriceRule"/> - <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <actionGroup ref="NewCatalogPriceRuleByUIActionGroup" stepKey="createNewPriceRule"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApply"/> <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="assertSuccess"/> @@ -196,4 +196,27 @@ <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$$createProduct.name$$" stepKey="seeProduct2"/> <see selector="{{StorefrontCategoryProductSection.ProductInfoByNumber('1')}}" userInput="$123.00" stepKey="seeDiscountedPrice2"/> </test> + + <test name="AdminCreateCatalogPriceRuleWithInvalidDataTest"> + <annotations> + <features value="CatalogRule"/> + <stories value="Create Catalog Price Rule"/> + <title value="Admin can not create catalog price rule with the invalid data"/> + <description value="Admin can not create catalog price rule with the invalid data"/> + <severity value="MAJOR"/> + <group value="CatalogRule"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + </after> + + <actionGroup ref="NewCatalogPriceRuleWithInvalidDataActionGroup" stepKey="createNewPriceRule"> + <argument name="catalogRule" value="catalogRuleWithInvalid"/> + </actionGroup> + + <see selector="{{AdminNewCatalogPriceRule.fieldError('sort_order')}}" userInput="Please enter a valid number in this field." stepKey="seeSortOrderError"/> + </test> </tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest.xml index ea5c2c33a0a39..02d998870fb65 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest.xml @@ -18,7 +18,7 @@ <group value="CatalogRule"/> <group value="mtf_migrated"/> </annotations> - + <before> <createData entity="Simple_US_Customer" stepKey="createCustomer1"/> <createData entity="_defaultCategory" stepKey="createCategory1"/> @@ -31,7 +31,7 @@ <amOnPage url="{{AdminNewCatalogPriceRulePage.url}}" stepKey="openNewCatalogPriceRulePage"/> <waitForPageLoad stepKey="waitForPageToLoad1"/> - <actionGroup ref="CreateCatalogPriceRuleViaTheUi" stepKey="createCatalogPriceRuleViaTheUi1"> + <actionGroup ref="CreateCatalogPriceRuleViaTheUiActionGroup" stepKey="createCatalogPriceRuleViaTheUi1"> <argument name="catalogRule" value="DeleteActiveCatalogPriceRuleWithConditions"/> <argument name="customerGroup" value="General"/> <argument name="disregardRules" value="Yes"/> @@ -59,7 +59,7 @@ <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> <!-- Assert that the Success message is present after the delete --> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="You deleted the rule." stepKey="seeDeletedRuleMessage1"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the rule." stepKey="seeDeletedRuleMessage1"/> <!-- Reindex --> <magentoCLI command="cache:flush" stepKey="flushCache1"/> @@ -78,7 +78,7 @@ <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createProduct1.price$$" stepKey="seeTrueProductPrice1"/> <!-- Assert that the rule isn't present in the Shopping Cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProductToShoppingCart1"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProductToShoppingCart1"> <argument name="productName" value="$$createProduct1.name$$"/> </actionGroup> <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniShoppingCart1"/> @@ -163,7 +163,7 @@ <amOnPage url="{{AdminNewCatalogPriceRulePage.url}}" stepKey="openNewCatalogPriceRulePage"/> <waitForPageLoad stepKey="waitForPageToLoad1"/> - <actionGroup ref="CreateCatalogPriceRuleViaTheUi" stepKey="createCatalogPriceRuleViaTheUi1"> + <actionGroup ref="CreateCatalogPriceRuleViaTheUiActionGroup" stepKey="createCatalogPriceRuleViaTheUi1"> <argument name="catalogRule" value="DeleteActiveCatalogPriceRuleWithConditions"/> <argument name="customerGroup" value="General"/> <argument name="disregardRules" value="Yes"/> @@ -192,7 +192,7 @@ <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> </actionGroup> <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="You deleted the rule." stepKey="seeDeletedRuleMessage1"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the rule." stepKey="seeDeletedRuleMessage1"/> <!-- Reindex --> <magentoCLI command="cache:flush" stepKey="flushCache1"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml index d3546d06492be..a6eb6eb1e5178 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleTest.xml @@ -19,29 +19,36 @@ <group value="CatalogRule"/> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- Create a category --> <createData entity="ApiCategory" stepKey="createCategory"/> - <!-- Create a simple product --> <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - + <!-- Login to Admin page --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!-- Create a configurable product --> - <actionGroup ref="createConfigurableProduct" stepKey="createConfigurableProduct"> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createConfigurableProduct"> <argument name="product" value="_defaultProduct"/> <argument name="category" value="$$createCategory$$"/> </actionGroup> </before> <after> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteConfigurableProduct"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <amOnPage url="{{AdminCatalogPriceRuleGridPage.url}}" stepKey="goToCatalogRuleGridPage"/> + <waitForPageLoad stepKey="waitForCatalogRuleGridPageLoaded"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearCatalogRuleGridFilters"/> + <actionGroup ref="logout" stepKey="amOnLogoutPage"/> </after> <!-- Create a catalog price rule --> - <actionGroup ref="newCatalogPriceRuleByUI" stepKey="createNewPriceRule"/> - <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <actionGroup ref="NewCatalogPriceRuleByUIActionGroup" stepKey="createNewPriceRule"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApply"/> <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="assertSuccess"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminEnableAttributeIsUndefinedCatalogPriceRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminEnableAttributeIsUndefinedCatalogPriceRuleTest.xml index 54bf243c4cde6..da855b08cf390 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminEnableAttributeIsUndefinedCatalogPriceRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminEnableAttributeIsUndefinedCatalogPriceRuleTest.xml @@ -70,11 +70,11 @@ <!--Create catalog price rule--> <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToPriceRulePage"/> <waitForPageLoad stepKey="waitForPriceRulePage"/> - <actionGroup ref="createCatalogPriceRule" stepKey="createCatalogPriceRule"> + <actionGroup ref="CreateCatalogPriceRuleActionGroup" stepKey="createCatalogPriceRule"> <argument name="catalogRule" value="CatalogRuleWithAllCustomerGroups"/> </actionGroup> - <actionGroup ref="selectNotLoggedInCustomerGroupActionGroup" stepKey="selectCustomerGroup"/> - <actionGroup ref="CreateCatalogPriceRuleConditionWithAttribute" stepKey="createCatalogPriceRuleCondition"> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectCustomerGroup"/> + <actionGroup ref="CreateCatalogPriceRuleConditionWithAttributeActionGroup" stepKey="createCatalogPriceRuleCondition"> <argument name="attributeName" value="$$createProductAttribute.attribute[frontend_labels][0][label]$$"/> <argument name="targetValue" value="is"/> <argument name="targetSelectValue" value="is undefined"/> @@ -113,11 +113,11 @@ <!--Create new Catalog Price Rule--> <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToPriceRulePage1"/> <waitForPageLoad stepKey="waitForPriceRulePage1"/> - <actionGroup ref="createCatalogPriceRule" stepKey="createCatalogPriceRule1"> + <actionGroup ref="CreateCatalogPriceRuleActionGroup" stepKey="createCatalogPriceRule1"> <argument name="catalogRule" value="CatalogRuleWithAllCustomerGroups"/> </actionGroup> - <actionGroup ref="selectNotLoggedInCustomerGroupActionGroup" stepKey="selectCustomerGroup1"/> - <actionGroup ref="CreateCatalogPriceRuleConditionWithAttribute" stepKey="createCatalogPriceRuleCondition1"> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectCustomerGroup1"/> + <actionGroup ref="CreateCatalogPriceRuleConditionWithAttributeActionGroup" stepKey="createCatalogPriceRuleCondition1"> <argument name="attributeName" value="$$createSecondProductAttribute.attribute[frontend_labels][0][label]$$"/> <argument name="targetValue" value="is"/> <argument name="targetSelectValue" value="is undefined"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogPriceRuleByProductAttributeTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogPriceRuleByProductAttributeTest.xml index dd54c0767e8e1..1615985370182 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogPriceRuleByProductAttributeTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogPriceRuleByProductAttributeTest.xml @@ -16,6 +16,9 @@ <severity value="CRITICAL"/> <testCaseId value="MC-148"/> <group value="CatalogRule"/> + <skip> + <issueId value="MC-22577"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="login"/> @@ -137,49 +140,49 @@ <selectOption selector="{{StorefrontPropertiesSection.useForPromoRuleConditions}}" userInput="Yes" stepKey="selectYes"/> <!-- Save the attribute --> - <actionGroup ref="saveProductAttribute" stepKey="saveAttribute"/> + <actionGroup ref="SaveProductAttributeActionGroup" stepKey="saveAttribute"/> <!-- Add this product attribute to Default attribute set --> <actionGroup ref="AdminOpenAttributeSetGridPageActionGroup" stepKey="openAttributeSetPage"/> <actionGroup ref="AdminOpenAttributeSetByNameActionGroup" stepKey="openDefaultAttributeSet"/> - <actionGroup ref="AssignAttributeToGroup" stepKey="assignAttributeToGroup"> + <actionGroup ref="AssignAttributeToGroupActionGroup" stepKey="assignAttributeToGroup"> <argument name="group" value="Product Details"/> <argument name="attribute" value="{{productAttributeDropdownTwoOptions.attribute_code}}"/> </actionGroup> - <actionGroup ref="SaveAttributeSet" stepKey="saveAttributeSet"/> - + <actionGroup ref="SaveAttributeSetActionGroup" stepKey="saveAttributeSet"/> + <!-- First Simple Product: choose green as attribute value --> - <actionGroup ref="filterAndSelectProduct" stepKey="openFirstSimpleProduct"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="openFirstSimpleProduct"> <argument name="productSku" value="$$createFirstProduct.sku$$"/> </actionGroup> <selectOption userInput="green" selector="{{AdminProductFormSection.customSelectField(productAttributeDropdownTwoOptions.attribute_code)}}" stepKey="selectGreen"/> - <actionGroup ref="saveProductForm" stepKey="saveFirstProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveFirstProduct"/> <!-- Second Simple Product: choose red as attribute value --> - <actionGroup ref="filterAndSelectProduct" stepKey="openSecondSimpleProduct"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="openSecondSimpleProduct"> <argument name="productSku" value="$$createSecondProduct.sku$$"/> </actionGroup> <selectOption userInput="red" selector="{{AdminProductFormSection.customSelectField(productAttributeDropdownTwoOptions.attribute_code)}}" stepKey="selectRed"/> - <actionGroup ref="saveProductForm" stepKey="saveSecondProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSecondProduct"/> <!-- Configurable child product1: choose green as attribute value --> - <actionGroup ref="filterAndSelectProduct" stepKey="openConfigChild1Product"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="openConfigChild1Product"> <argument name="productSku" value="$$createConfigChildProduct1.sku$$"/> </actionGroup> <selectOption userInput="green" selector="{{AdminProductFormSection.customSelectField(productAttributeDropdownTwoOptions.attribute_code)}}" stepKey="selectGreenAttr"/> - <actionGroup ref="saveProductForm" stepKey="saveConfigChild1Product"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigChild1Product"/> <!-- Configurable child product2: choose green as attribute value --> - <actionGroup ref="filterAndSelectProduct" stepKey="openConfigChild2Product"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="openConfigChild2Product"> <argument name="productSku" value="$$createConfigChildProduct2.sku$$"/> </actionGroup> <selectOption userInput="green" selector="{{AdminProductFormSection.customSelectField(productAttributeDropdownTwoOptions.attribute_code)}}" stepKey="selectGreenAttribute"/> - <actionGroup ref="saveProductForm" stepKey="saveConfigChild2Product"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigChild2Product"/> <!-- Navigate to Marketing - Promotions - Catalog Price Rules --> <amOnPage url="{{AdminCatalogPriceRuleGridPage.url}}" stepKey="amOnCatalogPriceRule"/> @@ -229,6 +232,7 @@ <!-- Run cron twice --> <magentoCLI command="cron:run" stepKey="runCron1"/> <magentoCLI command="cron:run" stepKey="runCron2"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> <!-- Go to Frontend and open the simple product --> <amOnPage url="{{StorefrontProductPage.url($$createFirstProduct.sku$$)}}" stepKey="amOnSimpleProductPage"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml index b9e7bfb4d41e4..2f12df8b75fc6 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml @@ -101,12 +101,12 @@ <deleteData createDataKey="createCategory" stepKey="deleteApiCategory"/> </after> <!-- Begin creating a new catalog price rule --> - <actionGroup ref="newCatalogPriceRuleByUIWithConditionIsCategory" stepKey="newCatalogPriceRuleByUIWithConditionIsCategory"> + <actionGroup ref="NewCatalogPriceRuleByUIWithConditionIsCategoryActionGroup" stepKey="newCatalogPriceRuleByUIWithConditionIsCategory"> <argument name ="categoryId" value="$createCategory.id$"/> </actionGroup> <!-- Select not logged in customer group --> - <actionGroup ref="selectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> <!-- Save and apply the new catalog price rule --> <actionGroup ref="SaveAndApplyCatalogPriceRuleActionGroup" stepKey="saveAndApplyCatalogPriceRule"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductAndFixedMethodTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductAndFixedMethodTest.xml index 3405d0c4e776d..956a2e07fc6b4 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductAndFixedMethodTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductAndFixedMethodTest.xml @@ -53,22 +53,22 @@ <actionGroup ref="logout" stepKey="logout"/> </after> <!-- 1. Begin creating a new catalog price rule --> - <actionGroup ref="newCatalogPriceRuleByUIWithConditionIsCategory" stepKey="newCatalogPriceRuleByUIWithConditionIsCategory"> + <actionGroup ref="NewCatalogPriceRuleByUIWithConditionIsCategoryActionGroup" stepKey="newCatalogPriceRuleByUIWithConditionIsCategory"> <argument name ="categoryId" value="$createCategory.id$"/> <argument name ="catalogRule" value="CatalogRuleByFixed"/> </actionGroup> <!-- Select not logged in customer group --> - <actionGroup ref="selectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> <!-- Save and apply the new catalog price rule --> <actionGroup ref="SaveAndApplyCatalogPriceRuleActionGroup" stepKey="saveAndApplyCatalogPriceRule"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> - + <!-- Navigate to category on store front --> <amOnPage url="{{StorefrontProductPage.url($createCategory.name$)}}" stepKey="goToCategoryPage"/> - + <!-- Check product 1 name on store front category page --> <actionGroup ref="AssertProductDetailsOnStorefrontActionGroup" stepKey="storefrontProduct1Name"> <argument name="productInfo" value="$createProduct1.name$"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductForNewCustomerGroupTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductForNewCustomerGroupTest.xml index c3bcde92bd1f2..e5e3cf9ead5e5 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductForNewCustomerGroupTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductForNewCustomerGroupTest.xml @@ -64,7 +64,7 @@ <actionGroup ref="logout" stepKey="logout"/> </after> <!-- 1. Begin creating a new catalog price rule --> - <actionGroup ref="newCatalogPriceRuleByUIWithConditionIsCategory" stepKey="newCatalogPriceRuleByUIWithConditionIsCategory"> + <actionGroup ref="NewCatalogPriceRuleByUIWithConditionIsCategoryActionGroup" stepKey="newCatalogPriceRuleByUIWithConditionIsCategory"> <argument name ="categoryId" value="$createCategory.id$"/> <argument name="catalogRule" value="CatalogRuleByFixed"/> </actionGroup> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithCustomOptionsTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithCustomOptionsTest.xml index a251ee1e235d0..166d202ec2410 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithCustomOptionsTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithCustomOptionsTest.xml @@ -66,12 +66,12 @@ <actionGroup ref="logout" stepKey="logout"/> </after> <!-- 1. Begin creating a new catalog price rule --> - <actionGroup ref="newCatalogPriceRuleByUIWithConditionIsCategory" stepKey="newCatalogPriceRuleByUIWithConditionIsCategory"> + <actionGroup ref="NewCatalogPriceRuleByUIWithConditionIsCategoryActionGroup" stepKey="newCatalogPriceRuleByUIWithConditionIsCategory"> <argument name ="categoryId" value="$createCategory.id$"/> </actionGroup> <!-- Select not logged in customer group --> - <actionGroup ref="selectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> <!-- Save and apply the new catalog price rule --> <actionGroup ref="SaveAndApplyCatalogPriceRuleActionGroup" stepKey="saveAndApplyCatalogPriceRule"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml index 738f193fcc511..86d3dccba7595 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml @@ -32,11 +32,11 @@ <actionGroup ref="LoginAsAdmin" stepKey="login"/> <!--Create Catalog Rule--> - <actionGroup ref="newCatalogPriceRuleByUIWithConditionIsCategory" stepKey="createCatalogPriceRule"> + <actionGroup ref="NewCatalogPriceRuleByUIWithConditionIsCategoryActionGroup" stepKey="createCatalogPriceRule"> <argument name="catalogRule" value="_defaultCatalogRule"/> <argument name="categoryId" value="$$createCategory.id$$"/> </actionGroup> - <actionGroup ref="selectGeneralCustomerGroupActionGroup" stepKey="selectCustomerGroup"/> + <actionGroup ref="SelectGeneralCustomerGroupActionGroup" stepKey="selectCustomerGroup"/> <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="clickSaveAndApplyRules"/> <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="assertSuccess"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml index 08e59c6316411..a4fc6757ae6cb 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml @@ -24,8 +24,8 @@ <createData entity="ApiSimpleProduct" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup stepKey="createNewPriceRule" ref="newCatalogPriceRuleByUI"/> - <actionGroup stepKey="selectLoggedInCustomers" ref="selectNotLoggedInCustomerGroup"/> + <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"/> + <actionGroup stepKey="selectLoggedInCustomers" ref="SelectNotLoggedInCustomerGroupActionGroup"/> <scrollToTopOfPage stepKey="scrollToTop"/> <click stepKey="setInactive" selector="{{AdminCategoryBasicFieldSection.enableCategoryLabel}}"/> <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApply"/> diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/IndexBuilderTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/IndexBuilderTest.php deleted file mode 100644 index 78668366bccdc..0000000000000 --- a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/IndexBuilderTest.php +++ /dev/null @@ -1,289 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\CatalogRule\Test\Unit\Model\Indexer; - -use Magento\CatalogRule\Model\Indexer\IndexBuilder\ProductLoader; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - * @SuppressWarnings(PHPMD.TooManyFields) - */ -class IndexBuilderTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\CatalogRule\Model\Indexer\IndexBuilder - */ - protected $indexBuilder; - - /** - * @var \Magento\Framework\App\ResourceConnection|\PHPUnit_Framework_MockObject_MockObject - */ - protected $resource; - - /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $storeManager; - - /** - * @var \Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject - */ - protected $ruleCollectionFactory; - - /** - * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $logger; - - /** - * @var \Magento\Framework\Pricing\PriceCurrencyInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $priceCurrency; - - /** - * @var \Magento\Eav\Model\Config|\PHPUnit_Framework_MockObject_MockObject - */ - protected $eavConfig; - - /** - * @var \Magento\Framework\Stdlib\DateTime|\PHPUnit_Framework_MockObject_MockObject - */ - protected $dateFormat; - - /** - * @var \Magento\Framework\Stdlib\DateTime\DateTime|\PHPUnit_Framework_MockObject_MockObject - */ - protected $dateTime; - - /** - * @var \Magento\Catalog\Model\ProductFactory|\PHPUnit_Framework_MockObject_MockObject - */ - protected $productFactory; - - /** - * @var \Magento\Framework\DB\Adapter\AdapterInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $connection; - - /** - * @var \Magento\Framework\EntityManager\MetadataPool|\PHPUnit_Framework_MockObject_MockObject - */ - protected $metadataPool; - - /** - * @var \Magento\Framework\DB\Select|\PHPUnit_Framework_MockObject_MockObject - */ - protected $select; - - /** - * @var \Zend_Db_Statement_Interface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $db; - - /** - * @var \Magento\Store\Model\Website|\PHPUnit_Framework_MockObject_MockObject - */ - protected $website; - - /** - * @var \Magento\Rule\Model\Condition\Combine|\PHPUnit_Framework_MockObject_MockObject - */ - protected $combine; - - /** - * @var \Magento\CatalogRule\Model\Rule|\PHPUnit_Framework_MockObject_MockObject - */ - protected $rules; - - /** - * @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject - */ - protected $product; - - /** - * @var \Magento\Eav\Model\Entity\Attribute\AbstractAttribute|\PHPUnit_Framework_MockObject_MockObject - */ - protected $attribute; - - /** - * @var \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend|\PHPUnit_Framework_MockObject_MockObject - */ - protected $backend; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $reindexRuleProductPrice; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $reindexRuleGroupWebsite; - - /** - * @var ProductLoader|\PHPUnit_Framework_MockObject_MockObject - */ - private $productLoader; - - /** - * Set up test - * - * @return void - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - protected function setUp() - { - $this->resource = $this->createPartialMock( - \Magento\Framework\App\ResourceConnection::class, - ['getConnection', 'getTableName'] - ); - $this->ruleCollectionFactory = $this->createPartialMock( - \Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory::class, - ['create'] - ); - $this->backend = $this->createMock(\Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend::class); - $this->select = $this->createMock(\Magento\Framework\DB\Select::class); - $this->metadataPool = $this->createMock(\Magento\Framework\EntityManager\MetadataPool::class); - $metadata = $this->createMock(\Magento\Framework\EntityManager\EntityMetadata::class); - $this->metadataPool->expects($this->any())->method('getMetadata')->willReturn($metadata); - $this->connection = $this->createMock(\Magento\Framework\DB\Adapter\AdapterInterface::class); - $this->db = $this->createMock(\Zend_Db_Statement_Interface::class); - $this->website = $this->createMock(\Magento\Store\Model\Website::class); - $this->storeManager = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); - $this->combine = $this->createMock(\Magento\Rule\Model\Condition\Combine::class); - $this->rules = $this->createMock(\Magento\CatalogRule\Model\Rule::class); - $this->logger = $this->createMock(\Psr\Log\LoggerInterface::class); - $this->attribute = $this->createMock(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class); - $this->priceCurrency = $this->createMock(\Magento\Framework\Pricing\PriceCurrencyInterface::class); - $this->dateFormat = $this->createMock(\Magento\Framework\Stdlib\DateTime::class); - $this->dateTime = $this->createMock(\Magento\Framework\Stdlib\DateTime\DateTime::class); - $this->eavConfig = $this->createPartialMock(\Magento\Eav\Model\Config::class, ['getAttribute']); - $this->product = $this->createMock(\Magento\Catalog\Model\Product::class); - $this->productFactory = $this->createPartialMock(\Magento\Catalog\Model\ProductFactory::class, ['create']); - $this->connection->expects($this->any())->method('select')->will($this->returnValue($this->select)); - $this->connection->expects($this->any())->method('query')->will($this->returnValue($this->db)); - $this->select->expects($this->any())->method('distinct')->will($this->returnSelf()); - $this->select->expects($this->any())->method('where')->will($this->returnSelf()); - $this->select->expects($this->any())->method('from')->will($this->returnSelf()); - $this->select->expects($this->any())->method('order')->will($this->returnSelf()); - $this->resource->expects($this->any())->method('getConnection')->will($this->returnValue($this->connection)); - $this->resource->expects($this->any())->method('getTableName')->will($this->returnArgument(0)); - $this->storeManager->expects($this->any())->method('getWebsites')->will($this->returnValue([$this->website])); - $this->storeManager->expects($this->any())->method('getWebsite')->will($this->returnValue($this->website)); - $this->rules->expects($this->any())->method('getId')->will($this->returnValue(1)); - $this->rules->expects($this->any())->method('getWebsiteIds')->will($this->returnValue([1])); - $this->rules->expects($this->any())->method('getCustomerGroupIds')->will($this->returnValue([1])); - - $ruleCollection = $this->createMock(\Magento\CatalogRule\Model\ResourceModel\Rule\Collection::class); - $this->ruleCollectionFactory->expects($this->once()) - ->method('create') - ->willReturn($ruleCollection); - $ruleCollection->expects($this->once()) - ->method('addFieldToFilter') - ->willReturnSelf(); - $ruleIterator = new \ArrayIterator([$this->rules]); - $ruleCollection->method('getIterator') - ->willReturn($ruleIterator); - - $this->product->expects($this->any())->method('load')->will($this->returnSelf()); - $this->product->expects($this->any())->method('getId')->will($this->returnValue(1)); - $this->product->expects($this->any())->method('getWebsiteIds')->will($this->returnValue([1])); - - $this->rules->expects($this->any())->method('validate')->with($this->product)->willReturn(true); - $this->attribute->expects($this->any())->method('getBackend')->will($this->returnValue($this->backend)); - $this->productFactory->expects($this->any())->method('create')->will($this->returnValue($this->product)); - $this->productLoader = $this->getMockBuilder(ProductLoader::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->indexBuilder = (new ObjectManager($this))->getObject( - \Magento\CatalogRule\Model\Indexer\IndexBuilder::class, - [ - 'ruleCollectionFactory' => $this->ruleCollectionFactory, - 'priceCurrency' => $this->priceCurrency, - 'resource' => $this->resource, - 'storeManager' => $this->storeManager, - 'logger' => $this->logger, - 'eavConfig' => $this->eavConfig, - 'dateFormat' => $this->dateFormat, - 'dateTime' => $this->dateTime, - 'productFactory' => $this->productFactory, - 'productLoader' => $this->productLoader, - ] - ); - - $this->reindexRuleProductPrice = $this->createMock( - \Magento\CatalogRule\Model\Indexer\ReindexRuleProductPrice::class - ); - $this->reindexRuleGroupWebsite = $this->createMock( - \Magento\CatalogRule\Model\Indexer\ReindexRuleGroupWebsite::class - ); - $this->setProperties( - $this->indexBuilder, - [ - 'metadataPool' => $this->metadataPool, - 'reindexRuleProductPrice' => $this->reindexRuleProductPrice, - 'reindexRuleGroupWebsite' => $this->reindexRuleGroupWebsite, - ] - ); - } - - /** - * Test UpdateCatalogRuleGroupWebsiteData - * - * @covers \Magento\CatalogRule\Model\Indexer\IndexBuilder::updateCatalogRuleGroupWebsiteData - * @return void - */ - public function testUpdateCatalogRuleGroupWebsiteData() - { - $priceAttrMock = $this->createPartialMock(\Magento\Catalog\Model\Entity\Attribute::class, ['getBackend']); - $backendModelMock = $this->createPartialMock( - \Magento\Catalog\Model\Product\Attribute\Backend\Tierprice::class, - ['getResource'] - ); - $resourceMock = $this->createPartialMock( - \Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice::class, - ['getMainTable'] - ); - $resourceMock->expects($this->any()) - ->method('getMainTable') - ->will($this->returnValue('catalog_product_entity_tier_price')); - $backendModelMock->expects($this->any()) - ->method('getResource') - ->will($this->returnValue($resourceMock)); - $priceAttrMock->expects($this->any()) - ->method('getBackend') - ->will($this->returnValue($backendModelMock)); - - $iterator = [$this->product]; - $this->productLoader->expects($this->once()) - ->method('getProducts') - ->willReturn($iterator); - - $this->reindexRuleProductPrice->expects($this->once())->method('execute')->willReturn(true); - $this->reindexRuleGroupWebsite->expects($this->once())->method('execute')->willReturn(true); - - $this->indexBuilder->reindexByIds([1]); - } - - /** - * @param $object - * @param array $properties - */ - private function setProperties($object, $properties = []) - { - $reflectionClass = new \ReflectionClass(get_class($object)); - foreach ($properties as $key => $value) { - if ($reflectionClass->hasProperty($key)) { - $reflectionProperty = $reflectionClass->getProperty($key); - $reflectionProperty->setAccessible(true); - $reflectionProperty->setValue($object, $value); - } - } - } -} diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductPriceTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductPriceTest.php index 5f63283df6760..d0f266d574945 100644 --- a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductPriceTest.php +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/ReindexRuleProductPriceTest.php @@ -71,6 +71,7 @@ public function testExecute() $websiteId = 234; $defaultGroupId = 11; $defaultStoreId = 22; + $productId = 55; $websiteMock = $this->createMock(WebsiteInterface::class); $websiteMock->expects($this->once()) @@ -93,11 +94,10 @@ public function testExecute() ->with($defaultGroupId) ->willReturn($groupMock); - $productMock = $this->createMock(Product::class); $statementMock = $this->createMock(\Zend_Db_Statement_Interface::class); $this->ruleProductsSelectBuilderMock->expects($this->once()) ->method('build') - ->with($websiteId, $productMock, true) + ->with($websiteId, $productId, true) ->willReturn($statementMock); $ruleData = [ @@ -126,6 +126,6 @@ public function testExecute() $this->pricesPersistorMock->expects($this->once()) ->method('execute'); - $this->assertTrue($this->model->execute(1, $productMock, true)); + $this->assertTrue($this->model->execute(1, $productId, true)); } } diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/RuleProductsSelectBuilderTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/RuleProductsSelectBuilderTest.php deleted file mode 100644 index e43fe41dc2127..0000000000000 --- a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/RuleProductsSelectBuilderTest.php +++ /dev/null @@ -1,200 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\CatalogRule\Test\Unit\Model\Indexer; - -use Magento\Catalog\Model\Product; -use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; -use Magento\CatalogRule\Model\Indexer\IndexerTableSwapperInterface; -use Magento\Framework\DB\Adapter\AdapterInterface; -use Magento\Framework\DB\Select; -use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; -use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend; -use Magento\Framework\EntityManager\EntityMetadataInterface; -use Magento\Store\Api\Data\WebsiteInterface; - -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class RuleProductsSelectBuilderTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder - */ - private $model; - - /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $storeManagerMock; - - /** - * @var \Magento\Framework\App\ResourceConnection|\PHPUnit_Framework_MockObject_MockObject - */ - private $resourceMock; - - /** - * @var ActiveTableSwitcher|\PHPUnit_Framework_MockObject_MockObject - */ - private $activeTableSwitcherMock; - - /** - * @var \Magento\Eav\Model\Config|\PHPUnit_Framework_MockObject_MockObject - */ - private $eavConfigMock; - - /** - * @var \Magento\Framework\EntityManager\MetadataPool|\PHPUnit_Framework_MockObject_MockObject - */ - private $metadataPoolMock; - - /** - * @var IndexerTableSwapperInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $tableSwapperMock; - - protected function setUp() - { - $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) - ->getMockForAbstractClass(); - $this->resourceMock = $this->getMockBuilder(\Magento\Framework\App\ResourceConnection::class) - ->disableOriginalConstructor() - ->getMock(); - $this->activeTableSwitcherMock = $this->getMockBuilder(ActiveTableSwitcher::class) - ->disableOriginalConstructor() - ->getMock(); - $this->eavConfigMock = $this->getMockBuilder(\Magento\Eav\Model\Config::class) - ->disableOriginalConstructor() - ->getMock(); - $this->metadataPoolMock = $this->getMockBuilder(\Magento\Framework\EntityManager\MetadataPool::class) - ->disableOriginalConstructor() - ->getMock(); - $this->tableSwapperMock = $this->getMockForAbstractClass( - IndexerTableSwapperInterface::class - ); - - $this->model = new \Magento\CatalogRule\Model\Indexer\RuleProductsSelectBuilder( - $this->resourceMock, - $this->eavConfigMock, - $this->storeManagerMock, - $this->metadataPoolMock, - $this->activeTableSwitcherMock, - $this->tableSwapperMock - ); - } - - /** - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testBuild() - { - $websiteId = 55; - $ruleTable = 'catalogrule_product'; - $rplTable = 'catalogrule_product_replica'; - $prTable = 'catalog_product_entity'; - $wsTable = 'catalog_product_website'; - $productMock = $this->getMockBuilder(Product::class)->disableOriginalConstructor()->getMock(); - $productMock->expects($this->exactly(2))->method('getEntityId')->willReturn(500); - - $connectionMock = $this->getMockBuilder(AdapterInterface::class)->disableOriginalConstructor()->getMock(); - $this->resourceMock->expects($this->at(0))->method('getConnection')->willReturn($connectionMock); - - $this->tableSwapperMock->expects($this->once()) - ->method('getWorkingTableName') - ->with($ruleTable) - ->willReturn($rplTable); - - $this->resourceMock->expects($this->at(1))->method('getTableName')->with($ruleTable)->willReturn($ruleTable); - $this->resourceMock->expects($this->at(2))->method('getTableName')->with($rplTable)->willReturn($rplTable); - $this->resourceMock->expects($this->at(3))->method('getTableName')->with($prTable)->willReturn($prTable); - $this->resourceMock->expects($this->at(4))->method('getTableName')->with($wsTable)->willReturn($wsTable); - - $selectMock = $this->getMockBuilder(Select::class)->disableOriginalConstructor()->getMock(); - $connectionMock->expects($this->once())->method('select')->willReturn($selectMock); - $selectMock->expects($this->at(0))->method('from')->with(['rp' => $rplTable])->willReturnSelf(); - $selectMock->expects($this->at(1)) - ->method('order') - ->with(['rp.website_id', 'rp.customer_group_id', 'rp.product_id', 'rp.sort_order', 'rp.rule_id']) - ->willReturnSelf(); - $selectMock->expects($this->at(2))->method('where')->with('rp.product_id=?', 500)->willReturnSelf(); - - $attributeMock = $this->getMockBuilder(AbstractAttribute::class)->disableOriginalConstructor()->getMock(); - $this->eavConfigMock->expects($this->once()) - ->method('getAttribute') - ->with(Product::ENTITY, 'price') - ->willReturn($attributeMock); - $backendMock = $this->getMockBuilder(AbstractBackend::class)->disableOriginalConstructor()->getMock(); - $backendMock->expects($this->once())->method('getTable')->willReturn('price_table'); - $attributeMock->expects($this->once())->method('getBackend')->willReturn($backendMock); - $attributeMock->expects($this->once())->method('getId')->willReturn(200); - - $metadataMock = $this->getMockBuilder(EntityMetadataInterface::class)->disableOriginalConstructor()->getMock(); - $this->metadataPoolMock->expects($this->once()) - ->method('getMetadata') - ->with(\Magento\Catalog\Api\Data\ProductInterface::class) - ->willReturn($metadataMock); - $metadataMock->expects($this->once())->method('getLinkField')->willReturn('link_field'); - - $selectMock->expects($this->at(3)) - ->method('join') - ->with(['e' => $prTable], 'e.entity_id = rp.product_id', []) - ->willReturnSelf(); - $selectMock->expects($this->at(4)) - ->method('join') - ->with( - ['pp_default' => 'price_table'], - 'pp_default.link_field=e.link_field AND (pp_default.attribute_id=200) and pp_default.store_id=0', - [] - )->willReturnSelf(); - $websiteMock = $this->getMockBuilder(WebsiteInterface::class) - ->setMethods(['getDefaultGroup']) - ->getMockForAbstractClass(); - $this->storeManagerMock->expects($this->once()) - ->method('getWebsite') - ->with($websiteId) - ->willReturn($websiteMock); - - $groupMock = $this->getMockBuilder(\Magento\Store\Model\Group::class) - ->setMethods(['getDefaultStoreId']) - ->disableOriginalConstructor() - ->getMock(); - $websiteMock->expects($this->once())->method('getDefaultGroup')->willReturn($groupMock); - $groupMock->expects($this->once())->method('getDefaultStoreId')->willReturn(700); - - $selectMock->expects($this->at(5)) - ->method('joinInner') - ->with( - ['product_website' => $wsTable], - 'product_website.product_id=rp.product_id ' - . 'AND product_website.website_id = rp.website_id ' - . 'AND product_website.website_id=' - . $websiteId, - [] - )->willReturnSelf(); - $selectMock->expects($this->at(6)) - ->method('joinLeft') - ->with( - ['pp' . $websiteId => 'price_table'], - 'pp55.link_field=e.link_field AND (pp55.attribute_id=200) and pp55.store_id=700', - [] - )->willReturnSelf(); - - $connectionMock->expects($this->once()) - ->method('getIfNullSql') - ->with('pp55.value', 'pp_default.value') - ->willReturn('IF NULL SQL'); - $selectMock->expects($this->at(7)) - ->method('columns') - ->with(['default_price' => 'IF NULL SQL']) - ->willReturnSelf(); - $statementMock = $this->getMockBuilder(\Zend_Db_Statement_Interface::class) - ->disableOriginalConstructor() - ->getMock(); - $connectionMock->expects($this->once())->method('query')->with($selectMock)->willReturn($statementMock); - - $this->assertEquals($statementMock, $this->model->build($websiteId, $productMock, true)); - } -} diff --git a/app/code/Magento/CatalogRule/Test/Unit/Observer/ProcessAdminFinalPriceObserverTest.php b/app/code/Magento/CatalogRule/Test/Unit/Observer/ProcessAdminFinalPriceObserverTest.php new file mode 100644 index 0000000000000..558fef8660606 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Unit/Observer/ProcessAdminFinalPriceObserverTest.php @@ -0,0 +1,191 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogRule\Test\Unit\Observer; + +use Magento\Catalog\Model\Product; +use Magento\CatalogRule\Model\ResourceModel\RuleFactory; +use Magento\CatalogRule\Observer\ProcessAdminFinalPriceObserver; +use Magento\CatalogRule\Observer\RulePricesStorage; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Ui\Component\Form\Element\DataType\Date; +use PHPUnit\Framework\TestCase; + +/** + * Class ProcessAdminFinalPriceObserverTest + * + * Test class for Observer for applying catalog rules on product for admin area + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ProcessAdminFinalPriceObserverTest extends TestCase +{ + /** + * @var ProcessAdminFinalPriceObserver + */ + private $observer; + + /** + * Store Manager mock + * + * @var StoreManagerInterface + */ + private $storeManagerMock; + + /** + * Locale Date mock + * + * @var TimezoneInterface + */ + private $localeDateMock; + + /** + * Resource Rule Factory mock + * + * @var RuleFactory + */ + private $resourceRuleFactoryMock; + + /** + * Rule Prices Storage mock + * + * @var RulePricesStorage + */ + private $rulePricesStorageMock; + + /** + * @var Event|\PHPUnit_Framework_MockObject_MockObject + */ + private $eventMock; + + /** + * @var Observer|\PHPUnit\Framework\MockObject\MockObject + */ + private $observerMock; + + protected function setUp() + { + $this->observerMock = $this + ->getMockBuilder(Observer::class) + ->disableOriginalConstructor() + ->getMock(); + $this->eventMock = $this + ->getMockBuilder(Event::class) + ->setMethods(['getProduct']) + ->disableOriginalConstructor() + ->getMock(); + $this->rulePricesStorageMock = $this->getMockBuilder(RulePricesStorage::class) + ->setMethods(['getWebsiteId', 'getRulePrice', 'getCustomerGroupId', 'setRulePrice']) + ->disableOriginalConstructor() + ->getMock(); + $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class) + ->setMethods(['getStore']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->resourceRuleFactoryMock = $this->getMockBuilder(RuleFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $this->localeDateMock = $this->getMockBuilder(TimezoneInterface::class) + ->setMethods(['scopeDate']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $objectManagerHelper = new ObjectManager($this); + $this->observer = $objectManagerHelper->getObject( + ProcessAdminFinalPriceObserver::class, + [ + 'rulePricesStorage' => $this->rulePricesStorageMock, + 'storeManager' => $this->storeManagerMock, + 'resourceRuleFactory' => $this->resourceRuleFactoryMock, + 'localeDate' => $this->localeDateMock + ] + ); + } + + public function testExecute() + { + $finalPrice = 20.00; + $rulePrice = 10.00; + $storeId = 2; + $wId = 1; + $gId = 4; + $pId = 20; + $localeDateFormat = 'Y-m-d H:i:s'; + $date = '2019-12-02 08:00:00'; + $storeMock = $this->createMock(Store::class); + $this->observerMock + ->expects($this->atLeastOnce()) + ->method('getEvent') + ->willReturn($this->eventMock); + + $productMock = $this->getMockBuilder(Product::class) + ->setMethods( + [ + 'getStoreId', + 'getWebsiteId', + 'getId', + 'getData', + 'getCustomerGroupId', + 'setFinalPrice' + ] + ) + ->disableOriginalConstructor() + ->getMock(); + $dateMock = $this->getMockBuilder(Date::class) + ->setMethods(['format']) + ->disableOriginalConstructor() + ->getMock(); + + $this->localeDateMock->expects($this->once()) + ->method('scopeDate') + ->with($storeId) + ->willReturn($dateMock); + $dateMock->expects($this->once()) + ->method('format') + ->with($localeDateFormat) + ->willReturn($date); + $storeMock->expects($this->once()) + ->method('getWebsiteId') + ->willReturn($wId); + $this->storeManagerMock->expects($this->once()) + ->method('getStore') + ->with($storeId) + ->willReturn($storeMock); + $productMock->expects($this->once()) + ->method('getStoreId') + ->willReturn($storeId); + $productMock->expects($this->any()) + ->method('getCustomerGroupId') + ->willReturn($gId); + $productMock->expects($this->once()) + ->method('getId') + ->willReturn($pId); + $productMock->expects($this->once()) + ->method('getData') + ->with('final_price') + ->willReturn($finalPrice); + $this->rulePricesStorageMock->expects($this->any()) + ->method('getCustomerGroupId') + ->willReturn($gId); + $this->resourceRuleFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->rulePricesStorageMock); + $this->rulePricesStorageMock->expects($this->any()) + ->method('getRulePrice') + ->willReturn($rulePrice); + $this->rulePricesStorageMock->expects($this->once()) + ->method('setRulePrice') + ->willReturnSelf(); + $this->eventMock + ->expects($this->atLeastOnce()) + ->method('getProduct') + ->willReturn($productMock); + $this->assertEquals($this->observer, $this->observer->execute($this->observerMock)); + } +} diff --git a/app/code/Magento/CatalogRule/Test/Unit/Pricing/Price/CatalogRulePriceTest.php b/app/code/Magento/CatalogRule/Test/Unit/Pricing/Price/CatalogRulePriceTest.php index 7514d2bc4b5c5..54cd9e411df5c 100644 --- a/app/code/Magento/CatalogRule/Test/Unit/Pricing/Price/CatalogRulePriceTest.php +++ b/app/code/Magento/CatalogRule/Test/Unit/Pricing/Price/CatalogRulePriceTest.php @@ -176,4 +176,17 @@ public function testGetAmountNoBaseAmount() $result = $this->object->getValue(); $this->assertFalse($result); } + + public function testGetValueWithNullAmount() + { + $catalogRulePrice = null; + $convertedPrice = 0.0; + + $this->saleableItemMock->expects($this->once())->method('hasData') + ->with('catalog_rule_price')->willReturn(true); + $this->saleableItemMock->expects($this->once())->method('getData') + ->with('catalog_rule_price')->willReturn($catalogRulePrice); + + $this->assertEquals($convertedPrice, $this->object->getValue()); + } } diff --git a/app/code/Magento/CatalogRule/registration.php b/app/code/Magento/CatalogRule/registration.php index ec3cbee2e1582..fb370fedf3752 100644 --- a/app/code/Magento/CatalogRule/registration.php +++ b/app/code/Magento/CatalogRule/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CatalogRule', __DIR__); diff --git a/app/code/Magento/CatalogRule/view/adminhtml/ui_component/catalog_rule_form.xml b/app/code/Magento/CatalogRule/view/adminhtml/ui_component/catalog_rule_form.xml index 2af8bb0770b20..59e3c4668e8a4 100644 --- a/app/code/Magento/CatalogRule/view/adminhtml/ui_component/catalog_rule_form.xml +++ b/app/code/Magento/CatalogRule/view/adminhtml/ui_component/catalog_rule_form.xml @@ -208,6 +208,9 @@ </item> </argument> <settings> + <validation> + <rule name="validate-digits" xsi:type="boolean">true</rule> + </validation> <dataType>text</dataType> <label translate="true">Priority</label> <dataScope>sort_order</dataScope> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml index 3e700b5bcfb1b..1bc794ae80cd7 100644 --- a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml +++ b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml @@ -178,7 +178,7 @@ <actionGroup ref="logout" stepKey="logout"/> </after> <!-- Create price rule --> - <actionGroup ref="newCatalogPriceRuleByUI" stepKey="createPriceRule"> + <actionGroup ref="NewCatalogPriceRuleByUIActionGroup" stepKey="createPriceRule"> <argument name="catalogRule" value="CatalogRuleToFixed"/> </actionGroup> <actionGroup ref="CatalogSelectCustomerGroupActionGroup" stepKey="addCustomerGroup"> @@ -258,7 +258,7 @@ </actionGroup> <!--Assert products prices in the cart --> - <amOnPage url="/checkout/cart/" stepKey="amOnShoppingCartPage"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnShoppingCartPage"/> <waitForPageLoad stepKey="waitForShoppingCartPageLoad"/> <see userInput="$210.69" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createFirstConfigProductAttributeFirstOption.option[store_labels][1][label]$$)}}" stepKey="assertFirstProductPriceForFirstProductOption"/> <see userInput="$120.70" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createFirstConfigProductAttributeSecondOption.option[store_labels][1][label]$$)}}" stepKey="assertFirstProductPriceForSecondProductOption"/> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml index e53e51c626aa9..fcf5e2c038047 100644 --- a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml +++ b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml @@ -126,10 +126,10 @@ </after> <!-- Create price rule for first configurable product option --> - <actionGroup ref="newCatalogPriceRuleByUI" stepKey="createFirstPriceRule"> + <actionGroup ref="NewCatalogPriceRuleByUIActionGroup" stepKey="createFirstPriceRule"> <argument name="catalogRule" value="CatalogRuleToFixed"/> </actionGroup> - <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroupForFirstPriceRule"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroupForFirstPriceRule"/> <actionGroup ref="CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup" stepKey="createFirstCatalogPriceRuleCondition"> <argument name="attributeName" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$"/> <argument name="targetSelectValue" value="$$createConfigProductAttributeFirstOption.option[store_labels][1][label]$$"/> @@ -140,10 +140,10 @@ <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="assertSuccessMessageForFirstPriceRule"/> <!-- Create price rule for second configurable product option --> - <actionGroup ref="newCatalogPriceRuleByUI" stepKey="createSecondPriceRule"> + <actionGroup ref="NewCatalogPriceRuleByUIActionGroup" stepKey="createSecondPriceRule"> <argument name="catalogRule" value="_defaultCatalogRule"/> </actionGroup> - <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroupForSecondPriceRule"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroupForSecondPriceRule"/> <actionGroup ref="CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup" stepKey="createSecondCatalogPriceRuleCondition"> <argument name="attributeName" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$"/> <argument name="targetSelectValue" value="$$createConfigProductAttributeSecondOption.option[store_labels][1][label]$$"/> @@ -154,10 +154,10 @@ <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="assertSuccessMessageForSecondPriceRule"/> <!-- Create price rule for third configurable product option --> - <actionGroup ref="newCatalogPriceRuleByUI" stepKey="createThirdPriceRule"> + <actionGroup ref="NewCatalogPriceRuleByUIActionGroup" stepKey="createThirdPriceRule"> <argument name="catalogRule" value="CatalogRuleWithoutDiscount"/> </actionGroup> - <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroupForThirdPriceRule"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroupForThirdPriceRule"/> <actionGroup ref="CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup" stepKey="createThirdCatalogPriceRuleCondition"> <argument name="attributeName" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$"/> <argument name="targetSelectValue" value="$$createConfigProductAttributeThirdOption.option[store_labels][1][label]$$"/> @@ -212,7 +212,7 @@ </actionGroup> <!--Assert product price in the cart --> - <amOnPage url="/checkout/cart/" stepKey="amOnShoppingCartPage"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnShoppingCartPage"/> <waitForPageLoad stepKey="waitForShoppingCartPageLoad"/> <see userInput="{{CatalogRuleToFixed.discount_amount}}" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createConfigProductAttributeFirstOption.option[store_labels][1][label]$$)}}" stepKey="assertProductPriceForFirstProductOption"/> <see userInput="$110.70" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createConfigProductAttributeSecondOption.option[store_labels][1][label]$$)}}" stepKey="assertProductPriceForSecondProductOption"/> diff --git a/app/code/Magento/CatalogRuleConfigurable/registration.php b/app/code/Magento/CatalogRuleConfigurable/registration.php index 0cc791e23a9a4..5f60111f246f1 100644 --- a/app/code/Magento/CatalogRuleConfigurable/registration.php +++ b/app/code/Magento/CatalogRuleConfigurable/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CatalogRuleConfigurable', __DIR__); diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Product/Category/Action/Rows.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Product/Category/Action/Rows.php new file mode 100644 index 0000000000000..2b1844deb114c --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Plugin/Product/Category/Action/Rows.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\Product\Category\Action; + +use Magento\Framework\Indexer\IndexerRegistry; +use Magento\CatalogSearch\Model\Indexer\Fulltext as FulltextIndexer; +use Magento\Catalog\Model\Indexer\Product\Category\Action\Rows as ActionRows; + +/** + * Catalog search indexer plugin for catalog category products assignment + */ +class Rows +{ + /** + * @var IndexerRegistry + */ + private $indexerRegistry; + + /** + * @param IndexerRegistry $indexerRegistry + */ + public function __construct(IndexerRegistry $indexerRegistry) + { + $this->indexerRegistry = $indexerRegistry; + } + + /** + * Reindex after catalog category product reindex + * + * @param ActionRows $subject + * @param ActionRows $result + * @param array $entityIds + * @return ActionRows + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterExecute(ActionRows $subject, ActionRows $result, array $entityIds): ActionRows + { + if (!empty($entityIds)) { + $indexer = $this->indexerRegistry->get(FulltextIndexer::INDEXER_ID); + if ($indexer->isScheduled()) { + $indexer->reindexList($entityIds); + } + } + return $result; + } +} diff --git a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php index 85a32dc60b119..d8947ac4224a8 100644 --- a/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php +++ b/app/code/Magento/CatalogSearch/Model/Layer/Filter/Attribute.php @@ -58,15 +58,18 @@ public function apply(\Magento\Framework\App\RequestInterface $request) if (empty($attributeValue) && !is_numeric($attributeValue)) { return $this; } + $attribute = $this->getAttributeModel(); /** @var \Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection $productCollection */ $productCollection = $this->getLayer() ->getProductCollection(); $productCollection->addFieldToFilter($attribute->getAttributeCode(), $attributeValue); - $label = $this->getOptionText($attributeValue); - if (is_array($label)) { - $label = implode(',', $label); + + $labels = []; + foreach ((array) $attributeValue as $value) { + $labels[] = (array) $this->getOptionText($value); } + $label = implode(',', array_unique(array_merge(...$labels))); $this->getLayer() ->getState() ->addFilter($this->_createItem($label, $attributeValue)); diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php index 51879a3dcfa45..bbebbc99103a2 100644 --- a/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php +++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/Advanced/Collection.php @@ -27,6 +27,7 @@ use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplierFactory; use Magento\Framework\App\ObjectManager; use Magento\Framework\Api\Search\SearchResultInterface; +use Magento\Search\Model\EngineResolver; /** * Advanced search collection @@ -40,6 +41,11 @@ */ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection { + /** + * Config search engine path. + */ + private const SEARCH_ENGINE_VALUE_PATH = 'catalog/search/engine'; + /** * List Of filters * @var array @@ -344,6 +350,63 @@ protected function _renderFiltersBefore() parent::_renderFiltersBefore(); } + /** + * @inheritDoc + */ + public function clear() + { + $this->searchResult = null; + return parent::clear(); + } + + /** + * @inheritDoc + */ + protected function _reset() + { + $this->searchResult = null; + return parent::_reset(); + } + + /** + * @inheritdoc + */ + public function _loadEntities($printQuery = false, $logQuery = false) + { + $this->getEntity(); + + $currentSearchEngine = $this->_scopeConfig->getValue(self::SEARCH_ENGINE_VALUE_PATH); + if ($this->_pageSize && $currentSearchEngine === EngineResolver::CATALOG_SEARCH_MYSQL_ENGINE) { + $this->getSelect()->limitPage($this->getCurPage(), $this->_pageSize); + } + + $this->printLogQuery($printQuery, $logQuery); + + try { + /** + * Prepare select query + * @var string $query + */ + $query = $this->getSelect(); + $rows = $this->_fetchAll($query); + } catch (\Exception $e) { + $this->printLogQuery(false, true, $query); + throw $e; + } + + foreach ($rows as $value) { + $object = $this->getNewEmptyItem()->setData($value); + $this->addItem($object); + if (isset($this->_itemsById[$object->getId()])) { + $this->_itemsById[$object->getId()][] = $object; + } else { + $this->_itemsById[$object->getId()] = [$object]; + } + } + + return $this; + } + /** * Get total records resolver. * diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php index f11ddb3a3d6b7..3506437ea038d 100644 --- a/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php +++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php @@ -27,6 +27,7 @@ use Magento\Framework\Exception\LocalizedException; use Magento\Framework\App\ObjectManager; use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; +use Magento\Search\Model\EngineResolver; /** * Fulltext Collection @@ -41,6 +42,11 @@ */ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection { + /** + * Config search engine path. + */ + private const SEARCH_ENGINE_VALUE_PATH = 'catalog/search/engine'; + /** * @var QueryResponse * @deprecated 100.1.0 @@ -212,10 +218,8 @@ public function __construct( DefaultFilterStrategyApplyCheckerInterface $defaultFilterStrategyApplyChecker = null ) { $this->queryFactory = $catalogSearchData; - if ($searchResultFactory === null) { - $this->searchResultFactory = \Magento\Framework\App\ObjectManager::getInstance() + $this->searchResultFactory = $searchResultFactory ?? \Magento\Framework\App\ObjectManager::getInstance() ->get(\Magento\Framework\Api\Search\SearchResultFactory::class); - } parent::__construct( $entityFactory, $logger, @@ -375,6 +379,63 @@ public function addFieldToFilter($field, $condition = null) return $this; } + /** + * @inheritDoc + */ + public function clear() + { + $this->searchResult = null; + return parent::clear(); + } + + /** + * @inheritDoc + */ + protected function _reset() + { + $this->searchResult = null; + return parent::_reset(); + } + + /** + * @inheritdoc + */ + public function _loadEntities($printQuery = false, $logQuery = false) + { + $this->getEntity(); + + $currentSearchEngine = $this->_scopeConfig->getValue(self::SEARCH_ENGINE_VALUE_PATH); + if ($this->_pageSize && $currentSearchEngine === EngineResolver::CATALOG_SEARCH_MYSQL_ENGINE) { + $this->getSelect()->limitPage($this->getCurPage(), $this->_pageSize); + } + + $this->printLogQuery($printQuery, $logQuery); + + try { + /** + * Prepare select query + * @var string $query + */ + $query = $this->getSelect(); + $rows = $this->_fetchAll($query); + } catch (\Exception $e) { + $this->printLogQuery(false, true, $query); + throw $e; + } + + foreach ($rows as $value) { + $object = $this->getNewEmptyItem()->setData($value); + $this->addItem($object); + if (isset($this->_itemsById[$object->getId()])) { + $this->_itemsById[$object->getId()][] = $object; + } else { + $this->_itemsById[$object->getId()] = [$object]; + } + } + + return $this; + } + /** * Add search query filter * @@ -427,25 +488,40 @@ protected function _renderFiltersBefore() return; } - $this->prepareSearchTermFilter(); - $this->preparePriceAggregation(); - - $searchCriteria = $this->getSearchCriteriaResolver()->resolve(); - try { - $this->searchResult = $this->getSearch()->search($searchCriteria); - $this->_totalRecords = $this->getTotalRecordsResolver($this->searchResult)->resolve(); - } catch (EmptyRequestDataException $e) { - /** @var \Magento\Framework\Api\Search\SearchResultInterface $searchResult */ - $this->searchResult = $this->searchResultFactory->create()->setItems([]); - } catch (NonExistingRequestNameException $e) { - $this->_logger->error($e->getMessage()); - throw new LocalizedException(__('An error occurred. For details, see the error log.')); + if ($this->searchRequestName !== 'quick_search_container' + || strlen(trim($this->queryText)) + ) { + $this->prepareSearchTermFilter(); + $this->preparePriceAggregation(); + + $searchCriteria = $this->getSearchCriteriaResolver()->resolve(); + try { + $this->searchResult = $this->getSearch()->search($searchCriteria); + $this->_totalRecords = $this->getTotalRecordsResolver($this->searchResult)->resolve(); + } catch (EmptyRequestDataException $e) { + $this->searchResult = $this->createEmptyResult(); + } catch (NonExistingRequestNameException $e) { + $this->_logger->error($e->getMessage()); + throw new LocalizedException(__('An error occurred. For details, see the error log.')); + } + } else { + $this->searchResult = $this->createEmptyResult(); } $this->getSearchResultApplier($this->searchResult)->apply(); parent::_renderFiltersBefore(); } + /** + * Create empty search result + * + * @return SearchResultInterface + */ + private function createEmptyResult() + { + return $this->searchResultFactory->create()->setItems([]); + } + /** * Set sort order for search query. * diff --git a/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/CustomAttributeStockStatusFilter.php b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/CustomAttributeStockStatusFilter.php new file mode 100644 index 0000000000000..28aa3df2d56b4 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/CustomAttributeStockStatusFilter.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogSearch\Model\Search\FilterMapper; + +use Magento\Catalog\Model\Product; +use Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver; +use Magento\Eav\Model\Config as EavConfig; +use Magento\Framework\DB\Select; +use Magento\Framework\Search\Request\FilterInterface; + +/** + * Add stock status filter for each requested filter + */ +class CustomAttributeStockStatusFilter +{ + /** + * Suffix to append to filter name in order to generate stock status table alias for JOIN clause + */ + private const STOCK_STATUS_TABLE_ALIAS_SUFFIX = '_stock_index'; + /** + * Attribute types to apply + */ + private const TARGET_ATTRIBUTE_TYPES = [ + 'select', + 'multiselect' + ]; + /** + * @var EavConfig + */ + private $eavConfig; + /** + * @var AliasResolver + */ + private $aliasResolver; + /** + * @var StockStatusQueryBuilder|null + */ + private $stockStatusQueryBuilder; + + /** + * @param EavConfig $eavConfig + * @param AliasResolver $aliasResolver + * @param StockStatusQueryBuilder $stockStatusQueryBuilder + */ + public function __construct( + EavConfig $eavConfig, + AliasResolver $aliasResolver, + StockStatusQueryBuilder $stockStatusQueryBuilder + ) { + $this->eavConfig = $eavConfig; + $this->aliasResolver = $aliasResolver; + $this->stockStatusQueryBuilder = $stockStatusQueryBuilder; + } + + /** + * Apply stock status filter to provided filter + * + * @param Select $select + * @param mixed $values + * @param FilterInterface[] $filters + * @return Select + */ + public function apply(Select $select, $values = null, FilterInterface ...$filters): Select + { + $select = clone $select; + foreach ($filters as $filter) { + if ($this->isApplicable($filter)) { + $mainTableAlias = $this->aliasResolver->getAlias($filter); + $stockTableAlias = $mainTableAlias . self::STOCK_STATUS_TABLE_ALIAS_SUFFIX; + $select = $this->stockStatusQueryBuilder->apply( + $select, + $mainTableAlias, + $stockTableAlias, + 'source_id', + $values + ); + } + } + return $select; + } + + /** + * Check if stock status filter is applicable to provided filter + * + * @param FilterInterface $filter + * @return bool + */ + private function isApplicable(FilterInterface $filter): bool + { + $attribute = $this->eavConfig->getAttribute(Product::ENTITY, $filter->getField()); + return $attribute + && $filter->getType() === FilterInterface::TYPE_TERM + && in_array($attribute->getFrontendInput(), self::TARGET_ATTRIBUTE_TYPES, true); + } +} diff --git a/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/FilterMapper.php b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/FilterMapper.php index 7136fad5b19a9..1c4a803c1dd00 100644 --- a/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/FilterMapper.php +++ b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/FilterMapper.php @@ -9,13 +9,14 @@ use Magento\CatalogSearch\Model\Search\SelectContainer\SelectContainer; use Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver; use Magento\CatalogInventory\Model\Stock; +use Magento\Framework\App\ObjectManager; /** - * Class FilterMapper * This class applies filters to Select based on SelectContainer configuration * - * @deprecated + * @deprecated MySQL search engine is not recommended. * @see \Magento\ElasticSearch + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class FilterMapper { @@ -43,6 +44,10 @@ class FilterMapper * @var StockStatusFilter */ private $stockStatusFilter; + /** + * @var CustomAttributeStockStatusFilter + */ + private $customAttributeStockStatusFilter; /** * @param AliasResolver $aliasResolver @@ -50,24 +55,27 @@ class FilterMapper * @param FilterStrategyInterface $filterStrategy * @param VisibilityFilter $visibilityFilter * @param StockStatusFilter $stockStatusFilter + * @param CustomAttributeStockStatusFilter|null $customAttributeStockStatusFilter */ public function __construct( AliasResolver $aliasResolver, CustomAttributeFilter $customAttributeFilter, FilterStrategyInterface $filterStrategy, VisibilityFilter $visibilityFilter, - StockStatusFilter $stockStatusFilter + StockStatusFilter $stockStatusFilter, + ?CustomAttributeStockStatusFilter $customAttributeStockStatusFilter = null ) { $this->aliasResolver = $aliasResolver; $this->customAttributeFilter = $customAttributeFilter; $this->filterStrategy = $filterStrategy; $this->visibilityFilter = $visibilityFilter; $this->stockStatusFilter = $stockStatusFilter; + $this->customAttributeStockStatusFilter = $customAttributeStockStatusFilter + ?? ObjectManager::getInstance()->get(CustomAttributeStockStatusFilter::class); } /** - * Applies filters to Select query in SelectContainer - * based on SelectContainer configuration + * Applies filters to Select query in SelectContainer based on SelectContainer configuration * * @param SelectContainer $selectContainer * @return SelectContainer @@ -79,22 +87,22 @@ public function applyFilters(SelectContainer $selectContainer) { $select = $selectContainer->getSelect(); - if ($selectContainer->hasCustomAttributesFilters()) { - $select = $this->customAttributeFilter->apply($select, ...$selectContainer->getCustomAttributesFilters()); - } - - $filterType = StockStatusFilter::FILTER_JUST_ENTITY; - if ($selectContainer->hasCustomAttributesFilters()) { - $filterType = StockStatusFilter::FILTER_ENTITY_AND_SUB_PRODUCTS; - } - $select = $this->stockStatusFilter->apply( $select, Stock::STOCK_IN_STOCK, - $filterType, + StockStatusFilter::FILTER_JUST_ENTITY, $selectContainer->isShowOutOfStockEnabled() ); + if ($selectContainer->hasCustomAttributesFilters()) { + $select = $this->customAttributeFilter->apply($select, ...$selectContainer->getCustomAttributesFilters()); + $select = $this->customAttributeStockStatusFilter->apply( + $select, + $selectContainer->isShowOutOfStockEnabled() ? null : Stock::STOCK_IN_STOCK, + ...$selectContainer->getCustomAttributesFilters() + ); + } + $appliedFilters = []; if ($selectContainer->hasVisibilityFilter()) { diff --git a/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusFilter.php b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusFilter.php index 0e3ba0d4e669f..420c69a7325f6 100644 --- a/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusFilter.php +++ b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusFilter.php @@ -6,6 +6,7 @@ namespace Magento\CatalogSearch\Model\Search\FilterMapper; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Select; use Magento\Framework\Search\Adapter\Mysql\ConditionManager; @@ -13,10 +14,9 @@ use Magento\CatalogInventory\Api\StockRegistryInterface; /** - * Class StockStatusFilter * Adds filter by stock status to base select * - * @deprecated + * @deprecated MySQL search engine is not recommended. * @see \Magento\ElasticSearch */ class StockStatusFilter @@ -56,23 +56,31 @@ class StockStatusFilter * @var StockRegistryInterface */ private $stockRegistry; + /** + * @var StockStatusQueryBuilder + */ + private $stockStatusQueryBuilder; /** * @param ResourceConnection $resourceConnection * @param ConditionManager $conditionManager * @param StockConfigurationInterface $stockConfiguration * @param StockRegistryInterface $stockRegistry + * @param StockStatusQueryBuilder|null $stockStatusQueryBuilder */ public function __construct( ResourceConnection $resourceConnection, ConditionManager $conditionManager, StockConfigurationInterface $stockConfiguration, - StockRegistryInterface $stockRegistry + StockRegistryInterface $stockRegistry, + ?StockStatusQueryBuilder $stockStatusQueryBuilder = null ) { $this->resourceConnection = $resourceConnection; $this->conditionManager = $conditionManager; $this->stockConfiguration = $stockConfiguration; $this->stockRegistry = $stockRegistry; + $this->stockStatusQueryBuilder = $stockStatusQueryBuilder + ?? ObjectManager::getInstance()->get(StockStatusQueryBuilder::class); } /** @@ -94,99 +102,27 @@ public function apply(Select $select, $stockValues, $type, $showOutOfStockFlag) $select = clone $select; $mainTableAlias = $this->extractTableAliasFromSelect($select); - $this->addMainStockStatusJoin($select, $stockValues, $mainTableAlias, $showOutOfStockFlag); + $select = $this->stockStatusQueryBuilder->apply( + $select, + $mainTableAlias, + 'stock_index', + 'entity_id', + $showOutOfStockFlag ? null : $stockValues + ); if ($type === self::FILTER_ENTITY_AND_SUB_PRODUCTS) { - $this->addSubProductsStockStatusJoin($select, $stockValues, $mainTableAlias, $showOutOfStockFlag); + $select = $this->stockStatusQueryBuilder->apply( + $select, + $mainTableAlias, + 'sub_products_stock_index', + 'source_id', + $showOutOfStockFlag ? null : $stockValues + ); } return $select; } - /** - * Adds filter join for products by stock status - * In case when $showOutOfStockFlag is true - joins are still required to filter only enabled products - * - * @param Select $select - * @param array|int $stockValues - * @param string $mainTableAlias - * @param bool $showOutOfStockFlag - * @return void - */ - private function addMainStockStatusJoin(Select $select, $stockValues, $mainTableAlias, $showOutOfStockFlag) - { - $catalogInventoryTable = $this->resourceConnection->getTableName('cataloginventory_stock_status'); - $select->joinInner( - ['stock_index' => $catalogInventoryTable], - $this->conditionManager->combineQueries( - [ - sprintf('stock_index.product_id = %s.entity_id', $mainTableAlias), - $this->conditionManager->generateCondition( - 'stock_index.website_id', - '=', - $this->stockConfiguration->getDefaultScopeId() - ), - $showOutOfStockFlag - ? '' - : $this->conditionManager->generateCondition( - 'stock_index.stock_status', - is_array($stockValues) ? 'in' : '=', - $stockValues - ), - $this->conditionManager->generateCondition( - 'stock_index.stock_id', - '=', - (int) $this->stockRegistry->getStock()->getStockId() - ), - ], - Select::SQL_AND - ), - [] - ); - } - - /** - * Adds filter join for sub products by stock status - * In case when $showOutOfStockFlag is true - joins are still required to filter only enabled products - * - * @param Select $select - * @param array|int $stockValues - * @param string $mainTableAlias - * @param bool $showOutOfStockFlag - * @return void - */ - private function addSubProductsStockStatusJoin(Select $select, $stockValues, $mainTableAlias, $showOutOfStockFlag) - { - $catalogInventoryTable = $this->resourceConnection->getTableName('cataloginventory_stock_status'); - $select->joinInner( - ['sub_products_stock_index' => $catalogInventoryTable], - $this->conditionManager->combineQueries( - [ - sprintf('sub_products_stock_index.product_id = %s.source_id', $mainTableAlias), - $this->conditionManager->generateCondition( - 'sub_products_stock_index.website_id', - '=', - $this->stockConfiguration->getDefaultScopeId() - ), - $showOutOfStockFlag - ? '' - : $this->conditionManager->generateCondition( - 'sub_products_stock_index.stock_status', - is_array($stockValues) ? 'in' : '=', - $stockValues - ), - $this->conditionManager->generateCondition( - 'sub_products_stock_index.stock_id', - '=', - (int) $this->stockRegistry->getStock()->getStockId() - ), - ], - Select::SQL_AND - ), - [] - ); - } - /** * Extracts alias for table that is used in FROM clause in Select * diff --git a/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusQueryBuilder.php b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusQueryBuilder.php new file mode 100644 index 0000000000000..2d0d408875661 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Search/FilterMapper/StockStatusQueryBuilder.php @@ -0,0 +1,106 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogSearch\Model\Search\FilterMapper; + +use Magento\CatalogInventory\Model\ResourceModel\Stock\Status as StockStatusResourceModel; +use Magento\Framework\DB\Select; +use Magento\Framework\Search\Adapter\Mysql\ConditionManager; +use Magento\CatalogInventory\Api\StockConfigurationInterface; +use Magento\CatalogInventory\Api\StockRegistryInterface; + +/** + * Add stock status filter to Select + */ +class StockStatusQueryBuilder +{ + /** + * @var StockStatusResourceModel + */ + private $stockStatusResourceModel; + + /** + * @var ConditionManager + */ + private $conditionManager; + + /** + * @var StockConfigurationInterface + */ + private $stockConfiguration; + + /** + * @var StockRegistryInterface + */ + private $stockRegistry; + + /** + * @param StockStatusResourceModel $stockStatusResourceModel + * @param ConditionManager $conditionManager + * @param StockConfigurationInterface $stockConfiguration + * @param StockRegistryInterface $stockRegistry + */ + public function __construct( + StockStatusResourceModel $stockStatusResourceModel, + ConditionManager $conditionManager, + StockConfigurationInterface $stockConfiguration, + StockRegistryInterface $stockRegistry + ) { + $this->stockStatusResourceModel = $stockStatusResourceModel; + $this->conditionManager = $conditionManager; + $this->stockConfiguration = $stockConfiguration; + $this->stockRegistry = $stockRegistry; + } + + /** + * Add stock filter to Select + * + * @param Select $select + * @param string $mainTableAlias + * @param string $stockTableAlias + * @param string $joinField + * @param mixed $values + * @return Select + */ + public function apply( + Select $select, + string $mainTableAlias, + string $stockTableAlias, + string $joinField, + $values = null + ): Select { + $select->joinInner( + [$stockTableAlias => $this->stockStatusResourceModel->getMainTable()], + $this->conditionManager->combineQueries( + [ + sprintf('%s.product_id = %s.%s', $stockTableAlias, $mainTableAlias, $joinField), + $this->conditionManager->generateCondition( + sprintf('%s.website_id', $stockTableAlias), + '=', + $this->stockConfiguration->getDefaultScopeId() + ), + $values === null + ? '' + : $this->conditionManager->generateCondition( + sprintf('%s.stock_status', $stockTableAlias), + is_array($values) ? 'in' : '=', + $values + ), + $this->conditionManager->generateCondition( + sprintf('%s.stock_id', $stockTableAlias), + '=', + (int) $this->stockRegistry->getStock()->getStockId() + ), + ], + Select::SQL_AND + ), + [] + ); + + return $select; + } +} diff --git a/app/code/Magento/CatalogSearch/Setup/Patch/Data/SetInitialSearchWeightForAttributes.php b/app/code/Magento/CatalogSearch/Setup/Patch/Data/SetInitialSearchWeightForAttributes.php index 7f6dbe033e3a5..21d5e82d494b5 100644 --- a/app/code/Magento/CatalogSearch/Setup/Patch/Data/SetInitialSearchWeightForAttributes.php +++ b/app/code/Magento/CatalogSearch/Setup/Patch/Data/SetInitialSearchWeightForAttributes.php @@ -6,18 +6,21 @@ namespace Magento\CatalogSearch\Setup\Patch\Data; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; use Magento\Framework\App\State; +use Magento\Framework\Indexer\IndexerInterfaceFactory; use Magento\Framework\Setup\Patch\DataPatchInterface; use Magento\Framework\Setup\Patch\PatchVersionInterface; -use Magento\Framework\Indexer\IndexerInterfaceFactory; -use Magento\Catalog\Api\ProductAttributeRepositoryInterface; /** + * This patch sets up search weight for the product's system attributes, reindex required after patch applying. + * * @deprecated * @see \Magento\ElasticSearch */ class SetInitialSearchWeightForAttributes implements DataPatchInterface, PatchVersionInterface { + /** * @var IndexerInterfaceFactory */ @@ -50,7 +53,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function apply() { @@ -60,13 +63,15 @@ public function apply() $this->state->emulateAreaCode( \Magento\Framework\App\Area::AREA_CRONTAB, function () use ($indexer) { - $indexer->reindexAll(); + $indexer->getState() + ->setStatus(\Magento\Framework\Indexer\StateInterface::STATUS_INVALID) + ->save(); } ); } /** - * {@inheritdoc} + * @inheritdoc */ public static function getDependencies() { @@ -74,7 +79,7 @@ public static function getDependencies() } /** - * {@inheritdoc} + * @inheritdoc */ public static function getVersion() { @@ -82,7 +87,7 @@ public static function getVersion() } /** - * {@inheritdoc} + * @inheritdoc */ public function getAliases() { diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminCatalogSearchTermActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminCatalogSearchTermActionGroup.xml deleted file mode 100644 index 932c45c0d5aeb..0000000000000 --- a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AdminCatalogSearchTermActionGroup.xml +++ /dev/null @@ -1,73 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertSearchTermSaveSuccessMessage"> - <annotations> - <description>Goes to the Catalog Search Term grid page. Adds the provided Search Term. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <argument name="searchQuery" type="string"/> - <argument name="storeValue" type="string"/> - <argument name="redirectUrl" type="string"/> - <argument name="displayInSuggestedTerm" type="string"/> - </arguments> - - <amOnPage url="{{AdminCatalogSearchTermIndexPage.url}}" stepKey="openAdminCatalogSearchTermIndexPage"/> - <waitForPageLoad stepKey="waitForAdminCatalogSearchTermIndexPageLoad"/> - <click selector="{{AdminCatalogSearchTermIndexSection.addNewSearchTermButton}}" stepKey="clickAddNewSearchTermButton"/> - <waitForPageLoad stepKey="waitForAdminCatalogSearchTermNewPageLoad"/> - <fillField selector="{{AdminCatalogSearchTermNewSection.searchQuery}}" userInput="{{searchQuery}}" stepKey="fillSearchQueryTextBox"/> - <selectOption selector="{{AdminCatalogSearchTermNewSection.store}}" userInput="{{storeValue}}" stepKey="selectStoreValue"/> - <fillField selector="{{AdminCatalogSearchTermNewSection.redirectUrl}}" userInput="{{redirectUrl}}" stepKey="fillRedirectUrl"/> - <selectOption selector="{{AdminCatalogSearchTermNewSection.displayInSuggestedTerm}}" userInput="{{displayInSuggestedTerm}}" stepKey="selectDisplayInSuggestedTerm"/> - <click selector="{{AdminCatalogSearchTermNewSection.saveSearchButton}}" stepKey="clickSaveSearchButton"/> - <see selector="{{AdminCatalogSearchTermMessagesSection.successMessage}}" userInput="You saved the search term." stepKey="seeSaveSuccessMessage"/> - </actionGroup> - - <actionGroup name="AssertSearchTermSuccessDeleteMessage"> - <annotations> - <description>Goes to the Catalog Search Term grid page. Deletes the provided Search Term. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <argument name="searchQuery" type="string"/> - </arguments> - - <amOnPage url="{{AdminCatalogSearchTermIndexPage.url}}" stepKey="openCatalogSearchIndexPage"/> - <waitForPageLoad stepKey="waitForAdminCatalogSearchTermIndexPageLoad"/> - <click selector="{{AdminCatalogSearchTermIndexSection.resetFilterButton}}" stepKey="clickOnResetButton"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> - <fillField selector="{{AdminCatalogSearchTermIndexSection.searchQuery}}" userInput="{{searchQuery}}" stepKey="fillSearchQuery"/> - <click selector="{{AdminCatalogSearchTermIndexSection.searchButton}}" stepKey="clickSearchButton"/> - <waitForPageLoad stepKey="waitForSearchResultLoad"/> - <click selector="{{AdminCatalogSearchTermIndexSection.nthRow('1')}}" stepKey="checkFirstRow"/> - <selectOption selector="{{AdminCatalogSearchTermIndexSection.massActions}}" userInput="delete" stepKey="selectDeleteOption"/> - <click selector="{{AdminCatalogSearchTermIndexSection.submit}}" stepKey="clickSubmitButton"/> - <click selector="{{AdminCatalogSearchTermIndexSection.okButton}}" stepKey="clickOkButton"/> - <see selector="{{AdminCatalogSearchTermMessagesSection.successMessage}}" userInput="Total of 1 record(s) were deleted." stepKey="seeSuccessMessage"/> - </actionGroup> - - <actionGroup name="AssertSearchTermNotInGrid"> - <annotations> - <description>Goes to the Catalog Search Term grid page. Searches for the provided Search Term. Validates that it is NOT present in the grid.</description> - </annotations> - <arguments> - <argument name="searchQuery" type="string"/> - </arguments> - - <amOnPage url="{{AdminCatalogSearchTermIndexPage.url}}" stepKey="openCatalogSearchIndexPage"/> - <waitForPageLoad stepKey="waitForAdminCatalogSearchTermIndexPageLoad"/> - <click selector="{{AdminCatalogSearchTermIndexSection.resetFilterButton}}" stepKey="clickOnResetButton"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> - <fillField selector="{{AdminCatalogSearchTermIndexSection.searchQuery}}" userInput="{{searchQuery}}" stepKey="fillSearchQuery"/> - <click selector="{{AdminCatalogSearchTermIndexSection.searchButton}}" stepKey="clickSearchButton"/> - <waitForPageLoad stepKey="waitForSearchResultToLoad"/> - <see selector="{{AdminCatalogSearchTermIndexSection.emptyRecords}}" userInput="We couldn't find any records." stepKey="seeEmptyRecordMessage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AssertSearchTermNotInGridActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AssertSearchTermNotInGridActionGroup.xml new file mode 100644 index 0000000000000..6b8b94931b3f9 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AssertSearchTermNotInGridActionGroup.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="AssertSearchTermNotInGridActionGroup"> + <annotations> + <description>Goes to the Catalog Search Term grid page. Searches for the provided Search Term. Validates that it is NOT present in the grid.</description> + </annotations> + <arguments> + <argument name="searchQuery" type="string"/> + </arguments> + + <amOnPage url="{{AdminCatalogSearchTermIndexPage.url}}" stepKey="openCatalogSearchIndexPage"/> + <waitForPageLoad stepKey="waitForAdminCatalogSearchTermIndexPageLoad"/> + <click selector="{{AdminCatalogSearchTermIndexSection.resetFilterButton}}" stepKey="clickOnResetButton"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <fillField selector="{{AdminCatalogSearchTermIndexSection.searchQuery}}" userInput="{{searchQuery}}" stepKey="fillSearchQuery"/> + <click selector="{{AdminCatalogSearchTermIndexSection.searchButton}}" stepKey="clickSearchButton"/> + <waitForPageLoad stepKey="waitForSearchResultToLoad"/> + <see selector="{{AdminCatalogSearchTermIndexSection.emptyRecords}}" userInput="We couldn't find any records." stepKey="seeEmptyRecordMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AssertSearchTermNotOnFrontendActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AssertSearchTermNotOnFrontendActionGroup.xml new file mode 100644 index 0000000000000..67e60a1603a9b --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AssertSearchTermNotOnFrontendActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!--Verify AssertSearchTermNotOnFrontend--> + <actionGroup name="AssertSearchTermNotOnFrontendActionGroup"> + <annotations> + <description>Goes to the Storefront. Fills the Search field with the provided Search Query. Clicks on Search. Validates that there are no results.</description> + </annotations> + <arguments> + <argument name="searchQuery" type="string"/> + <argument name="url_key" type="string"/> + </arguments> + + <amOnPage url="{{StorefrontProductPage.url('url_key')}}" stepKey="goToMagentoStorefrontPage"/> + <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> + <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{searchQuery}}" stepKey="fillSearchQuery"/> + <waitForPageLoad stepKey="waitForSearchTextBox"/> + <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> + <waitForPageLoad stepKey="waitForSearch"/> + <see selector="{{StorefrontMessagesSection.noticeMessage}}" userInput="Your search returned no results." stepKey="seeAssertSearchTermNotOnFrontendNoticeMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AssertSearchTermOnFrontendActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AssertSearchTermOnFrontendActionGroup.xml new file mode 100644 index 0000000000000..cb2ce92531af8 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AssertSearchTermOnFrontendActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertSearchTermOnFrontendActionGroup"> + <annotations> + <description>Fills the Storefront Search field with the provided Search Query. Clicks on Search. Validates that the URL is correct.</description> + </annotations> + <arguments> + <argument name="searchQuery" type="string"/> + <argument name="redirectUrl" type="string"/> + </arguments> + + <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{searchQuery}}" stepKey="fillSearchQuery"/> + <waitForPageLoad stepKey="waitForFillField"/> + <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> + <waitForPageLoad stepKey="waitForSearch"/> + <seeInCurrentUrl url="{{redirectUrl}}" stepKey="checkUrl"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AssertSearchTermSaveSuccessMessageActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AssertSearchTermSaveSuccessMessageActionGroup.xml new file mode 100644 index 0000000000000..4291bbff88aae --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AssertSearchTermSaveSuccessMessageActionGroup.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertSearchTermSaveSuccessMessageActionGroup"> + <annotations> + <description>Goes to the Catalog Search Term grid page. Adds the provided Search Term. Validates that the Success Message is present and correct.</description> + </annotations> + <arguments> + <argument name="searchQuery" type="string"/> + <argument name="storeValue" type="string"/> + <argument name="redirectUrl" type="string"/> + <argument name="displayInSuggestedTerm" type="string"/> + </arguments> + + <amOnPage url="{{AdminCatalogSearchTermIndexPage.url}}" stepKey="openAdminCatalogSearchTermIndexPage"/> + <waitForPageLoad stepKey="waitForAdminCatalogSearchTermIndexPageLoad"/> + <click selector="{{AdminCatalogSearchTermIndexSection.addNewSearchTermButton}}" stepKey="clickAddNewSearchTermButton"/> + <waitForPageLoad stepKey="waitForAdminCatalogSearchTermNewPageLoad"/> + <fillField selector="{{AdminCatalogSearchTermNewSection.searchQuery}}" userInput="{{searchQuery}}" stepKey="fillSearchQueryTextBox"/> + <selectOption selector="{{AdminCatalogSearchTermNewSection.store}}" userInput="{{storeValue}}" stepKey="selectStoreValue"/> + <fillField selector="{{AdminCatalogSearchTermNewSection.redirectUrl}}" userInput="{{redirectUrl}}" stepKey="fillRedirectUrl"/> + <selectOption selector="{{AdminCatalogSearchTermNewSection.displayInSuggestedTerm}}" userInput="{{displayInSuggestedTerm}}" stepKey="selectDisplayInSuggestedTerm"/> + <click selector="{{AdminCatalogSearchTermNewSection.saveSearchButton}}" stepKey="clickSaveSearchButton"/> + <see selector="{{AdminCatalogSearchTermMessagesSection.successMessage}}" userInput="You saved the search term." stepKey="seeSaveSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AssertSearchTermSuccessDeleteMessageActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AssertSearchTermSuccessDeleteMessageActionGroup.xml new file mode 100644 index 0000000000000..3d32b3c292305 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/AssertSearchTermSuccessDeleteMessageActionGroup.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="AssertSearchTermSuccessDeleteMessageActionGroup"> + <annotations> + <description>Goes to the Catalog Search Term grid page. Deletes the provided Search Term. Validates that the Success Message is present and correct.</description> + </annotations> + <arguments> + <argument name="searchQuery" type="string"/> + </arguments> + + <amOnPage url="{{AdminCatalogSearchTermIndexPage.url}}" stepKey="openCatalogSearchIndexPage"/> + <waitForPageLoad stepKey="waitForAdminCatalogSearchTermIndexPageLoad"/> + <click selector="{{AdminCatalogSearchTermIndexSection.resetFilterButton}}" stepKey="clickOnResetButton"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <fillField selector="{{AdminCatalogSearchTermIndexSection.searchQuery}}" userInput="{{searchQuery}}" stepKey="fillSearchQuery"/> + <click selector="{{AdminCatalogSearchTermIndexSection.searchButton}}" stepKey="clickSearchButton"/> + <waitForPageLoad stepKey="waitForSearchResultLoad"/> + <click selector="{{AdminCatalogSearchTermIndexSection.nthRow('1')}}" stepKey="checkFirstRow"/> + <selectOption selector="{{AdminCatalogSearchTermIndexSection.massActions}}" userInput="delete" stepKey="selectDeleteOption"/> + <click selector="{{AdminCatalogSearchTermIndexSection.submit}}" stepKey="clickSubmitButton"/> + <click selector="{{AdminCatalogSearchTermIndexSection.okButton}}" stepKey="clickOkButton"/> + <see selector="{{AdminCatalogSearchTermMessagesSection.successMessage}}" userInput="Total of 1 record(s) were deleted." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/GoToStoreViewAdvancedCatalogSearchActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/GoToStoreViewAdvancedCatalogSearchActionGroup.xml new file mode 100644 index 0000000000000..65ba0719af496 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/GoToStoreViewAdvancedCatalogSearchActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="GoToStoreViewAdvancedCatalogSearchActionGroup"> + <annotations> + <description>Goes to the Storefront 'Advanced Search' page.</description> + </annotations> + + <amOnPage url="{{StorefrontCatalogSearchAdvancedFormPage.url}}" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <waitForPageLoad time="90" stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontAddToCartFromQuickSearchActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontAddToCartFromQuickSearchActionGroup.xml new file mode 100644 index 0000000000000..1ee9003a6bdb6 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontAddToCartFromQuickSearchActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAddToCartFromQuickSearchActionGroup"> + <annotations> + <description>Adds the provided Product Name to the Shopping Cart from the Storefront Quick Search Results page. Validates that the Success Message is present and correct.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + </arguments> + + <scrollTo selector="{{StorefrontQuickSearchResultsSection.productByName(productName)}}" stepKey="scrollToProduct"/> + <moveMouseOver stepKey="hoverOverProduct" selector="{{StorefrontQuickSearchResultsSection.productByName(productName)}}"/> + <click selector="{{StorefrontQuickSearchResultsSection.productByName(productName)}} {{StorefrontQuickSearchResultsSection.addToCartBtn}}" stepKey="addToCart"/> + <waitForElementVisible selector="{{StorefrontQuickSearchResultsSection.messageSection}}" time="30" stepKey="waitForProductAdded"/> + <see selector="{{StorefrontQuickSearchResultsSection.messageSection}}" userInput="You added {{productName}} to your shopping cart." stepKey="seeAddedToCartMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontAdvancedCatalogSearchByDescriptionActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontAdvancedCatalogSearchByDescriptionActionGroup.xml new file mode 100644 index 0000000000000..d342909d54723 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontAdvancedCatalogSearchByDescriptionActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAdvancedCatalogSearchByDescriptionActionGroup"> + <annotations> + <description>Fills the Product Description field on the Storefront 'Advanced Search' page. Clicks on the Submit button.</description> + </annotations> + <arguments> + <argument name="description" type="string"/> + </arguments> + + <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.Description}}" userInput="{{description}}" stepKey="fill"/> + <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="clickSubmit"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontAdvancedCatalogSearchByProductNameActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontAdvancedCatalogSearchByProductNameActionGroup.xml new file mode 100644 index 0000000000000..491817dc6391c --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontAdvancedCatalogSearchByProductNameActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAdvancedCatalogSearchByProductNameActionGroup"> + <annotations> + <description>Fills the Product Name field on the Storefront 'Advanced Search' page. Clicks on the Submit button.</description> + </annotations> + <arguments> + <argument name="name" type="string"/> + </arguments> + + <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.ProductName}}" userInput="{{name}}" stepKey="fill"/> + <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="clickSubmit"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup.xml new file mode 100644 index 0000000000000..377098dfc372a --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup"> + <annotations> + <description>Fills the Product Name and Description fields on the Storefront 'Advanced Search' page. Clicks on the Submit button.</description> + </annotations> + <arguments> + <argument name="name" type="string"/> + <argument name="description" type="string"/> + </arguments> + + <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.ProductName}}" userInput="{{name}}" stepKey="fillName"/> + <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.Description}}" userInput="{{description}}" stepKey="fillDescription"/> + <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="clickSubmit"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontAdvancedCatalogSearchByProductNameAndPriceActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontAdvancedCatalogSearchByProductNameAndPriceActionGroup.xml new file mode 100644 index 0000000000000..89e454db95615 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontAdvancedCatalogSearchByProductNameAndPriceActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAdvancedCatalogSearchByProductNameAndPriceActionGroup"> + <annotations> + <description>Fills the Product Name, Price From and Price To fields on the Storefront 'Advanced Search' page. Clicks on the Submit button.</description> + </annotations> + <arguments> + <argument name="name" type="string"/> + <argument name="priceFrom" type="string"/> + <argument name="priceTo" type="string"/> + </arguments> + + <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.ProductName}}" userInput="{{name}}" stepKey="fillName"/> + <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceFrom}}" userInput="{{priceFrom}}" stepKey="fillPriceFrom"/> + <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceTo}}" userInput="{{priceTo}}" stepKey="fillPriceTo"/> + <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="clickSubmit"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontAdvancedCatalogSearchByProductSkuActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontAdvancedCatalogSearchByProductSkuActionGroup.xml new file mode 100644 index 0000000000000..328b57e8ca080 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontAdvancedCatalogSearchByProductSkuActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAdvancedCatalogSearchByProductSkuActionGroup"> + <annotations> + <description>Fills the Product SKU field on the Storefront 'Advanced Search' page. Clicks on the Submit button.</description> + </annotations> + <arguments> + <argument name="sku" type="string"/> + </arguments> + + <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.SKU}}" userInput="{{sku}}" stepKey="fill"/> + <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="clickSubmit"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontAdvancedCatalogSearchByShortDescriptionActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontAdvancedCatalogSearchByShortDescriptionActionGroup.xml new file mode 100644 index 0000000000000..28b2be68018f4 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontAdvancedCatalogSearchByShortDescriptionActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAdvancedCatalogSearchByShortDescriptionActionGroup"> + <annotations> + <description>Fills the Product Short Description field on the Storefront 'Advanced Search' page. Clicks on the Submit button.</description> + </annotations> + <arguments> + <argument name="shortDescription" type="string"/> + </arguments> + + <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.ShortDescription}}" userInput="{{shortDescription}}" stepKey="fill"/> + <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="clickSubmit"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml deleted file mode 100644 index a72762ff796e0..0000000000000 --- a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml +++ /dev/null @@ -1,250 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!-- Quick search the phrase and check if the result page contains correct information --> - <actionGroup name="StorefrontCheckQuickSearchActionGroup"> - <annotations> - <description>Submits Form POST for the Storefront Search feature. Validates that the URL is correct. Validates that the Title is present and correct.</description> - </annotations> - <arguments> - <argument name="phrase"/> - </arguments> - - <submitForm selector="{{StorefrontQuickSearchSection.searchMiniForm}}" parameterArray="['q' => {{phrase}}]" stepKey="fillQuickSearch"/> - <seeInCurrentUrl url="{{StorefrontCatalogSearchPage.url}}" stepKey="checkUrl"/> - <dontSeeInCurrentUrl url="form_key=" stepKey="checkUrlFormKey"/> - <seeInTitle userInput="Search results for: '{{phrase}}'" stepKey="assertQuickSearchTitle"/> - <see userInput="Search results for: '{{phrase}}'" selector="{{StorefrontCatalogSearchMainSection.SearchTitle}}" stepKey="assertQuickSearchName"/> - </actionGroup> - - <!-- Quick search the phrase and check if the result page contains correct information, usable with type="string" --> - <actionGroup name="StorefrontCheckQuickSearchStringActionGroup"> - <annotations> - <description>Fill the Storefront Search field. Submits the Form. Validates that the provided Search Phrase is present and correct.</description> - </annotations> - <arguments> - <argument name="phrase" type="string"/> - </arguments> - - <fillField stepKey="fillInput" selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{phrase}}"/> - <submitForm selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" parameterArray="[]" stepKey="submitQuickSearch"/> - <seeInCurrentUrl url="{{StorefrontCatalogSearchPage.url}}" stepKey="checkUrl"/> - <dontSeeInCurrentUrl url="form_key=" stepKey="checkUrlFormKey"/> - <seeInTitle userInput="Search results for: '{{phrase}}'" stepKey="assertQuickSearchTitle"/> - <see userInput="Search results for: '{{phrase}}'" selector="{{StorefrontCatalogSearchMainSection.SearchTitle}}" stepKey="assertQuickSearchName"/> - </actionGroup> - - <!-- Opens product from QuickSearch and performs assertions--> - <actionGroup name="StorefrontOpenProductFromQuickSearch"> - <annotations> - <description>Clicks on the provided Product Name from the Storefront Quick Search Results page.</description> - </annotations> - <arguments> - <argument name="productName" type="string"/> - <argument name="productUrlKey" type="string"/> - </arguments> - - <click stepKey="openProduct" selector="{{StorefrontQuickSearchResultsSection.productByName(productName)}}"/> - <waitForPageLoad stepKey="waitForProductLoad"/> - <seeInCurrentUrl url="{{productUrlKey}}" stepKey="checkUrl"/> - <see stepKey="checkName" selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{productName}}"/> - </actionGroup> - - <!-- Adds product from Quick Search page and perform assertions--> - <actionGroup name="StorefrontAddToCartFromQuickSearch"> - <annotations> - <description>Adds the provided Product Name to the Shopping Cart from the Storefront Quick Search Results page. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <argument name="productName" type="string"/> - </arguments> - - <moveMouseOver stepKey="hoverOverProduct" selector="{{StorefrontQuickSearchResultsSection.productByIndex('1')}}"/> - <click selector="{{StorefrontQuickSearchResultsSection.productByName(productName)}} {{StorefrontQuickSearchResultsSection.addToCartBtn}}" stepKey="addToCart"/> - <waitForElementVisible selector="{{StorefrontQuickSearchResultsSection.messageSection}}" time="30" stepKey="waitForProductAdded"/> - <see selector="{{StorefrontQuickSearchResultsSection.messageSection}}" userInput="You added {{productName}} to your shopping cart." stepKey="seeAddedToCartMessage"/> - </actionGroup> - - <actionGroup name="StorefrontQuickSearchCheckProductNameInGrid"> - <annotations> - <description>Validates that the provided Product Name appears at the correct index on the Storefront Quick Search page.</description> - </annotations> - <arguments> - <argument name="productName" type="string"/> - <argument name="index" type="string"/> - </arguments> - - <see selector="{{StorefrontQuickSearchResultsSection.productByIndex(index)}}" userInput="{{productName}}" stepKey="seeProductName"/> - </actionGroup> - - <actionGroup name="StorefrontQuickSearchSeeProductByName"> - <arguments> - <argument name="productName" type="string"/> - </arguments> - <see selector="{{StorefrontQuickSearchResultsSection.productByName(productName)}}" userInput="{{productName}}" stepKey="seeProductName"/> - </actionGroup> - - <actionGroup name="StorefrontQuickSearchCheckProductNameNotInGrid"> - <annotations> - <description>Validates that the provided Product Name does NOT appear on the Storefront Quick Search page.</description> - </annotations> - <arguments> - <argument name="productName" type="string"/> - </arguments> - - <dontSee selector="{{StorefrontQuickSearchResultsSection.allResults}}" userInput="{{productName}}" stepKey="dontSeeProductName"/> - </actionGroup> - - <!-- Open advanced search page --> - <actionGroup name="StorefrontOpenAdvancedSearchActionGroup"> - <annotations> - <description>Clicks on 'Advanced Search' in the Storefront Footer. Validates that the URL and Title are present and correct.</description> - </annotations> - - <click selector="{{StorefrontFooterSection.AdvancedSearch}}" stepKey="clickAdvancedSearchLink"/> - <seeInCurrentUrl url="{{StorefrontCatalogSearchAdvancedFormPage.url}}" stepKey="checkUrl"/> - <seeInTitle userInput="Advanced Search" stepKey="assertAdvancedSearchTitle1"/> - <see userInput="Advanced Search" selector="{{StorefrontCatalogSearchAdvancedFormSection.SearchTitle}}" stepKey="assertAdvancedSearchTitle2"/> - </actionGroup> - - <!-- Check that Advanced Search result page contains correct information --> - <actionGroup name="StorefrontCheckAdvancedSearchResultActionGroup"> - <annotations> - <description>Validates that the URL and Title are present and correct on the Storefront Advanced Search Results page.</description> - </annotations> - - <seeInCurrentUrl url="{{StorefrontCatalogSearchAdvancedResultPage.url}}" stepKey="checkUrl"/> - <seeInTitle userInput="Advanced Search Results" stepKey="assertAdvancedSearchTitle"/> - <see userInput="Catalog Advanced Search" selector="{{StorefrontCatalogSearchMainSection.SearchTitle}}" stepKey="assertAdvancedSearchName"/> - </actionGroup> - - <!-- Select the category in the filter --> - <actionGroup name="StorefrontSelectSearchFilterCategoryActionGroup"> - <annotations> - <description>Clicks on Category Filter. Clicks on the provided Category.</description> - </annotations> - <arguments> - <argument name="category"/> - </arguments> - - <click selector="{{StorefrontCategoryFilterSection.CategoryFilter}}" stepKey="clickCategoryFilterTitle"/> - <scrollTo selector="{{StorefrontCategoryFilterSection.CategoryByName(category.name)}}" stepKey="scrollToClickCategoryFilter"/> - <click selector="{{StorefrontCategoryFilterSection.CategoryByName(category.name)}}" stepKey="clickCategoryFilter"/> - </actionGroup> - - <!-- Go to store's advanced catalog search page --> - <actionGroup name="GoToStoreViewAdvancedCatalogSearchActionGroup"> - <annotations> - <description>Goes to the Storefront 'Advanced Search' page.</description> - </annotations> - - <amOnPage url="{{StorefrontCatalogSearchAdvancedFormPage.url}}" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> - <waitForPageLoad time="90" stepKey="waitForPageLoad"/> - </actionGroup> - - <!-- Storefront advanced catalog search by product name --> - <actionGroup name="StorefrontAdvancedCatalogSearchByProductNameActionGroup"> - <annotations> - <description>Fills the Product Name field on the Storefront 'Advanced Search' page. Clicks on the Submit button.</description> - </annotations> - <arguments> - <argument name="name" type="string"/> - </arguments> - - <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.ProductName}}" userInput="{{name}}" stepKey="fill"/> - <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="clickSubmit"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - </actionGroup> - - <!-- Storefront advanced catalog search by product sku --> - <actionGroup name="StorefrontAdvancedCatalogSearchByProductSkuActionGroup"> - <annotations> - <description>Fills the Product SKU field on the Storefront 'Advanced Search' page. Clicks on the Submit button.</description> - </annotations> - <arguments> - <argument name="sku" type="string"/> - </arguments> - - <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.SKU}}" userInput="{{sku}}" stepKey="fill"/> - <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="clickSubmit"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - </actionGroup> - - <!-- Storefront advanced catalog search by product description --> - <actionGroup name="StorefrontAdvancedCatalogSearchByDescriptionActionGroup"> - <annotations> - <description>Fills the Product Description field on the Storefront 'Advanced Search' page. Clicks on the Submit button.</description> - </annotations> - <arguments> - <argument name="description" type="string"/> - </arguments> - - <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.Description}}" userInput="{{description}}" stepKey="fill"/> - <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="clickSubmit"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - </actionGroup> - - <!-- Storefront advanced catalog search by product short description --> - <actionGroup name="StorefrontAdvancedCatalogSearchByShortDescriptionActionGroup"> - <annotations> - <description>Fills the Product Short Description field on the Storefront 'Advanced Search' page. Clicks on the Submit button.</description> - </annotations> - <arguments> - <argument name="shortDescription" type="string"/> - </arguments> - - <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.ShortDescription}}" userInput="{{shortDescription}}" stepKey="fill"/> - <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="clickSubmit"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - </actionGroup> - - <!-- Storefront advanced catalog search by product name and price --> - <actionGroup name="StorefrontAdvancedCatalogSearchByProductNameAndPriceActionGroup"> - <annotations> - <description>Fills the Product Name, Price From and Price To fields on the Storefront 'Advanced Search' page. Clicks on the Submit button.</description> - </annotations> - <arguments> - <argument name="name" type="string"/> - <argument name="priceFrom" type="string"/> - <argument name="priceTo" type="string"/> - </arguments> - - <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.ProductName}}" userInput="{{name}}" stepKey="fillName"/> - <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceFrom}}" userInput="{{priceFrom}}" stepKey="fillPriceFrom"/> - <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceTo}}" userInput="{{priceTo}}" stepKey="fillPriceTo"/> - <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="clickSubmit"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - </actionGroup> - - <!-- Storefront advanced catalog search by product name and description --> - <actionGroup name="StorefrontAdvancedCatalogSearchByProductNameAndDescriptionActionGroup"> - <annotations> - <description>Fills the Product Name and Description fields on the Storefront 'Advanced Search' page. Clicks on the Submit button.</description> - </annotations> - <arguments> - <argument name="name" type="string"/> - <argument name="description" type="string"/> - </arguments> - - <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.ProductName}}" userInput="{{name}}" stepKey="fillName"/> - <fillField selector="{{StorefrontCatalogSearchAdvancedFormSection.Description}}" userInput="{{description}}" stepKey="fillDescription"/> - <click selector="{{StorefrontCatalogSearchAdvancedFormSection.SubmitButton}}" stepKey="clickSubmit"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - </actionGroup> - - <!-- Asserts that search results do not contain any results--> - <actionGroup name="StorefrontCheckSearchIsEmpty"> - <annotations> - <description>Validates that the 'No Results' message is present and correct on the Storefront Search Results page. PLEASE NOTE: The expected message is Hardcoded.</description> - </annotations> - - <see stepKey="checkEmpty" selector="{{StorefrontQuickSearchResultsSection.messageSection}}" userInput="Your search returned no results"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchTermActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchTermActionGroup.xml deleted file mode 100644 index e1f0396b68c15..0000000000000 --- a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchTermActionGroup.xml +++ /dev/null @@ -1,45 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Verify AssertSearchTermNotOnFrontend--> - <actionGroup name="AssertSearchTermNotOnFrontend"> - <annotations> - <description>Goes to the Storefront. Fills the Search field with the provided Search Query. Clicks on Search. Validates that there are no results.</description> - </annotations> - <arguments> - <argument name="searchQuery" type="string"/> - <argument name="url_key" type="string"/> - </arguments> - - <amOnPage url="{{StorefrontProductPage.url('url_key')}}" stepKey="goToMagentoStorefrontPage"/> - <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> - <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{searchQuery}}" stepKey="fillSearchQuery"/> - <waitForPageLoad stepKey="waitForSearchTextBox"/> - <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> - <waitForPageLoad stepKey="waitForSearch"/> - <see selector="{{StorefrontMessagesSection.noticeMessage}}" userInput="Your search returned no results." stepKey="seeAssertSearchTermNotOnFrontendNoticeMessage"/> - </actionGroup> - - <actionGroup name="AssertSearchTermOnFrontend"> - <annotations> - <description>Fills the Storefront Search field with the provided Search Query. Clicks on Search. Validates that the URL is correct.</description> - </annotations> - <arguments> - <argument name="searchQuery" type="string"/> - <argument name="redirectUrl" type="string"/> - </arguments> - - <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{searchQuery}}" stepKey="fillSearchQuery"/> - <waitForPageLoad stepKey="waitForFillField"/> - <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> - <waitForPageLoad stepKey="waitForSearch"/> - <seeInCurrentUrl url="{{redirectUrl}}" stepKey="checkUrl"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCheckAdvancedSearchResultActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCheckAdvancedSearchResultActionGroup.xml new file mode 100644 index 0000000000000..e26405bbafbea --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCheckAdvancedSearchResultActionGroup.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="StorefrontCheckAdvancedSearchResultActionGroup"> + <annotations> + <description>Validates that the URL and Title are present and correct on the Storefront Advanced Search Results page.</description> + </annotations> + + <seeInCurrentUrl url="{{StorefrontCatalogSearchAdvancedResultPage.url}}" stepKey="checkUrl"/> + <seeInTitle userInput="Advanced Search Results" stepKey="assertAdvancedSearchTitle"/> + <see userInput="Catalog Advanced Search" selector="{{StorefrontCatalogSearchMainSection.SearchTitle}}" stepKey="assertAdvancedSearchName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCheckQuickSearchActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCheckQuickSearchActionGroup.xml new file mode 100644 index 0000000000000..4d97c2d37e7a1 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCheckQuickSearchActionGroup.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"> + <!-- Quick search the phrase and check if the result page contains correct information --> + <actionGroup name="StorefrontCheckQuickSearchActionGroup"> + <annotations> + <description>Submits Form POST for the Storefront Search feature. Validates that the URL is correct. Validates that the Title is present and correct.</description> + </annotations> + <arguments> + <argument name="phrase"/> + </arguments> + + <submitForm selector="{{StorefrontQuickSearchSection.searchMiniForm}}" parameterArray="['q' => {{phrase}}]" stepKey="fillQuickSearch"/> + <seeInCurrentUrl url="{{StorefrontCatalogSearchPage.url}}" stepKey="checkUrl"/> + <dontSeeInCurrentUrl url="form_key=" stepKey="checkUrlFormKey"/> + <seeInTitle userInput="Search results for: '{{phrase}}'" stepKey="assertQuickSearchTitle"/> + <see userInput="Search results for: '{{phrase}}'" selector="{{StorefrontCatalogSearchMainSection.SearchTitle}}" stepKey="assertQuickSearchName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCheckQuickSearchStringActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCheckQuickSearchStringActionGroup.xml new file mode 100644 index 0000000000000..097a02a81679d --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCheckQuickSearchStringActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCheckQuickSearchStringActionGroup"> + <annotations> + <description>Fill the Storefront Search field. Submits the Form. Validates that the provided Search Phrase is present and correct.</description> + </annotations> + <arguments> + <argument name="phrase" type="string"/> + </arguments> + + <fillField stepKey="fillInput" selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{phrase}}"/> + <submitForm selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" parameterArray="[]" stepKey="submitQuickSearch"/> + <seeInCurrentUrl url="{{StorefrontCatalogSearchPage.url}}" stepKey="checkUrl"/> + <dontSeeInCurrentUrl url="form_key=" stepKey="checkUrlFormKey"/> + <seeInTitle userInput="Search results for: '{{phrase}}'" stepKey="assertQuickSearchTitle"/> + <see userInput="Search results for: '{{phrase}}'" selector="{{StorefrontCatalogSearchMainSection.SearchTitle}}" stepKey="assertQuickSearchName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCheckSearchIsEmptyActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCheckSearchIsEmptyActionGroup.xml new file mode 100644 index 0000000000000..c1df5d029d284 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCheckSearchIsEmptyActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCheckSearchIsEmptyActionGroup"> + <annotations> + <description>Validates that the 'No Results' message is present and correct on the Storefront Search Results page. PLEASE NOTE: The expected message is Hardcoded.</description> + </annotations> + + <see stepKey="checkEmpty" selector="{{StorefrontQuickSearchResultsSection.messageSection}}" userInput="Your search returned no results"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontOpenAdvancedSearchActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontOpenAdvancedSearchActionGroup.xml new file mode 100644 index 0000000000000..84e4cb29b884d --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontOpenAdvancedSearchActionGroup.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="StorefrontOpenAdvancedSearchActionGroup"> + <annotations> + <description>Clicks on 'Advanced Search' in the Storefront Footer. Validates that the URL and Title are present and correct.</description> + </annotations> + + <click selector="{{StorefrontFooterSection.AdvancedSearch}}" stepKey="clickAdvancedSearchLink"/> + <seeInCurrentUrl url="{{StorefrontCatalogSearchAdvancedFormPage.url}}" stepKey="checkUrl"/> + <seeInTitle userInput="Advanced Search" stepKey="assertAdvancedSearchTitle1"/> + <see userInput="Advanced Search" selector="{{StorefrontCatalogSearchAdvancedFormSection.SearchTitle}}" stepKey="assertAdvancedSearchTitle2"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontOpenProductFromQuickSearchActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontOpenProductFromQuickSearchActionGroup.xml new file mode 100644 index 0000000000000..348c5dabc65e0 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontOpenProductFromQuickSearchActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontOpenProductFromQuickSearchActionGroup"> + <annotations> + <description>Clicks on the provided Product Name from the Storefront Quick Search Results page.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + <argument name="productUrlKey" type="string"/> + </arguments> + + <scrollTo selector="{{StorefrontQuickSearchResultsSection.productByName(productName)}}" stepKey="scrollToProduct"/> + <click stepKey="openProduct" selector="{{StorefrontQuickSearchResultsSection.productByName(productName)}}"/> + <waitForPageLoad stepKey="waitForProductLoad"/> + <seeInCurrentUrl url="{{productUrlKey}}" stepKey="checkUrl"/> + <see stepKey="checkName" selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{productName}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontQuickSearchCheckProductNameInGridActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontQuickSearchCheckProductNameInGridActionGroup.xml new file mode 100644 index 0000000000000..4f68d9b004c79 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontQuickSearchCheckProductNameInGridActionGroup.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="StorefrontQuickSearchCheckProductNameInGridActionGroup"> + <annotations> + <description>Validates that the provided Product Name appears at the correct index on the Storefront Quick Search page.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + <argument name="index" type="string"/> + </arguments> + + <see selector="{{StorefrontQuickSearchResultsSection.productByIndex(index)}}" userInput="{{productName}}" stepKey="seeProductName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontQuickSearchCheckProductNameNotInGridActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontQuickSearchCheckProductNameNotInGridActionGroup.xml new file mode 100644 index 0000000000000..493b266e2c368 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontQuickSearchCheckProductNameNotInGridActionGroup.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="StorefrontQuickSearchCheckProductNameNotInGridActionGroup"> + <annotations> + <description>Validates that the provided Product Name does NOT appear on the Storefront Quick Search page.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + </arguments> + + <dontSee selector="{{StorefrontQuickSearchResultsSection.allResults}}" userInput="{{productName}}" stepKey="dontSeeProductName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontQuickSearchRelatedSearchTermsAppearsActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontQuickSearchRelatedSearchTermsAppearsActionGroup.xml new file mode 100644 index 0000000000000..00a4f3e5d3917 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontQuickSearchRelatedSearchTermsAppearsActionGroup.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="StorefrontQuickSearchRelatedSearchTermsAppearsActionGroup"> + <arguments> + <argument name="term" type="string"/> + </arguments> + + <waitForElementVisible selector="{{StorefrontCatalogSearchMainSection.relatedSearchTermsTitle}}" stepKey="waitMessageAppears"/> + <see selector="{{StorefrontCatalogSearchMainSection.relatedSearchTermsTitle}}" userInput="Related search terms" stepKey="checkRelatedTermsTitle"/> + <see selector="{{StorefrontCatalogSearchMainSection.relatedSearchTerm}}" userInput="{{term}}" stepKey="checkRelatedTermExists"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontQuickSearchSeeProductByNameActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontQuickSearchSeeProductByNameActionGroup.xml new file mode 100644 index 0000000000000..a900dfce9c1fa --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontQuickSearchSeeProductByNameActionGroup.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="StorefrontQuickSearchSeeProductByNameActionGroup"> + <arguments> + <argument name="productName" type="string"/> + </arguments> + <see selector="{{StorefrontQuickSearchResultsSection.productByName(productName)}}" userInput="{{productName}}" stepKey="seeProductName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontQuickSearchTooShortStringActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontQuickSearchTooShortStringActionGroup.xml new file mode 100644 index 0000000000000..58ef412410c74 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontQuickSearchTooShortStringActionGroup.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="StorefrontQuickSearchTooShortStringActionGroup" extends="StorefrontCheckQuickSearchStringActionGroup"> + <annotations> + <description>Fill the Storefront Search field. Submits the Form. Validates that 'Minimum Search query length' warning appears.</description> + </annotations> + <arguments> + <argument name="minQueryLength" type="string"/> + </arguments> + + <see selector="{{StorefrontQuickSearchResultsSection.messageSection}}" userInput="Minimum Search query length is {{minQueryLength}}" stepKey="assertQuickSearchNeedThreeOrMoreChars"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontSelectSearchFilterCategoryActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontSelectSearchFilterCategoryActionGroup.xml new file mode 100644 index 0000000000000..3048217f5dfda --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontSelectSearchFilterCategoryActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontSelectSearchFilterCategoryActionGroup"> + <annotations> + <description>Clicks on Category Filter. Clicks on the provided Category.</description> + </annotations> + <arguments> + <argument name="category"/> + </arguments> + + <click selector="{{StorefrontCategoryFilterSection.CategoryFilter}}" stepKey="clickCategoryFilterTitle"/> + <scrollTo selector="{{StorefrontCategoryFilterSection.CategoryByName(category.name)}}" stepKey="scrollToClickCategoryFilter"/> + <click selector="{{StorefrontCategoryFilterSection.CategoryByName(category.name)}}" stepKey="clickCategoryFilter"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Data/CatalogSearchData.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Data/CatalogSearchData.xml index 6868456079110..9dab06ffb14f0 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Data/CatalogSearchData.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Data/CatalogSearchData.xml @@ -23,5 +23,10 @@ <entity name="SetMinQueryLengthToOne" type="number"> <data key="value">1</data> </entity> - + <entity name="SetCatalogSearchEngineToDefault" type="catalog_search_engine_default"> + <requiredEntity type="enable">DefaultCatalogSearchEngine</requiredEntity> + </entity> + <entity name="DefaultCatalogSearchEngine" type="enable"> + <data key="inherit">true</data> + </entity> </entities> \ No newline at end of file diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Data/ConfigData.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Data/ConfigData.xml new file mode 100644 index 0000000000000..dd8c426592619 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Data/ConfigData.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="MinimalQueryLengthDefaultConfigData"> + <data key="path">catalog/search/min_query_length</data> + <data key="value">3</data> + </entity> + <entity name="MinimalQueryLengthFourConfigData"> + <data key="path">catalog/search/min_query_length</data> + <data key="value">4</data> + </entity> +</entities> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml index 7405377249aa4..ce869f81a23df 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Metadata/catalog_search-meta.xml @@ -29,4 +29,15 @@ </object> </object> </operation> + <operation name="CatalogSearchEngineDefault" dataType="catalog_search_engine_default" type="create" auth="adminFormKey" url="/admin/system_config/save/section/catalog/" method="POST"> + <object key="groups" dataType="catalog_search_engine_default"> + <object key="search" dataType="catalog_search_engine_default"> + <object key="fields" dataType="catalog_search_engine_default"> + <object key="engine" dataType="enable"> + <field key="inherit">boolean</field> + </object> + </object> + </object> + </object> + </operation> </operations> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchMainSection.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchMainSection.xml index 667f08fea6579..b005e100b30bb 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchMainSection.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchMainSection.xml @@ -16,5 +16,7 @@ <element name="productCount" type="text" selector="#toolbar-amount"/> <element name="message" type="text" selector="div.message div"/> <element name="searchResults" type="block" selector="#maincontent .column.main"/> + <element name="relatedSearchTermsTitle" type="text" selector="div.message dl.block dt.title"/> + <element name="relatedSearchTerm" type="text" selector="div.message dl.block dd.item a"/> </section> </sections> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml index 2b425f34f8a5b..86a1d4321cba0 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminCreateSearchTermEntityTest.xml @@ -27,7 +27,7 @@ </before> <after> <!-- Delete created search term --> - <actionGroup ref="AssertSearchTermSuccessDeleteMessage" stepKey="deleteSearchTerm"> + <actionGroup ref="AssertSearchTermSuccessDeleteMessageActionGroup" stepKey="deleteSearchTerm"> <argument name="searchQuery" value="$$createSimpleProduct.sku$$"/> </actionGroup> @@ -39,7 +39,7 @@ </after> <!-- Go to the search terms page and create new search term --> - <actionGroup ref="AssertSearchTermSaveSuccessMessage" stepKey="createNewSearchTerm"> + <actionGroup ref="AssertSearchTermSaveSuccessMessageActionGroup" stepKey="createNewSearchTerm"> <argument name="searchQuery" value="$$createSimpleProduct.sku$$"/> <argument name="storeValue" value="{{SimpleTerm.store_id}}"/> <argument name="redirectUrl" value="{{SimpleTerm.redirect}}"/> @@ -51,7 +51,7 @@ <waitForPageLoad stepKey="waitForPageLoad"/> <!-- Assert created search term on storefront --> - <actionGroup ref="AssertSearchTermOnFrontend" stepKey="assertCreatedSearchTermOnFrontend"> + <actionGroup ref="AssertSearchTermOnFrontendActionGroup" stepKey="assertCreatedSearchTermOnFrontend"> <argument name="searchQuery" value="$$createSimpleProduct.sku$$"/> <argument name="redirectUrl" value="{{SimpleTerm.redirect}}"/> </actionGroup> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml index c72ed424ef307..b449d92d8e003 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/AdminDeleteSearchTermTest.xml @@ -33,7 +33,7 @@ </after> <!--Add new search term--> - <actionGroup ref="AssertSearchTermSaveSuccessMessage" stepKey="addNewSearchTerm"> + <actionGroup ref="AssertSearchTermSaveSuccessMessageActionGroup" stepKey="addNewSearchTerm"> <argument name="searchQuery" value="{{SimpleTerm.search_query}}"/> <argument name="storeValue" value="{{SimpleTerm.store_id}}"/> <argument name="redirectUrl" value="{{SimpleTerm.redirect}}"/> @@ -41,19 +41,19 @@ </actionGroup> <!--Search and delete search term and AssertSearchTermSuccessDeleteMessage--> - <actionGroup ref="AssertSearchTermSuccessDeleteMessage" stepKey="deleteSearchTerm"> + <actionGroup ref="AssertSearchTermSuccessDeleteMessageActionGroup" stepKey="deleteSearchTerm"> <argument name="searchQuery" value="{{SimpleTerm.search_query}}"/> </actionGroup> <!--Verify deleted search term in grid and AssertSearchTermNotInGrid--> - <actionGroup ref="AssertSearchTermNotInGrid" stepKey="verifyDeletedSearchTermNotInGrid"> + <actionGroup ref="AssertSearchTermNotInGridActionGroup" stepKey="verifyDeletedSearchTermNotInGrid"> <argument name="searchQuery" value="{{SimpleTerm.search_query}}"/> </actionGroup> <!--Go to storefront and Verify AssertSearchTermNotOnFrontend--> - <actionGroup ref="AssertSearchTermNotOnFrontend" stepKey="verifySearchTermNotOnFrontend"> + <actionGroup ref="AssertSearchTermNotOnFrontendActionGroup" stepKey="verifySearchTermNotOnFrontend"> <argument name="searchQuery" value="{{SimpleTerm.search_query}}"/> <argument name="url_key" value="$$simpleProduct.custom_attributes[url_key]$$"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index aa7cf933f6328..57940e39e9281 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -23,7 +23,7 @@ <waitForLoadingMaskToDisappear stepKey="waitForSearchProductsloaded" after="searchClickAdvancedSearchSubmitButton"/> <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="searchCheckAdvancedSearchResult" after="waitForSearchProductsloaded"/> <see userInput="4" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productCount}} span" stepKey="searchAdvancedAssertProductCount" after="searchCheckAdvancedSearchResult"/> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="searchAssertSimpleProduct1" after="searchAdvancedAssertProductCount"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertSimpleProduct1" after="searchAdvancedAssertProductCount"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -31,7 +31,7 @@ <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchAdvancedGrabSimpleProduct1ImageSrc" stepKey="searchAdvancedAssertSimpleProduct1ImageNotDefault" after="searchAdvancedGrabSimpleProduct1ImageSrc"/> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="searchClickSimpleProduct1View" after="searchAdvancedAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForSearchSimpleProduct1Viewloaded" after="searchClickSimpleProduct1View"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="searchAssertSimpleProduct1Page" after="waitForSearchSimpleProduct1Viewloaded"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="searchAssertSimpleProduct1Page" after="waitForSearchSimpleProduct1Viewloaded"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -51,14 +51,14 @@ <!-- Search simple product 1 --> <comment userInput="Search simple product 1" stepKey="commentSearchSimpleProduct1" after="searchAssertFilterCategoryProductCountCommonPart"/> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="searchAssertFilterCategorySimpleProduct1" after="commentSearchSimpleProduct1"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct1" after="commentSearchSimpleProduct1"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct1ImageSrc" after="searchAssertFilterCategorySimpleProduct1"/> <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabSimpleProduct1ImageSrc" stepKey="searchAssertSimpleProduct1ImageNotDefault" after="searchGrabSimpleProduct1ImageSrc"/> <!-- Search simple product2 --> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="searchAssertFilterCategorySimpleProduct2" after="searchAssertSimpleProduct1ImageNotDefault"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct2" after="searchAssertSimpleProduct1ImageNotDefault"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -89,7 +89,7 @@ <waitForLoadingMaskToDisappear stepKey="waitForSearchProductsloaded" after="searchClickAdvancedSearchSubmitButton"/> <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="searchCheckAdvancedSearchResult" after="waitForSearchProductsloaded"/> <see userInput="1" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productCount}} span" stepKey="searchAdvancedAssertProductCount" after="searchCheckAdvancedSearchResult"/> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="searchAssertSimpleProduct1" after="searchAdvancedAssertProductCount"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertSimpleProduct1" after="searchAdvancedAssertProductCount"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -97,7 +97,7 @@ <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchAdvancedGrabSimpleProduct1ImageSrc" stepKey="searchAdvancedAssertSimpleProduct1ImageNotDefault" after="searchAdvancedGrabSimpleProduct1ImageSrc"/> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="searchClickSimpleProduct1View" after="searchAdvancedAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForSearchSimpleProduct1Viewloaded" after="searchClickSimpleProduct1View"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="searchAssertSimpleProduct1Page" after="waitForSearchSimpleProduct1Viewloaded"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="searchAssertSimpleProduct1Page" after="waitForSearchSimpleProduct1Viewloaded"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -117,14 +117,14 @@ <!-- Search simple product 1 --> <comment userInput="Search simple product 1" stepKey="commentSearchSimpleProduct1" after="searchAssertFilterCategoryProductCountCommonPart"/> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="searchAssertFilterCategorySimpleProduct1" after="commentSearchSimpleProduct1"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct1" after="commentSearchSimpleProduct1"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct1ImageSrc" after="searchAssertFilterCategorySimpleProduct1"/> <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabSimpleProduct1ImageSrc" stepKey="searchAssertSimpleProduct1ImageNotDefault" after="searchGrabSimpleProduct1ImageSrc"/> <!-- Search simple product2 --> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="searchAssertFilterCategorySimpleProduct2" after="searchAssertSimpleProduct1ImageNotDefault"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct2" after="searchAssertSimpleProduct1ImageNotDefault"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index 367cb6a6e214e..cd3dec912a3c1 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -23,7 +23,7 @@ <waitForLoadingMaskToDisappear stepKey="waitForSearchProductsloaded" after="searchClickAdvancedSearchSubmitButton"/> <actionGroup ref="StorefrontCheckAdvancedSearchResultActionGroup" stepKey="searchCheckAdvancedSearchResult" after="waitForSearchProductsloaded"/> <see userInput="1" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productCount}} span" stepKey="searchAdvancedAssertProductCount" after="searchCheckAdvancedSearchResult"/> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="searchAssertSimpleProduct1" after="searchAdvancedAssertProductCount"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertSimpleProduct1" after="searchAdvancedAssertProductCount"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -31,7 +31,7 @@ <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchAdvancedGrabSimpleProduct1ImageSrc" stepKey="searchAdvancedAssertSimpleProduct1ImageNotDefault" after="searchAdvancedGrabSimpleProduct1ImageSrc"/> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="searchClickSimpleProduct1View" after="searchAdvancedAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForSearchSimpleProduct1Viewloaded" after="searchClickSimpleProduct1View"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="searchAssertSimpleProduct1Page" after="waitForSearchSimpleProduct1Viewloaded"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="searchAssertSimpleProduct1Page" after="waitForSearchSimpleProduct1Viewloaded"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -51,14 +51,14 @@ <!-- Search simple product 1 --> <comment userInput="Search simple product 1" stepKey="commentSearchSimpleProduct1" after="searchAssertFilterCategoryProductCountCommonPart"/> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="searchAssertFilterCategorySimpleProduct1" after="commentSearchSimpleProduct1"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct1" after="commentSearchSimpleProduct1"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createSimpleProduct1.name$$)}}" userInput="src" stepKey="searchGrabSimpleProduct1ImageSrc" after="searchAssertFilterCategorySimpleProduct1"/> <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabSimpleProduct1ImageSrc" stepKey="searchAssertSimpleProduct1ImageNotDefault" after="searchGrabSimpleProduct1ImageSrc"/> <!-- Search simple product2 --> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="searchAssertFilterCategorySimpleProduct2" after="searchAssertSimpleProduct1ImageNotDefault"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="searchAssertFilterCategorySimpleProduct2" after="searchAssertSimpleProduct1ImageNotDefault"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml index c8f84c732d6ba..fa9407495e6e7 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/LayerNavigationOfCatalogSearchTest.xml @@ -42,7 +42,7 @@ </after> <!--Update value for price attribute of Product 1--> <comment userInput="Update value for price attribute of Product 1" stepKey="comment1"/> - <actionGroup ref="navigateToCreatedProductEditPage" stepKey="navigateToCreatedProductEditPage1"> + <actionGroup ref="NavigateToCreatedProductEditPageActionGroup" stepKey="navigateToCreatedProductEditPage1"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <grabTextFrom selector="{{AdminProductFormSection.attributeLabelByText($$createPriceAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="grabAttributeLabel"/> @@ -51,7 +51,7 @@ <waitForPageLoad stepKey="waitForSimpleProductSaved1"/> <!--Update value for price attribute of Product 2--> <comment userInput="Update value for price attribute of Product 1" stepKey="comment2"/> - <actionGroup ref="navigateToCreatedProductEditPage" stepKey="navigateToCreatedProductEditPage2"> + <actionGroup ref="NavigateToCreatedProductEditPageActionGroup" stepKey="navigateToCreatedProductEditPage2"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <fillField selector="{{AdminProductAttributeSection.customAttribute($$createPriceAttribute.attribute_code$$)}}" userInput="70" stepKey="fillCustomPrice2"/> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml index 90b13bd1b6b4f..cc6f202272e3b 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/SearchEntityResultsTest.xml @@ -36,7 +36,7 @@ <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> <argument name="phrase" value="$createSimpleProduct.sku$"/> </actionGroup> - <actionGroup ref="StorefrontOpenProductFromQuickSearch" stepKey="openAndCheckProduct"> + <actionGroup ref="StorefrontOpenProductFromQuickSearchActionGroup" stepKey="openAndCheckProduct"> <argument name="productName" value="$createSimpleProduct.name$"/> <argument name="productUrlKey" value="$createSimpleProduct.custom_attributes[url_key]$"/> </actionGroup> @@ -78,6 +78,7 @@ </test> <test name="QuickSearchEmptyResults"> <annotations> + <features value="CatalogSearch"/> <stories value="Search Product on Storefront"/> <title value="User should not get search results on query that doesn't return anything"/> <description value="Use invalid query to return no products"/> @@ -97,17 +98,20 @@ <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> - <deleteData stepKey="deleteProduct" createDataKey="createSimpleProduct"/> - <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> </after> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> <argument name="phrase" value="ThisShouldn'tReturnAnything"/> </actionGroup> - <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmpty"/> + <actionGroup ref="StorefrontCheckSearchIsEmptyActionGroup" stepKey="checkEmpty"/> </test> + <test name="QuickSearchWithTwoCharsEmptyResults" extends="QuickSearchEmptyResults"> <annotations> + <features value="CatalogSearch"/> <stories value="Search Product on Storefront"/> <title value="User should not get search results on query that only contains two characters"/> <description value="Use of 2 character query to return no products"/> @@ -115,15 +119,30 @@ <testCaseId value="MC-14794"/> <group value="CatalogSearch"/> <group value="mtf_migrated"/> - <skip> - <issueId value="MC-15827"/> - </skip> </annotations> - <executeJS function="var s = '$createSimpleProduct.name$'; var ret=s.substring(0,2); return ret;" stepKey="getFirstTwoLetters" before="searchStorefront"/> - <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="{$getFirstTwoLetters}"/> + + <before> + <magentoCLI command="config:set {{MinimalQueryLengthFourConfigData.path}} {{MinimalQueryLengthFourConfigData.value}}" after="createSimpleProduct" stepKey="setMinimalQueryLengthToFour"/> + </before> + + <after> + <magentoCLI command="config:set {{MinimalQueryLengthDefaultConfigData.path}} {{MinimalQueryLengthDefaultConfigData.value}}" after="deleteCategory" stepKey="setMinimalQueryLengthToFour"/> + </after> + + <executeJS function="var s = '$createSimpleProduct.name$'; var ret=s.substring(0,{{MinimalQueryLengthFourConfigData.value}} - 1); return ret;" before="searchStorefront" stepKey="getFirstLessThenConfigLetters"/> + + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" after="checkEmpty" stepKey="searchStorefrontConfigLetters"> + <argument name="phrase" value="$createSimpleProduct.name$"/> + </actionGroup> + <actionGroup ref="StorefrontQuickSearchTooShortStringActionGroup" after="searchStorefrontConfigLetters" stepKey="checkCannotSearchWithTooShortString"> + <argument name="phrase" value="$getFirstLessThenConfigLetters"/> + <argument name="minQueryLength" value="{{MinimalQueryLengthFourConfigData.value}}"/> + </actionGroup> + <actionGroup ref="StorefrontQuickSearchRelatedSearchTermsAppearsActionGroup" after="checkCannotSearchWithTooShortString" stepKey="checkRelatedSearchTerm"> + <argument name="term" value="$createSimpleProduct.name$"/> </actionGroup> </test> + <test name="QuickSearchProductByNameWithThreeLetters" extends="QuickSearchProductBySku"> <annotations> <stories value="Search Product on Storefront"/> @@ -184,47 +203,47 @@ <!-- Create and Assign Attribute to product1--> - <actionGroup ref="goToProductPageViaID" stepKey="goToProduct1"> + <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToProduct1"> <argument name="productId" value="$product1.id$"/> </actionGroup> - <actionGroup ref="AdminCreateAttributeWithSearchWeight" stepKey="createProduct1Attribute"> + <actionGroup ref="AdminCreateAttributeWithSearchWeightActionGroup" stepKey="createProduct1Attribute"> <argument name="attributeType" value="Text Field"/> <argument name="attributeName" value="$product1.name$"/> <argument name="attributeSetName" value="$product1.name$"/> <argument name="weight" value="1"/> <argument name="defaultValue" value="{{_defaultProduct.name}}"/> </actionGroup> - <actionGroup ref="AdminProductPageSelectAttributeSet" stepKey="selectAttributeSet1"> + <actionGroup ref="AdminProductPageSelectAttributeSetActionGroup" stepKey="selectAttributeSet1"> <argument name="attributeSetName" value="$product1.name$"/> </actionGroup> <!--fill in default--> - <actionGroup ref="saveProductForm" stepKey="saveProduct1a"/> - <actionGroup ref="AdminProductPageFillTextAttributeValueByName" stepKey="fillDefault1"> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1a"/> + <actionGroup ref="AdminProductPageFillTextAttributeValueByNameActionGroup" stepKey="fillDefault1"> <argument name="attributeName" value="$product1.name$"/> <argument name="value" value="{{_defaultProduct.name}}"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct1b"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1b"/> <!-- Create and Assign Attribute to product2--> - <actionGroup ref="goToProductPageViaID" stepKey="goToProduct2"> + <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToProduct2"> <argument name="productId" value="$product2.id$"/> </actionGroup> - <actionGroup ref="AdminCreateAttributeWithSearchWeight" stepKey="createProduct2Attribute"> + <actionGroup ref="AdminCreateAttributeWithSearchWeightActionGroup" stepKey="createProduct2Attribute"> <argument name="attributeType" value="Text Field"/> <argument name="attributeName" value="$product2.name$"/> <argument name="attributeSetName" value="$product2.name$"/> <argument name="weight" value="1"/> <argument name="defaultValue" value="{{_defaultProduct.name}}"/> </actionGroup> - <actionGroup ref="AdminProductPageSelectAttributeSet" stepKey="selectAttributeSet2"> + <actionGroup ref="AdminProductPageSelectAttributeSetActionGroup" stepKey="selectAttributeSet2"> <argument name="attributeSetName" value="$product2.name$"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct2a"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct2a"/> <!--fill in default--> - <actionGroup ref="AdminProductPageFillTextAttributeValueByName" stepKey="fillDefault2"> + <actionGroup ref="AdminProductPageFillTextAttributeValueByNameActionGroup" stepKey="fillDefault2"> <argument name="attributeName" value="$product2.name$"/> <argument name="value" value="{{_defaultProduct.name}}"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct2b"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct2b"/> </before> <after> <deleteData stepKey="deleteProduct1" createDataKey="product1"/> @@ -235,11 +254,11 @@ <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> <argument name="phrase" value="{{_defaultProduct.name}}"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchCheckProductNameInGrid" stepKey="assertProduct1Position"> + <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="assertProduct1Position"> <argument name="productName" value="$product1.name$"/> <argument name="index" value="2"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchCheckProductNameInGrid" stepKey="assertProduct2Position"> + <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="assertProduct2Position"> <argument name="productName" value="$product2.name$"/> <argument name="index" value="1"/> </actionGroup> @@ -256,7 +275,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref="AdminCreateAttributeWithSearchWeight" stepKey="createProduct1Attribute"> + <actionGroup ref="AdminCreateAttributeWithSearchWeightActionGroup" stepKey="createProduct1Attribute"> <argument name="attributeType" value="Text Field"/> <argument name="attributeName" value="$product1.name$"/> <argument name="attributeSetName" value="$product1.name$"/> @@ -264,11 +283,11 @@ <argument name="defaultValue" value="{{_defaultProduct.name}}"/> </actionGroup> </before> - <actionGroup ref="StorefrontQuickSearchCheckProductNameInGrid" stepKey="assertProduct1Position"> + <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="assertProduct1Position"> <argument name="productName" value="$product1.name$"/> <argument name="index" value="1"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchCheckProductNameInGrid" stepKey="assertProduct2Position"> + <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="assertProduct2Position"> <argument name="productName" value="$product2.name$"/> <argument name="index" value="2"/> </actionGroup> @@ -283,15 +302,16 @@ <testCaseId value="MC-14784"/> <group value="CatalogSearch"/> <group value="mtf_migrated"/> - <skip> - <issueId value="MC-21717"/> - </skip> </annotations> <before> <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="_defaultProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="createCategory"/> </createData> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> <deleteData stepKey="deleteProduct" createDataKey="createSimpleProduct"/> @@ -301,7 +321,7 @@ <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> <argument name="phrase" value="$createSimpleProduct.name$"/> </actionGroup> - <actionGroup ref="StorefrontAddToCartFromQuickSearch" stepKey="addProductToCart"> + <actionGroup ref="StorefrontAddToCartFromQuickSearchActionGroup" stepKey="addProductToCart"> <argument name="productName" value="$createSimpleProduct.name$"/> </actionGroup> </test> @@ -333,7 +353,7 @@ <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> <argument name="phrase" value="$createVirtualProduct.name$"/> </actionGroup> - <actionGroup ref="StorefrontAddToCartFromQuickSearch" stepKey="addProductToCart"> + <actionGroup ref="StorefrontAddToCartFromQuickSearchActionGroup" stepKey="addProductToCart"> <argument name="productName" value="$createVirtualProduct.name$"/> </actionGroup> </test> @@ -350,7 +370,7 @@ <before> <createData entity="_defaultCategory" stepKey="createCategory"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> <argument name="product" value="_defaultProduct"/> <argument name="category" value="$$createCategory$$"/> </actionGroup> @@ -361,19 +381,21 @@ </before> <after> <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> - <actionGroup ref="deleteProductBySku" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> <argument name="sku" value="{{_defaultProduct.sku}}"/> </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> <argument name="phrase" value="{{_defaultProduct.name}}"/> </actionGroup> - <actionGroup ref="StorefrontOpenProductFromQuickSearch" stepKey="openAndCheckProduct"> + <actionGroup ref="StorefrontOpenProductFromQuickSearchActionGroup" stepKey="openAndCheckProduct"> <argument name="productName" value="{{_defaultProduct.name}}"/> <argument name="productUrlKey" value="{{_defaultProduct.urlKey}}"/> </actionGroup> - <actionGroup ref="SelectSingleAttributeAndAddToCart" stepKey="addProductToCart"> + <actionGroup ref="SelectSingleAttributeAndAddToCartActionGroup" stepKey="addProductToCart"> <argument name="productName" value="{{_defaultProduct.name}}"/> <argument name="attributeCode" value="{{colorProductAttribute.default_label}}"/> <argument name="optionName" value="{{colorProductAttribute1.name}}"/> @@ -412,7 +434,7 @@ <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> <argument name="phrase" value="$createProduct.name$"/> </actionGroup> - <actionGroup ref="StorefrontAddToCartFromQuickSearch" stepKey="addProductToCart"> + <actionGroup ref="StorefrontAddToCartFromQuickSearchActionGroup" stepKey="addProductToCart"> <argument name="productName" value="$createProduct.name$"/> </actionGroup> </test> @@ -427,6 +449,8 @@ <group value="mtf_migrated"/> </annotations> <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> <createData entity="ApiProductWithDescription" stepKey="simple1"/> <createData entity="ApiGroupedProduct" stepKey="createProduct"/> <createData entity="OneSimpleProductLink" stepKey="addProductOne"> @@ -439,13 +463,15 @@ <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> - <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> + <deleteData stepKey="deleteGroupedProduct" createDataKey="createProduct"/> + <deleteData stepKey="deleteSimpleProduct" createDataKey="simple1"/> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> - <argument name="phrase" value="$createProduct.name$"/> + <argument name="phrase" value=""$createProduct.name$""/> </actionGroup> - <actionGroup ref="StorefrontAddToCartFromQuickSearch" stepKey="addProductToCart"> + <actionGroup ref="StorefrontAddToCartFromQuickSearchActionGroup" stepKey="addProductToCart"> <argument name="productName" value="$createProduct.name$"/> </actionGroup> </test> @@ -481,7 +507,7 @@ <!--Finish bundle creation--> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <amOnPage url="{{AdminProductEditPage.url($$createBundleProduct.id$$)}}" stepKey="goToProductEditPage"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Perform reindex and flush cache --> <magentoCLI command="indexer:reindex" stepKey="reindex"/> @@ -491,12 +517,13 @@ <deleteData stepKey="deleteBundleProduct" createDataKey="createBundleProduct"/> <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> <argument name="phrase" value="$createBundleProduct.name$"/> </actionGroup> - <actionGroup ref="StorefrontOpenProductFromQuickSearch" stepKey="openAndCheckProduct"> + <actionGroup ref="StorefrontOpenProductFromQuickSearchActionGroup" stepKey="openAndCheckProduct"> <argument name="productName" value="$createBundleProduct.name$"/> <argument name="productUrlKey" value="$createBundleProduct.custom_attributes[url_key]$"/> </actionGroup> @@ -547,7 +574,7 @@ <!--Finish bundle creation--> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <amOnPage url="{{AdminProductEditPage.url($$createBundleProduct.id$$)}}" stepKey="goToProductEditPage"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Perform reindex and flush cache --> <magentoCLI command="indexer:reindex" stepKey="reindex"/> @@ -556,18 +583,21 @@ <after> <deleteData stepKey="deleteBundleProduct" createDataKey="createBundleProduct"/> <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> <comment userInput="$simpleProduct1.name$" stepKey="asdf"/> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPage"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> <argument name="phrase" value="$createBundleProduct.name$"/> </actionGroup> - <actionGroup ref="StorefrontOpenProductFromQuickSearch" stepKey="openAndCheckProduct"> + <actionGroup ref="StorefrontOpenProductFromQuickSearchActionGroup" stepKey="openAndCheckProduct"> <argument name="productName" value="$createBundleProduct.name$"/> <argument name="productUrlKey" value="$createBundleProduct.custom_attributes[url_key]$"/> </actionGroup> - <actionGroup ref="StorefrontAddBundleProductFromProductToCartWithMultiOption" stepKey="addProductToCart"> + <actionGroup ref="StorefrontAddBundleProductFromProductToCartWithMultiOptionActionGroup" stepKey="addProductToCart"> <argument name="productName" value="$createBundleProduct.name$"/> <argument name="optionName" value="$createBundleOption1_1.name$"/> <argument name="value" value="$simpleProduct1.name$"/> @@ -603,14 +633,14 @@ <!-- Assign attribute to set --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <actionGroup ref="goToAttributeGridPage" stepKey="goToPage"/> - <actionGroup ref="goToAttributeSetByName" stepKey="goToSet"> + <actionGroup ref="GoToAttributeSetByNameActionGroup" stepKey="goToSet"> <argument name="name" value="$attributeSet.attribute_set_name$"/> </actionGroup> - <actionGroup ref="AssignAttributeToGroup" stepKey="assignToAttributeSetAndGroup"> + <actionGroup ref="AssignAttributeToGroupActionGroup" stepKey="assignToAttributeSetAndGroup"> <argument name="group" value="Product Details"/> <argument name="attribute" value="$createConfigProductAttribute.attribute_code$"/> </actionGroup> - <actionGroup ref="SaveAttributeSet" stepKey="savePage"/> + <actionGroup ref="SaveAttributeSetActionGroup" stepKey="savePage"/> <!-- Get the first option of the attribute we created --> <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> @@ -650,23 +680,23 @@ <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefront"> <argument name="phrase" value="$createConfigProduct.name$"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchCheckProductNameInGrid" stepKey="seeProductInGrid"> + <actionGroup ref="StorefrontQuickSearchCheckProductNameInGridActionGroup" stepKey="seeProductInGrid"> <argument name="productName" value="$createConfigProduct.name$"/> <argument name="index" value="1"/> </actionGroup> <!-- Disable Child Product --> - <actionGroup ref="goToProductPageViaID" stepKey="goToChildProduct"> + <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToChildProduct"> <argument name="productId" value="$createConfigChildProduct1.id$"/> </actionGroup> - <actionGroup ref="toggleProductEnabled" stepKey="disableProduct"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="ToggleProductEnabledActionGroup" stepKey="disableProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontPageAgain"/> <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="searchStorefrontAgain"> <argument name="phrase" value="$createConfigProduct.name$"/> </actionGroup> - <actionGroup ref="StorefrontQuickSearchCheckProductNameNotInGrid" stepKey="dontSeeProductAnymore"> + <actionGroup ref="StorefrontQuickSearchCheckProductNameNotInGridActionGroup" stepKey="dontSeeProductAnymore"> <argument name="productName" value="$createConfigProduct.name$"/> </actionGroup> </test> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPriceToTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPriceToTest.xml index 755bb92c897ea..33dff8aefa334 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPriceToTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchByPriceToTest.xml @@ -27,7 +27,8 @@ <actionGroup ref="StorefrontFillFormAdvancedSearchActionGroup" stepKey="search"> <argument name="price_to" value="100"/> </actionGroup> - <see userInput="2 items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> + <!-- See that some items were found, other products may exist besides our test products --> + <see userInput="items" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.itemFound}}" stepKey="see"/> <see userInput="$$createProduct2.name$$" selector="{{StorefrontCatalogSearchAdvancedResultMainSection.productName}}" stepKey="seeProduct2Name" after="seeProductName"/> </test> </tests> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchEntitySimpleProductTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchEntitySimpleProductTest.xml index 78110b531be33..61f274cd13061 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchEntitySimpleProductTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontAdvancedSearchEntitySimpleProductTest.xml @@ -19,12 +19,16 @@ <group value="mtf_migrated"/> </annotations> <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- Delete all products left by prev tests because it sensitive for search--> + <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> <!-- Create Data --> <createData entity="ABC_dfj_SimpleProduct" stepKey="createProduct"/> </before> <after> <!-- Delete data --> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <actionGroup ref="logout" stepKey="logoutAdmin"/> </after> <!-- Perform reindex and flush cache --> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontUpdateSearchTermEntityTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontUpdateSearchTermEntityTest.xml index 3ebb09f3c9c26..6c475ddc60a95 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontUpdateSearchTermEntityTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontUpdateSearchTermEntityTest.xml @@ -18,7 +18,7 @@ <group value="search"/> <group value="mtf_migrated"/> </annotations> - + <before> <createData entity="_defaultCategory" stepKey="createCategory1"/> <createData entity="SimpleProduct" stepKey="createProduct1"> @@ -44,7 +44,7 @@ <amOnPage url="{{AdminCatalogSearchTermIndexPage.url}}" stepKey="openAdminCatalogSearchTermIndexPage1"/> <waitForPageLoad stepKey="waitForPageLoad1"/> - <actionGroup ref="searchTermFilterBySearchQuery" stepKey="filterByFirstSearchQuery1"> + <actionGroup ref="AdminSearchTermFilterBySearchQueryActionGroup" stepKey="filterByFirstSearchQuery1"> <argument name="searchQuery" value="$$createProduct1.name$$"/> </actionGroup> @@ -58,7 +58,7 @@ <amOnPage url="{{AdminCatalogSearchTermIndexPage.url}}" stepKey="openAdminCatalogSearchTermIndexPage2"/> <waitForPageLoad stepKey="waitForPageLoad3"/> - <actionGroup ref="searchTermFilterBySearchQuery" stepKey="filterByFirstSearchQuery2"> + <actionGroup ref="AdminSearchTermFilterBySearchQueryActionGroup" stepKey="filterByFirstSearchQuery2"> <argument name="searchQuery" value="{{UpdatedSearchTermData1.query_text}}"/> </actionGroup> diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Ui/DataProvider/Product/AddFulltextFilterToCollectionTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Ui/DataProvider/Product/AddFulltextFilterToCollectionTest.php new file mode 100644 index 0000000000000..881c843ecf92b --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Unit/Ui/DataProvider/Product/AddFulltextFilterToCollectionTest.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogSearch\Test\Unit\Ui\DataProvider\Product; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\CatalogSearch\Model\ResourceModel\Search\Collection as SearchCollection; +use Magento\Framework\Data\Collection; +use Magento\CatalogSearch\Ui\DataProvider\Product\AddFulltextFilterToCollection; + +class AddFulltextFilterToCollectionTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var SearchCollection|\PHPUnit_Framework_MockObject_MockObject + */ + private $searchCollection; + + /** + * @var Collection|\PHPUnit_Framework_MockObject_MockObject + */ + private $collection; + + /** + * @var ObjectManagerHelper + */ + private $objectManager; + + /** + * @var AddFulltextFilterToCollection + */ + private $model; + + protected function setUp() + { + $this->objectManager = new ObjectManagerHelper($this); + + $this->searchCollection = $this->getMockBuilder(SearchCollection::class) + ->setMethods(['addBackendSearchFilter', 'load', 'getAllIds']) + ->disableOriginalConstructor() + ->getMock(); + $this->searchCollection->expects($this->any()) + ->method('load') + ->willReturnSelf(); + $this->collection = $this->getMockBuilder(Collection::class) + ->setMethods(['addIdFilter']) + ->disableOriginalConstructor() + ->getMock(); + + $this->model = $this->objectManager->getObject( + AddFulltextFilterToCollection::class, + [ + 'searchCollection' => $this->searchCollection + ] + ); + } + + public function testAddFilter() + { + $this->searchCollection->expects($this->once()) + ->method('addBackendSearchFilter') + ->with('test'); + $this->searchCollection->expects($this->once()) + ->method('getAllIds') + ->willReturn([]); + $this->collection->expects($this->once()) + ->method('addIdFilter') + ->with(-1); + $this->model->addFilter($this->collection, 'test', ['fulltext' => 'test']); + } +} diff --git a/app/code/Magento/CatalogSearch/Ui/DataProvider/Product/AddFulltextFilterToCollection.php b/app/code/Magento/CatalogSearch/Ui/DataProvider/Product/AddFulltextFilterToCollection.php index f312178e0bf0b..af5020a2f8c94 100644 --- a/app/code/Magento/CatalogSearch/Ui/DataProvider/Product/AddFulltextFilterToCollection.php +++ b/app/code/Magento/CatalogSearch/Ui/DataProvider/Product/AddFulltextFilterToCollection.php @@ -40,6 +40,10 @@ public function addFilter(Collection $collection, $field, $condition = null) if (isset($condition['fulltext']) && (string)$condition['fulltext'] !== '') { $this->searchCollection->addBackendSearchFilter($condition['fulltext']); $productIds = $this->searchCollection->load()->getAllIds(); + if (empty($productIds)) { + //add dummy id to prevent returning full unfiltered collection + $productIds = -1; + } $collection->addIdFilter($productIds); } } diff --git a/app/code/Magento/CatalogSearch/etc/di.xml b/app/code/Magento/CatalogSearch/etc/di.xml index 372b389c545e6..4e5b38878ee52 100644 --- a/app/code/Magento/CatalogSearch/etc/di.xml +++ b/app/code/Magento/CatalogSearch/etc/di.xml @@ -75,6 +75,9 @@ <type name="Magento\Catalog\Model\Product\Action"> <plugin name="catalogsearchFulltextMassAction" type="Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\Product\Action"/> </type> + <type name="Magento\Catalog\Model\Indexer\Product\Category\Action\Rows"> + <plugin name="catalogsearchFulltextCategoryAssignment" type="Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\Product\Category\Action\Rows"/> + </type> <type name="Magento\Store\Model\ResourceModel\Store"> <plugin name="catalogsearchFulltextIndexerStoreView" type="Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\Store\View" /> </type> diff --git a/app/code/Magento/CatalogSearch/registration.php b/app/code/Magento/CatalogSearch/registration.php index 959e26c84a722..31f792d2ea90c 100644 --- a/app/code/Magento/CatalogSearch/registration.php +++ b/app/code/Magento/CatalogSearch/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CatalogSearch', __DIR__); diff --git a/app/code/Magento/CatalogUrlRewrite/Model/Product/AnchorUrlRewriteGenerator.php b/app/code/Magento/CatalogUrlRewrite/Model/Product/AnchorUrlRewriteGenerator.php index 4a191b54dea68..5d08ea33ff8a1 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/Product/AnchorUrlRewriteGenerator.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/Product/AnchorUrlRewriteGenerator.php @@ -6,6 +6,7 @@ namespace Magento\CatalogUrlRewrite\Model\Product; use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Model\Category; use Magento\Catalog\Model\Product; use Magento\CatalogUrlRewrite\Model\ObjectRegistry; use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator; @@ -67,6 +68,9 @@ public function generate($storeId, Product $product, ObjectRegistry $productCate if ($anchorCategoryIds) { foreach ($anchorCategoryIds as $anchorCategoryId) { $anchorCategory = $this->categoryRepository->get($anchorCategoryId); + if ((int)$anchorCategory->getParentId() === Category::TREE_ROOT_ID) { + continue; + } $urls[] = $this->urlRewriteFactory->create() ->setEntityType(ProductUrlRewriteGenerator::ENTITY_TYPE) ->setEntityId($product->getId()) diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php index 54ef7102fcc47..0d0b0fb995706 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryUrlPathAutogeneratorObserver.php @@ -94,7 +94,7 @@ public function execute(\Magento\Framework\Event\Observer $observer) $resultUrlKey = $this->categoryUrlPathGenerator->getUrlKey($category); $this->updateUrlKey($category, $resultUrlKey); } elseif ($useDefaultAttribute) { - if (!$category->isObjectNew()) { + if (!$category->isObjectNew() && $category->getStoreId() === Store::DEFAULT_STORE_ID) { $resultUrlKey = $category->formatUrlKey($category->getOrigData('name')); $this->updateUrlKey($category, $resultUrlKey); } diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php b/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php index 083b39d621f2a..d1e78897e3269 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php @@ -7,8 +7,8 @@ namespace Magento\CatalogUrlRewrite\Observer; -use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Product; use Magento\Catalog\Model\ResourceModel\Product\Collection; use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; use Magento\CatalogUrlRewrite\Model\Category\ChildrenCategoriesProvider; @@ -118,13 +118,15 @@ public function __construct( $this->productCollectionFactory = $productCollectionFactory; $this->categoryBasedProductRewriteGenerator = $categoryBasedProductRewriteGenerator; - $objectManager = ObjectManager::getInstance(); - $mergeDataProviderFactory = $mergeDataProviderFactory ?: $objectManager->get(MergeDataProviderFactory::class); + $mergeDataProviderFactory = $mergeDataProviderFactory + ?? ObjectManager::getInstance()->get(MergeDataProviderFactory::class); $this->mergeDataProviderPrototype = $mergeDataProviderFactory->create(); - $this->serializer = $serializer ?: $objectManager->get(Json::class); + $this->serializer = $serializer + ?? ObjectManager::getInstance()->get(Json::class); $this->productScopeRewriteGenerator = $productScopeRewriteGenerator - ?: $objectManager->get(ProductScopeRewriteGenerator::class); - $this->scopeConfig = $scopeConfig ?? $objectManager->get(ScopeConfigInterface::class); + ?? ObjectManager::getInstance()->get(ProductScopeRewriteGenerator::class); + $this->scopeConfig = $scopeConfig + ?? ObjectManager::getInstance()->get(ScopeConfigInterface::class); } /** @@ -207,18 +209,14 @@ public function deleteCategoryRewritesForChildren(Category $category) foreach ($categoryIds as $categoryId) { $this->urlPersist->deleteByData( [ - UrlRewrite::ENTITY_ID => - $categoryId, - UrlRewrite::ENTITY_TYPE => - CategoryUrlRewriteGenerator::ENTITY_TYPE, + UrlRewrite::ENTITY_ID => $categoryId, + UrlRewrite::ENTITY_TYPE => CategoryUrlRewriteGenerator::ENTITY_TYPE, ] ); $this->urlPersist->deleteByData( [ - UrlRewrite::METADATA => - $this->serializer->serialize(['category_id' => $categoryId]), - UrlRewrite::ENTITY_TYPE => - ProductUrlRewriteGenerator::ENTITY_TYPE, + UrlRewrite::METADATA => $this->serializer->serialize(['category_id' => $categoryId]), + UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, ] ); } @@ -252,7 +250,7 @@ private function getCategoryProductsUrlRewrites( ->addAttributeToSelect('url_key') ->addAttributeToSelect('url_path'); - foreach ($productCollection as $product) { + foreach ($this->getProducts($productCollection) as $product) { if (isset($this->isSkippedProduct[$category->getEntityId()]) && in_array($product->getId(), $this->isSkippedProduct[$category->getEntityId()]) ) { @@ -270,6 +268,27 @@ private function getCategoryProductsUrlRewrites( return $mergeDataProvider->getData(); } + /** + * Get products from provided collection + * + * @param Collection $collection + * @return \Generator|Product[] + */ + private function getProducts(Collection $collection): \Generator + { + $collection->setPageSize(1000); + $pageCount = $collection->getLastPageNumber(); + $currentPage = 1; + while ($currentPage <= $pageCount) { + $collection->setCurPage($currentPage); + foreach ($collection as $key => $product) { + yield $key => $product; + } + $collection->clear(); + $currentPage++; + } + } + /** * Generates product URL rewrites. * diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml index 6538ec6e935df..c3c7e3c3281f4 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminCategoryWithRestrictedUrlKeyNotCreatedTest.xml @@ -24,103 +24,103 @@ <after> <!--Delete created categories--> <comment userInput="Delete created categories" stepKey="commentDeleteCreatedCategories"/> - <actionGroup ref="AdminDeleteCategoryByName" stepKey="deleteAdminCategory"> + <actionGroup ref="AdminDeleteCategoryByNameActionGroup" stepKey="deleteAdminCategory"> <argument name="categoryName" value="admin"/> </actionGroup> - <actionGroup ref="AdminDeleteCategoryByName" stepKey="deleteSoapCategory"> + <actionGroup ref="AdminDeleteCategoryByNameActionGroup" stepKey="deleteSoapCategory"> <argument name="categoryName" value="soap"/> </actionGroup> - <actionGroup ref="AdminDeleteCategoryByName" stepKey="deleteRestCategory"> + <actionGroup ref="AdminDeleteCategoryByNameActionGroup" stepKey="deleteRestCategory"> <argument name="categoryName" value="rest"/> </actionGroup> - <actionGroup ref="AdminDeleteCategoryByName" stepKey="deleteGraphQlCategory"> + <actionGroup ref="AdminDeleteCategoryByNameActionGroup" stepKey="deleteGraphQlCategory"> <argument name="categoryName" value="graphql"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> </after> <!--Check category creation with restricted url key 'admin'--> <comment userInput="Check category creation with restricted url key 'admin'" stepKey="commentCheckAdminCategoryCreation"/> - <actionGroup ref="goToCreateCategoryPage" stepKey="goToCreateAdminCategoryPage"/> - <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillAdminFirstCategoryForm"> + <actionGroup ref="GoToCreateCategoryPageActionGroup" stepKey="goToCreateAdminCategoryPage"/> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSaveActionGroup" stepKey="fillAdminFirstCategoryForm"> <argument name="categoryName" value="admin"/> <argument name="categoryUrlKey" value=""/> </actionGroup> - <see selector="{{AdminMessagesSection.errorMessage}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlAdmin}}' stepKey="seeAdminFirstErrorMessage"/> - <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillAdminSecondCategoryForm"> + <see selector="{{AdminMessagesSection.error}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlAdmin}}' stepKey="seeAdminFirstErrorMessage"/> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSaveActionGroup" stepKey="fillAdminSecondCategoryForm"> <argument name="categoryName" value="{{SimpleSubCategory.name}}"/> <argument name="categoryUrlKey" value="admin"/> </actionGroup> - <see selector="{{AdminMessagesSection.errorMessage}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlAdmin}}' stepKey="seeAdminSecondErrorMessage"/> + <see selector="{{AdminMessagesSection.error}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlAdmin}}' stepKey="seeAdminSecondErrorMessage"/> <!--Create category with 'admin' name--> <comment userInput="Create category with 'admin' name" stepKey="commentAdminCategoryCreation"/> - <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillAdminThirdCategoryForm"> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSaveActionGroup" stepKey="fillAdminThirdCategoryForm"> <argument name="categoryName" value="admin"/> <argument name="categoryUrlKey" value="{{SimpleSubCategory.name}}"/> </actionGroup> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="You saved the category." stepKey="seeAdminSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the category." stepKey="seeAdminSuccessMessage"/> <seeElement selector="{{AdminCategorySidebarTreeSection.categoryByName('admin')}}" stepKey="seeAdminCategoryInTree"/> <!--Check category creation with restricted url key 'soap'--> <comment userInput="Check category creation with restricted url key 'soap'" stepKey="commentCheckSoapCategoryCreation"/> - <actionGroup ref="goToCreateCategoryPage" stepKey="goToCreateSoapCategoryPage"/> - <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillSoapFirstCategoryForm"> + <actionGroup ref="GoToCreateCategoryPageActionGroup" stepKey="goToCreateSoapCategoryPage"/> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSaveActionGroup" stepKey="fillSoapFirstCategoryForm"> <argument name="categoryName" value="soap"/> <argument name="categoryUrlKey" value=""/> </actionGroup> - <see selector="{{AdminMessagesSection.errorMessage}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlSoap}}' stepKey="seeSoapFirstErrorMessage"/> - <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillSoapSecondCategoryForm"> + <see selector="{{AdminMessagesSection.error}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlSoap}}' stepKey="seeSoapFirstErrorMessage"/> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSaveActionGroup" stepKey="fillSoapSecondCategoryForm"> <argument name="categoryName" value="{{ApiCategory.name}}"/> <argument name="categoryUrlKey" value="soap"/> </actionGroup> - <see selector="{{AdminMessagesSection.errorMessage}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlSoap}}' stepKey="seeSoapSecondErrorMessage"/> + <see selector="{{AdminMessagesSection.error}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlSoap}}' stepKey="seeSoapSecondErrorMessage"/> <!--Create category with 'soap' name--> <comment userInput="Create category with 'soap' name" stepKey="commentSoapCategoryCreation"/> - <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillSoapThirdCategoryForm"> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSaveActionGroup" stepKey="fillSoapThirdCategoryForm"> <argument name="categoryName" value="soap"/> <argument name="categoryUrlKey" value="{{ApiCategory.name}}"/> </actionGroup> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="You saved the category." stepKey="seeSoapSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the category." stepKey="seeSoapSuccessMessage"/> <seeElement selector="{{AdminCategorySidebarTreeSection.categoryByName('soap')}}" stepKey="seeSoapCategoryInTree"/> <!--Check category creation with restricted url key 'rest'--> <comment userInput="Check category creation with restricted url key 'rest'" stepKey="commentCheckRestCategoryCreation"/> - <actionGroup ref="goToCreateCategoryPage" stepKey="goToCreateRestCategoryPage"/> - <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillRestFirstCategoryForm"> + <actionGroup ref="GoToCreateCategoryPageActionGroup" stepKey="goToCreateRestCategoryPage"/> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSaveActionGroup" stepKey="fillRestFirstCategoryForm"> <argument name="categoryName" value="rest"/> <argument name="categoryUrlKey" value=""/> </actionGroup> - <see selector="{{AdminMessagesSection.errorMessage}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlRest}}' stepKey="seeRestFirstErrorMessage"/> - <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillRestSecondCategoryForm"> + <see selector="{{AdminMessagesSection.error}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlRest}}' stepKey="seeRestFirstErrorMessage"/> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSaveActionGroup" stepKey="fillRestSecondCategoryForm"> <argument name="categoryName" value="{{SubCategoryWithParent.name}}"/> <argument name="categoryUrlKey" value="rest"/> </actionGroup> - <see selector="{{AdminMessagesSection.errorMessage}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlRest}}' stepKey="seeRestSecondErrorMessage"/> + <see selector="{{AdminMessagesSection.error}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlRest}}' stepKey="seeRestSecondErrorMessage"/> <!--Create category with 'rest' name--> <comment userInput="Create category with 'rest' name" stepKey="commentRestCategoryCreation"/> - <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillRestThirdCategoryForm"> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSaveActionGroup" stepKey="fillRestThirdCategoryForm"> <argument name="categoryName" value="rest"/> <argument name="categoryUrlKey" value="{{SubCategoryWithParent.name}}"/> </actionGroup> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="You saved the category." stepKey="seeRestSuccessMesdgssage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the category." stepKey="seeRestSuccessMesdgssage"/> <seeElement selector="{{AdminCategorySidebarTreeSection.categoryByName('rest')}}" stepKey="seeRestCategoryInTree"/> <!--Check category creation with restricted url key 'graphql'--> <comment userInput="Check category creation with restricted url key 'graphql'" stepKey="commentCheckGraphQlCategoryCreation"/> - <actionGroup ref="goToCreateCategoryPage" stepKey="goToCreateCategoryPage"/> - <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillGraphQlFirstCategoryForm"> + <actionGroup ref="GoToCreateCategoryPageActionGroup" stepKey="goToCreateCategoryPage"/> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSaveActionGroup" stepKey="fillGraphQlFirstCategoryForm"> <argument name="categoryName" value="graphql"/> <argument name="categoryUrlKey" value=""/> </actionGroup> - <see selector="{{AdminMessagesSection.errorMessage}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlGraphql}}' stepKey="seeGraphQlFirstErrorMessage"/> - <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillGraphQlSecondCategoryForm"> + <see selector="{{AdminMessagesSection.error}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlGraphql}}' stepKey="seeGraphQlFirstErrorMessage"/> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSaveActionGroup" stepKey="fillGraphQlSecondCategoryForm"> <argument name="categoryName" value="{{NewSubCategoryWithParent.name}}"/> <argument name="categoryUrlKey" value="graphql"/> </actionGroup> - <see selector="{{AdminMessagesSection.errorMessage}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlGraphql}}' stepKey="seeGraphQlSecondErrorMessage"/> + <see selector="{{AdminMessagesSection.error}}" userInput='{{AdminCategoryRestrictedUrlErrorMessage.urlGraphql}}' stepKey="seeGraphQlSecondErrorMessage"/> <!--Create category with 'graphql' name--> <comment userInput="Create category with 'graphql' name" stepKey="commentGraphQlCategoryCreation"/> - <actionGroup ref="FillCategoryNameAndUrlKeyAndSave" stepKey="fillGraphQlThirdCategoryForm"> + <actionGroup ref="FillCategoryNameAndUrlKeyAndSaveActionGroup" stepKey="fillGraphQlThirdCategoryForm"> <argument name="categoryName" value="graphql"/> <argument name="categoryUrlKey" value="{{NewSubCategoryWithParent.name}}"/> </actionGroup> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="You saved the category." stepKey="seeGraphQlSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the category." stepKey="seeGraphQlSuccessMessage"/> <seeElement selector="{{AdminCategorySidebarTreeSection.categoryByName('graphql')}}" stepKey="seeGraphQlCategoryInTree"/> </test> </tests> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml index 5423267676507..cc5f09faca57b 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/AdminUrlForProductRewrittenCorrectlyTest.xml @@ -41,7 +41,7 @@ <waitForPageLoad stepKey="waitForEditPage"/> <!--Switch to Default Store view--> - <actionGroup ref="SwitchToTheNewStoreView" stepKey="selectSecondStoreView"> + <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="selectSecondStoreView"> <argument name="storeViewName" value="Default Store View"/> </actionGroup> <waitForPageLoad stepKey="waitForStoreViewLoad"/> @@ -51,12 +51,12 @@ <waitForElementVisible selector="{{AdminProductSEOSection.useDefaultUrl}}" stepKey="waitForUseDefaultUrlCheckbox"/> <click selector="{{AdminProductSEOSection.useDefaultUrl}}" stepKey="clickUseDefaultUrlCheckbox"/> <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="$$createProduct.sku$$-new" stepKey="changeUrlKey"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!--Select product and go toUpdate Attribute page--> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="GoToCatalogPageChangingView"/> <waitForPageLoad stepKey="WaitForPageToLoadFullyChangingView"/> - <actionGroup ref="filterProductGridByName" stepKey="filterBundleProductOptionsDownToName"> + <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="filterBundleProductOptionsDownToName"> <argument name="product" value="ApiSimpleProduct"/> </actionGroup> <click selector="{{AdminProductFiltersSection.allCheckbox}}" stepKey="ClickOnSelectAllCheckBoxChangingView"/> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml index 67870c51140a6..c3a358bbbd292 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/RewriteStoreLevelUrlKeyOfChildCategoryTest.xml @@ -1,67 +1,67 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="RewriteStoreLevelUrlKeyOfChildCategoryTest"> - <annotations> - <title value="Rewriting Store-level URL key of child category"/> - <stories value="MAGETWO-91649: #13513: Magento ignore store-level url_key of child category in URL rewrite process for global scope"/> - <description value="Rewriting Store-level URL key of child category"/> - <features value="CatalogUrlRewrite"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-94934"/> - <group value="CatalogUrlRewrite"/> - </annotations> - - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView" /> - - <createData entity="_defaultCategory" stepKey="defaultCategory"/> - <createData entity="SubCategoryWithParent" stepKey="subCategory"> - <requiredEntity createDataKey="defaultCategory"/> - </createData> - </before> - - <actionGroup ref="navigateToCreatedCategory" stepKey="navigateToCreatedSubCategory"> - <argument name="Category" value="$$subCategory$$"/> - </actionGroup> - - <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="AdminSwitchStoreViewForSubCategory"/> - - <actionGroup ref="ChangeSeoUrlKeyForSubCategory" stepKey="changeSeoUrlKeyForSubCategory"> - <argument name="value" value="bags-second"/> - </actionGroup> - - <actionGroup ref="navigateToCreatedCategory" stepKey="navigateToCreatedDefaultCategory"> - <argument name="Category" value="$$defaultCategory$$"/> - </actionGroup> - - <actionGroup ref="ChangeSeoUrlKey" stepKey="changeSeoUrlKeyForDefaultCategory"> - <argument name="value" value="gear-global"/> - </actionGroup> - - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - - <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="storefrontSwitchStoreView"/> - - <actionGroup ref="GoToSubCategoryPage" stepKey="goToSubCategoryPage"> - <argument name="parentCategory" value="$$defaultCategory$$"/> - <argument name="subCategory" value="$$subCategory$$"/> - <argument name="urlPath" value="gear-global/bags-second"/> - </actionGroup> - - <after> - <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"/> - <actionGroup ref="logout" stepKey="logout"/> - - <deleteData createDataKey="subCategory" stepKey="deleteSubCategory"/> - <deleteData createDataKey="defaultCategory" stepKey="deleteNewRootCategory"/> - </after> - </test> -</tests> +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="RewriteStoreLevelUrlKeyOfChildCategoryTest"> + <annotations> + <title value="Rewriting Store-level URL key of child category"/> + <stories value="MAGETWO-91649: #13513: Magento ignore store-level url_key of child category in URL rewrite process for global scope"/> + <description value="Rewriting Store-level URL key of child category"/> + <features value="CatalogUrlRewrite"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-94934"/> + <group value="CatalogUrlRewrite"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView" /> + + <createData entity="_defaultCategory" stepKey="defaultCategory"/> + <createData entity="SubCategoryWithParent" stepKey="subCategory"> + <requiredEntity createDataKey="defaultCategory"/> + </createData> + </before> + + <actionGroup ref="NavigateToCreatedCategoryActionGroup" stepKey="navigateToCreatedSubCategory"> + <argument name="Category" value="$$subCategory$$"/> + </actionGroup> + + <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="AdminSwitchStoreViewForSubCategory"/> + + <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeSeoUrlKeyForSubCategory"> + <argument name="value" value="bags-second"/> + </actionGroup> + + <actionGroup ref="NavigateToCreatedCategoryActionGroup" stepKey="navigateToCreatedDefaultCategory"> + <argument name="Category" value="$$defaultCategory$$"/> + </actionGroup> + + <actionGroup ref="ChangeSeoUrlKeyActionGroup" stepKey="changeSeoUrlKeyForDefaultCategory"> + <argument name="value" value="gear-global"/> + </actionGroup> + + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="storefrontSwitchStoreView"/> + + <actionGroup ref="GoToSubCategoryPageActionGroup" stepKey="goToSubCategoryPage"> + <argument name="parentCategory" value="$$defaultCategory$$"/> + <argument name="subCategory" value="$$subCategory$$"/> + <argument name="urlPath" value="gear-global/bags-second"/> + </actionGroup> + + <after> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"/> + <actionGroup ref="logout" stepKey="logout"/> + + <deleteData createDataKey="subCategory" stepKey="deleteSubCategory"/> + <deleteData createDataKey="defaultCategory" stepKey="deleteNewRootCategory"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/StorefrontCategoryAccessibleWhenSuffixIsNullTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/StorefrontCategoryAccessibleWhenSuffixIsNullTest.xml new file mode 100644 index 0000000000000..ef8f2b6b1a3e2 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/StorefrontCategoryAccessibleWhenSuffixIsNullTest.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="StorefrontCategoryAccessibleWhenSuffixIsNullTest"> + <annotations> + <title value="Storefront category is accessible when url suffix is set to null test"/> + <description value="Check no crash occurs on Category page when catalog/seo/category_url_suffix is set to null"/> + <features value="CatalogUrlRewrite"/> + <severity value="MAJOR"/> + <group value="CatalogUrlRewrite"/> + </annotations> + <before> + <magentoCLI command="config:set catalog/seo/category_url_suffix ''" stepKey="setCategoryUrlSuffix"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 0" + stepKey="setCategoryProductRewrites"/> + <magentoCLI command="cache:flush" stepKey="flushCacheBefore"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <magentoCLI command="config:set catalog/seo/category_url_suffix '.html'" + stepKey="restoreCategoryUrlSuffix"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" + stepKey="restoreCategoryProductRewrites"/> + <magentoCLI command="cache:flush" stepKey="flushCacheAfter"/> + </after> + + <amOnPage url="/$$createCategory.name$$" stepKey="onCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <seeInTitle userInput="$$createCategory.name$$" stepKey="assertCategoryNameInTitle"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Product/AnchorUrlRewriteGeneratorTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Product/AnchorUrlRewriteGeneratorTest.php new file mode 100644 index 0000000000000..662e156b8f100 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Product/AnchorUrlRewriteGeneratorTest.php @@ -0,0 +1,140 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\CatalogUrlRewrite\Test\Unit\Model\Product; + +use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + +class AnchorUrlRewriteGeneratorTest extends \PHPUnit\Framework\TestCase +{ + /** @var \Magento\CatalogUrlRewrite\Model\Product\AnchorUrlRewriteGenerator */ + protected $anchorUrlRewriteGenerator; + + /** @var \Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator|\PHPUnit_Framework_MockObject_MockObject */ + protected $productUrlPathGenerator; + + /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject */ + protected $product; + + /** @var \Magento\Catalog\Api\CategoryRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $categoryRepositoryInterface; + + /** @var \Magento\CatalogUrlRewrite\Model\ObjectRegistry|\PHPUnit_Framework_MockObject_MockObject */ + protected $categoryRegistry; + + /** @var \Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory|\PHPUnit_Framework_MockObject_MockObject */ + protected $urlRewriteFactory; + + /** @var \Magento\UrlRewrite\Service\V1\Data\UrlRewrite|\PHPUnit_Framework_MockObject_MockObject */ + protected $urlRewrite; + + protected function setUp() + { + $this->urlRewriteFactory = $this->getMockBuilder(\Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor()->getMock(); + $this->urlRewrite = $this->getMockBuilder(\Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class) + ->disableOriginalConstructor()->getMock(); + $this->product = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + ->disableOriginalConstructor()->getMock(); + $this->categoryRepositoryInterface = $this->getMockBuilder( + \Magento\Catalog\Api\CategoryRepositoryInterface::class + )->disableOriginalConstructor()->getMock(); + $this->categoryRegistry = $this->getMockBuilder(\Magento\CatalogUrlRewrite\Model\ObjectRegistry::class) + ->disableOriginalConstructor()->getMock(); + $this->productUrlPathGenerator = $this->getMockBuilder( + \Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator::class + )->disableOriginalConstructor()->getMock(); + $this->anchorUrlRewriteGenerator = (new ObjectManager($this))->getObject( + \Magento\CatalogUrlRewrite\Model\Product\AnchorUrlRewriteGenerator::class, + [ + 'productUrlPathGenerator' => $this->productUrlPathGenerator, + 'urlRewriteFactory' => $this->urlRewriteFactory, + 'categoryRepository' => $this->categoryRepositoryInterface + ] + ); + } + + public function testGenerateEmpty() + { + $this->categoryRegistry->expects($this->any())->method('getList')->will($this->returnValue([])); + + $this->assertEquals( + [], + $this->anchorUrlRewriteGenerator->generate(1, $this->product, $this->categoryRegistry) + ); + } + + public function testGenerateCategories() + { + $urlPathWithCategory = 'category1/category2/category3/simple-product.html'; + $storeId = 10; + $productId = 12; + $canonicalUrlPathWithCategory = 'canonical-path-with-category'; + $categoryParentId = '1'; + $categoryIds = [$categoryParentId,'2','3','4']; + $urls = ['category1/simple-product.html', + 'category1/category2/simple-product.html', + 'category1/category2/category3/simple-product.html']; + + $this->product->expects($this->any())->method('getId')->will($this->returnValue($productId)); + $this->productUrlPathGenerator->expects($this->any())->method('getUrlPathWithSuffix') + ->will($this->returnValue($urlPathWithCategory)); + $this->productUrlPathGenerator->expects($this->any())->method('getCanonicalUrlPath') + ->will($this->returnValue($canonicalUrlPathWithCategory)); + $category = $this->createMock(\Magento\Catalog\Model\Category::class); + $category->expects($this->any())->method('getId')->will($this->returnValue($categoryIds)); + $category->expects($this->any())->method('getAnchorsAbove')->will($this->returnValue($categoryIds)); + $category->expects($this->any())->method('getParentId')->will( + $this->onConsecutiveCalls( + $categoryIds[0], + $categoryIds[1], + $categoryIds[2], + $categoryIds[3] + ) + ); + $this->categoryRepositoryInterface + ->expects($this->any()) + ->method('get') + ->withConsecutive( + [ 'category_id' => $categoryIds[0]], + [ 'category_id' => $categoryIds[1]], + [ 'category_id' => $categoryIds[2]] + ) + ->will($this->returnValue($category)); + $this->categoryRegistry->expects($this->any())->method('getList') + ->will($this->returnValue([$category])); + $this->urlRewrite->expects($this->any())->method('setStoreId') + ->with($storeId) + ->will($this->returnSelf()); + $this->urlRewrite->expects($this->any())->method('setEntityId') + ->with($productId) + ->will($this->returnSelf()); + $this->urlRewrite->expects($this->any())->method('setEntityType') + ->with(ProductUrlRewriteGenerator::ENTITY_TYPE) + ->will($this->returnSelf()); + $this->urlRewrite->expects($this->any())->method('setRequestPath') + ->will($this->returnSelf()); + $this->urlRewrite->expects($this->any())->method('setTargetPath') + ->will($this->returnSelf()); + $this->urlRewrite->expects($this->any())->method('setMetadata') + ->will( + $this->onConsecutiveCalls( + $urls[0], + $urls[1], + $urls[2] + ) + ); + $this->urlRewriteFactory->expects($this->any())->method('create')->will( + $this->returnValue($this->urlRewrite) + ); + + $this->assertEquals( + $urls, + $this->anchorUrlRewriteGenerator->generate($storeId, $this->product, $this->categoryRegistry) + ); + } +} diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/CategoryUrlPathAutogeneratorObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/CategoryUrlPathAutogeneratorObserverTest.php index 0a570adab309a..9e2090e36f08e 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/CategoryUrlPathAutogeneratorObserverTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/CategoryUrlPathAutogeneratorObserverTest.php @@ -159,6 +159,9 @@ public function testShouldThrowExceptionIfUrlKeyIsEmpty($useDefaultUrlKey, $isOb $this->expectExceptionMessage('Invalid URL key'); $categoryData = ['use_default' => ['url_key' => $useDefaultUrlKey], 'url_key' => '', 'url_path' => '']; $this->category->setData($categoryData); + $this->category + ->method('getStoreId') + ->willReturn(\Magento\Store\Model\Store::DEFAULT_STORE_ID); $this->category->isObjectNew($isObjectNew); $this->assertEquals($isObjectNew, $this->category->isObjectNew()); $this->assertEquals($categoryData['url_key'], $this->category->getUrlKey()); diff --git a/app/code/Magento/CatalogUrlRewrite/Ui/DataProvider/Product/Form/Modifier/ProductUrlRewrite.php b/app/code/Magento/CatalogUrlRewrite/Ui/DataProvider/Product/Form/Modifier/ProductUrlRewrite.php index bcb5154e35501..10791eae5405f 100644 --- a/app/code/Magento/CatalogUrlRewrite/Ui/DataProvider/Product/Form/Modifier/ProductUrlRewrite.php +++ b/app/code/Magento/CatalogUrlRewrite/Ui/DataProvider/Product/Form/Modifier/ProductUrlRewrite.php @@ -53,7 +53,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyMeta(array $meta) { @@ -65,7 +65,7 @@ public function modifyMeta(array $meta) } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyData(array $data) { @@ -95,16 +95,21 @@ protected function addUrlRewriteCheckbox(array $meta) ScopeInterface::SCOPE_STORE, $this->locator->getProduct()->getStoreId() ); - - $meta = $this->arrayManager->merge($containerPath, $meta, [ - 'arguments' => [ - 'data' => [ - 'config' => [ - 'component' => 'Magento_Ui/js/form/components/group', + $meta = $this->arrayManager->merge( + $containerPath, + $meta, + [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'component' => 'Magento_Ui/js/form/components/group', + 'label' => false, + 'required' => false, + ], ], ], - ], - ]); + ] + ); $checkbox['arguments']['data']['config'] = [ 'componentType' => Field::NAME, diff --git a/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/system.xml b/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/system.xml index 1e1f4e86fa3dc..ad0ff68192af4 100644 --- a/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/system.xml +++ b/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/system.xml @@ -28,13 +28,11 @@ <label>Create Permanent Redirect for URLs if URL Key Changed</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="generate_category_product_rewrites" translate="label" type="select" sortOrder="6" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <field id="generate_category_product_rewrites" translate="label comment" type="select" sortOrder="6" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Generate "category/product" URL Rewrites</label> <backend_model>Magento\CatalogUrlRewrite\Model\TableCleaner</backend_model> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <comment> - <![CDATA[<strong style="color:red">Warning!</strong> Turning this option off will result in permanent removal of category/product URL rewrites without an ability to restore them.]]> - </comment> + <comment><![CDATA[<strong style="color:red">Warning!</strong> Turning this option off will result in permanent removal of category/product URL rewrites without an ability to restore them.]]></comment> <frontend_class>generate_category_product_rewrites</frontend_class> </field> </group> diff --git a/app/code/Magento/CatalogUrlRewrite/etc/di.xml b/app/code/Magento/CatalogUrlRewrite/etc/di.xml index 2a74b5cd92b28..5fb7d33546d60 100644 --- a/app/code/Magento/CatalogUrlRewrite/etc/di.xml +++ b/app/code/Magento/CatalogUrlRewrite/etc/di.xml @@ -56,4 +56,18 @@ </argument> </arguments> </type> + <type name="Magento\Eav\Model\Config"> + <arguments> + <argument name="attributesForPreload" xsi:type="array"> + <item name="catalog_product" xsi:type="array"> + <item name="url_key" xsi:type="string">catalog_product</item> + <item name="url_path" xsi:type="string">catalog_product</item> + </item> + <item name="catalog_category" xsi:type="array"> + <item name="url_key" xsi:type="string">catalog_category</item> + <item name="url_path" xsi:type="string">catalog_category</item> + </item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/CatalogUrlRewrite/i18n/en_US.csv b/app/code/Magento/CatalogUrlRewrite/i18n/en_US.csv index 0f21e8ddf9fc9..1dddaa458a16c 100644 --- a/app/code/Magento/CatalogUrlRewrite/i18n/en_US.csv +++ b/app/code/Magento/CatalogUrlRewrite/i18n/en_US.csv @@ -5,5 +5,6 @@ "Product URL Suffix","Product URL Suffix" "Use Categories Path for Product URLs","Use Categories Path for Product URLs" "Create Permanent Redirect for URLs if URL Key Changed","Create Permanent Redirect for URLs if URL Key Changed" -"Generate "category/product" URL Rewrites","Generate "category/product" URL Rewrites" +"Generate ""category/product"" URL Rewrites","Generate ""category/product"" URL Rewrites" "URL key ""%1"" matches a reserved endpoint name (%2). Use another URL key.","URL key ""%1"" matches a reserved endpoint name (%2). Use another URL key." +"<strong style=""color:red"">Warning!</strong> Turning this option off will result in permanent removal of category/product URL rewrites without an ability to restore them.","<strong style=""color:red"">Warning!</strong> Turning this option off will result in permanent removal of category/product URL rewrites without an ability to restore them." diff --git a/app/code/Magento/CatalogUrlRewrite/registration.php b/app/code/Magento/CatalogUrlRewrite/registration.php index 50a5544c738d0..f00aaf5bb5518 100644 --- a/app/code/Magento/CatalogUrlRewrite/registration.php +++ b/app/code/Magento/CatalogUrlRewrite/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CatalogUrlRewrite', __DIR__); diff --git a/app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml b/app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml index e99f89477e807..8724972e71b17 100644 --- a/app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml +++ b/app/code/Magento/CatalogUrlRewriteGraphQl/etc/di.xml @@ -22,4 +22,12 @@ </argument> </arguments> </type> + + <type name="Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite"> + <arguments> + <argument name="entityTypeMapping" xsi:type="array"> + <item name="catalog_product" xsi:type="const">Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator::ENTITY_TYPE</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOperatorsTest.xml b/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOperatorsTest.xml index 32bea8b604cf8..13ffedb1f707b 100644 --- a/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOperatorsTest.xml +++ b/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOperatorsTest.xml @@ -38,11 +38,11 @@ <createData entity="_defaultBlock" stepKey="createPreReqBlock"/> <!--User log in on back-end as admin--> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> </before> <!--Open block with widget.--> - <actionGroup ref="navigateToCreatedCMSBlockPage" stepKey="navigateToCreatedCMSBlockPage1"> + <actionGroup ref="NavigateToCreatedCMSBlockPageActionGroup" stepKey="navigateToCreatedCMSBlockPage1"> <argument name="CMSBlockPage" value="$$createPreReqBlock$$"/> </actionGroup> @@ -85,7 +85,7 @@ <seeElement selector="{{InsertWidgetSection.checkElementStorefrontByPrice('100')}}" stepKey="seeElementByPrice100"/> <!--Open block with widget.--> - <actionGroup ref="navigateToCreatedCMSBlockPage" stepKey="navigateToCreatedCMSBlockPage2"> + <actionGroup ref="NavigateToCreatedCMSBlockPageActionGroup" stepKey="navigateToCreatedCMSBlockPage2"> <argument name="CMSBlockPage" value="$$createPreReqBlock$$"/> </actionGroup> @@ -105,7 +105,7 @@ <dontSeeElement selector="{{InsertWidgetSection.checkElementStorefrontByPrice('100')}}" stepKey="dontSeeElementByPrice100"/> <!--Open block with widget.--> - <actionGroup ref="navigateToCreatedCMSBlockPage" stepKey="navigateToCreatedCMSBlockPage3"> + <actionGroup ref="NavigateToCreatedCMSBlockPageActionGroup" stepKey="navigateToCreatedCMSBlockPage3"> <argument name="CMSBlockPage" value="$$createPreReqBlock$$"/> </actionGroup> @@ -125,7 +125,7 @@ <seeElement selector="{{InsertWidgetSection.checkElementStorefrontByPrice('100')}}" stepKey="seeElementByPrice100s"/> <!--Open block with widget.--> - <actionGroup ref="navigateToCreatedCMSBlockPage" stepKey="navigateToCreatedCMSBlockPage4"> + <actionGroup ref="NavigateToCreatedCMSBlockPageActionGroup" stepKey="navigateToCreatedCMSBlockPage4"> <argument name="CMSBlockPage" value="$$createPreReqBlock$$"/> </actionGroup> @@ -145,7 +145,7 @@ <dontSeeElement selector="{{InsertWidgetSection.checkElementStorefrontByPrice('100')}}" stepKey="dontSeeElementByPrice100s"/> <after> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <deleteData createDataKey="createPreReqBlock" stepKey="deletePreReqBlock" /> <deleteData createDataKey="simplecategory" stepKey="deleteSimpleCategory"/> <deleteData createDataKey="createFirstProduct" stepKey="deleteFirstProduct"/> diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOrderTest.xml b/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOrderTest.xml index 11586207c4d8e..f0af04478bc28 100644 --- a/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOrderTest.xml +++ b/app/code/Magento/CatalogWidget/Test/Mftf/Test/CatalogProductListWidgetOrderTest.xml @@ -38,11 +38,11 @@ </createData> <createData entity="_defaultCmsPage" stepKey="createPreReqPage"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> </before> <!--Open created cms page--> <comment userInput="Open created cms page" stepKey="commentOpenCreatedCmsPage"/> - <actionGroup ref="navigateToCreatedCMSPage" stepKey="navigateToCreatedCMSPage1"> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage1"> <argument name="CMSPage" value="$$createPreReqPage$$"/> </actionGroup> <!--Add widget to cms page--> @@ -76,7 +76,7 @@ <seeElement selector="{{InsertWidgetSection.checkElementStorefrontByName('2','$$createSecondProduct.name$$')}}" stepKey="seeElementByName2"/> <seeElement selector="{{InsertWidgetSection.checkElementStorefrontByName('3','$$createFirstProduct.name$$')}}" stepKey="seeElementByName3"/> <after> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <deleteData createDataKey="createPreReqPage" stepKey="deletePreReqPage" /> <deleteData createDataKey="simplecategory" stepKey="deleteSimpleCategory"/> <deleteData createDataKey="createFirstProduct" stepKey="deleteFirstProduct"/> @@ -85,4 +85,4 @@ <actionGroup ref="logout" stepKey="logout"/> </after> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/CatalogWidget/registration.php b/app/code/Magento/CatalogWidget/registration.php index f9e6a76047ad9..09cd043c70c76 100644 --- a/app/code/Magento/CatalogWidget/registration.php +++ b/app/code/Magento/CatalogWidget/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CatalogWidget', __DIR__); diff --git a/app/code/Magento/Checkout/Block/Cart/Shipping.php b/app/code/Magento/Checkout/Block/Cart/Shipping.php index c52b7fe18814f..712ee84afd232 100644 --- a/app/code/Magento/Checkout/Block/Cart/Shipping.php +++ b/app/code/Magento/Checkout/Block/Cart/Shipping.php @@ -3,54 +3,73 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Checkout\Block\Cart; +use Magento\Checkout\Model\CompositeConfigProvider; +use Magento\Checkout\Block\Checkout\LayoutProcessorInterface; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Serialize\Serializer\JsonHexTag; +use Magento\Framework\View\Element\Template\Context; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Framework\App\ObjectManager; + /** + * Cart Shipping Block + * * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Shipping extends \Magento\Checkout\Block\Cart\AbstractCart { /** - * @var \Magento\Checkout\Model\CompositeConfigProvider + * @var CompositeConfigProvider */ protected $configProvider; /** - * @var array|\Magento\Checkout\Block\Checkout\LayoutProcessorInterface[] + * @var array|LayoutProcessorInterface[] */ protected $layoutProcessors; /** - * @var \Magento\Framework\Serialize\Serializer\Json + * @var Json */ private $serializer; /** - * @param \Magento\Framework\View\Element\Template\Context $context - * @param \Magento\Customer\Model\Session $customerSession - * @param \Magento\Checkout\Model\Session $checkoutSession - * @param \Magento\Checkout\Model\CompositeConfigProvider $configProvider + * @var JsonHexTag + */ + private $jsonHexTagSerializer; + + /** + * @param Context $context + * @param CustomerSession $customerSession + * @param CheckoutSession $checkoutSession + * @param CompositeConfigProvider $configProvider * @param array $layoutProcessors * @param array $data - * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer + * @param Json|null $serializer + * @param JsonHexTag|null $jsonHexTagSerializer * @throws \RuntimeException */ public function __construct( - \Magento\Framework\View\Element\Template\Context $context, - \Magento\Customer\Model\Session $customerSession, - \Magento\Checkout\Model\Session $checkoutSession, - \Magento\Checkout\Model\CompositeConfigProvider $configProvider, + Context $context, + CustomerSession $customerSession, + CheckoutSession $checkoutSession, + CompositeConfigProvider $configProvider, array $layoutProcessors = [], array $data = [], - \Magento\Framework\Serialize\Serializer\Json $serializer = null + Json $serializer = null, + JsonHexTag $jsonHexTagSerializer = null ) { $this->configProvider = $configProvider; $this->layoutProcessors = $layoutProcessors; parent::__construct($context, $customerSession, $checkoutSession, $data); $this->_isScopePrivate = true; - $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\Serialize\Serializer\Json::class); + $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); + $this->jsonHexTagSerializer = $jsonHexTagSerializer ?: ObjectManager::getInstance()->get(JsonHexTag::class); } /** @@ -75,7 +94,7 @@ public function getJsLayout() $this->jsLayout = $processor->process($this->jsLayout); } - return json_encode($this->jsLayout, JSON_HEX_TAG); + return $this->jsonHexTagSerializer->serialize($this->jsLayout); } /** @@ -90,11 +109,13 @@ public function getBaseUrl() } /** + * Get Serialized Checkout Config + * * @return bool|string * @since 100.2.0 */ public function getSerializedCheckoutConfig() { - return json_encode($this->getCheckoutConfig(), JSON_HEX_TAG); + return $this->jsonHexTagSerializer->serialize($this->getCheckoutConfig()); } } diff --git a/app/code/Magento/Checkout/Model/DefaultConfigProvider.php b/app/code/Magento/Checkout/Model/DefaultConfigProvider.php index 70352b50d8de4..fdf49d6765a29 100644 --- a/app/code/Magento/Checkout/Model/DefaultConfigProvider.php +++ b/app/code/Magento/Checkout/Model/DefaultConfigProvider.php @@ -397,6 +397,9 @@ private function getQuoteData() if ($this->checkoutSession->getQuote()->getId()) { $quote = $this->quoteRepository->get($this->checkoutSession->getQuote()->getId()); $quoteData = $quote->toArray(); + if (null !== $quote->getExtensionAttributes()) { + $quoteData['extension_attributes'] = $quote->getExtensionAttributes()->__toArray(); + } $quoteData['is_virtual'] = $quote->getIsVirtual(); if (!$quote->getCustomer()->getId()) { diff --git a/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php b/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php index da29482f0123f..1d15a5dd7f176 100644 --- a/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php +++ b/app/code/Magento/Checkout/Model/GuestPaymentInformationManagement.php @@ -56,11 +56,6 @@ class GuestPaymentInformationManagement implements \Magento\Checkout\Api\GuestPa */ private $logger; - /** - * @var ResourceConnection - */ - private $connectionPool; - /** * @param \Magento\Quote\Api\GuestBillingAddressManagementInterface $billingAddressManagement * @param \Magento\Quote\Api\GuestPaymentMethodManagementInterface $paymentMethodManagement @@ -68,7 +63,6 @@ class GuestPaymentInformationManagement implements \Magento\Checkout\Api\GuestPa * @param \Magento\Checkout\Api\PaymentInformationManagementInterface $paymentInformationManagement * @param \Magento\Quote\Model\QuoteIdMaskFactory $quoteIdMaskFactory * @param CartRepositoryInterface $cartRepository - * @param ResourceConnection $connectionPool * @codeCoverageIgnore */ public function __construct( @@ -77,8 +71,7 @@ public function __construct( \Magento\Quote\Api\GuestCartManagementInterface $cartManagement, \Magento\Checkout\Api\PaymentInformationManagementInterface $paymentInformationManagement, \Magento\Quote\Model\QuoteIdMaskFactory $quoteIdMaskFactory, - CartRepositoryInterface $cartRepository, - ResourceConnection $connectionPool = null + CartRepositoryInterface $cartRepository ) { $this->billingAddressManagement = $billingAddressManagement; $this->paymentMethodManagement = $paymentMethodManagement; @@ -86,7 +79,6 @@ public function __construct( $this->paymentInformationManagement = $paymentInformationManagement; $this->quoteIdMaskFactory = $quoteIdMaskFactory; $this->cartRepository = $cartRepository; - $this->connectionPool = $connectionPool ?: ObjectManager::getInstance()->get(ResourceConnection::class); } /** @@ -98,33 +90,23 @@ public function savePaymentInformationAndPlaceOrder( \Magento\Quote\Api\Data\PaymentInterface $paymentMethod, \Magento\Quote\Api\Data\AddressInterface $billingAddress = null ) { - $salesConnection = $this->connectionPool->getConnection('sales'); - $checkoutConnection = $this->connectionPool->getConnection('checkout'); - $salesConnection->beginTransaction(); - $checkoutConnection->beginTransaction(); - + $this->savePaymentInformation($cartId, $email, $paymentMethod, $billingAddress); try { - $this->savePaymentInformation($cartId, $email, $paymentMethod, $billingAddress); - try { - $orderId = $this->cartManagement->placeOrder($cartId); - } catch (\Magento\Framework\Exception\LocalizedException $e) { - throw new CouldNotSaveException( - __($e->getMessage()), - $e - ); - } catch (\Exception $e) { - $this->getLogger()->critical($e); - throw new CouldNotSaveException( - __('An error occurred on the server. Please try to place the order again.'), - $e - ); - } - $salesConnection->commit(); - $checkoutConnection->commit(); + $orderId = $this->cartManagement->placeOrder($cartId); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->getLogger()->critical( + 'Placing an order with quote_id ' . $cartId . ' is failed: ' . $e->getMessage() + ); + throw new CouldNotSaveException( + __($e->getMessage()), + $e + ); } catch (\Exception $e) { - $salesConnection->rollBack(); - $checkoutConnection->rollBack(); - throw $e; + $this->getLogger()->critical($e); + throw new CouldNotSaveException( + __('An error occurred on the server. Please try to place the order again.'), + $e + ); } return $orderId; @@ -193,7 +175,9 @@ private function limitShippingCarrier(Quote $quote) : void $shippingAddress = $quote->getShippingAddress(); if ($shippingAddress && $shippingAddress->getShippingMethod()) { $shippingRate = $shippingAddress->getShippingRateByCode($shippingAddress->getShippingMethod()); - $shippingAddress->setLimitCarrier($shippingRate->getCarrier()); + if ($shippingRate) { + $shippingAddress->setLimitCarrier($shippingRate->getCarrier()); + } } } } diff --git a/app/code/Magento/Checkout/Model/PaymentInformationManagement.php b/app/code/Magento/Checkout/Model/PaymentInformationManagement.php index 2eced5c642261..1f7931d7d3e6a 100644 --- a/app/code/Magento/Checkout/Model/PaymentInformationManagement.php +++ b/app/code/Magento/Checkout/Model/PaymentInformationManagement.php @@ -9,7 +9,7 @@ use Magento\Framework\Exception\CouldNotSaveException; /** - * Payment information management + * Payment information management service. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -85,6 +85,9 @@ public function savePaymentInformationAndPlaceOrder( try { $orderId = $this->cartManagement->placeOrder($cartId); } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->getLogger()->critical( + 'Placing an order with quote_id ' . $cartId . ' is failed: ' . $e->getMessage() + ); throw new CouldNotSaveException( __($e->getMessage()), $e @@ -124,9 +127,9 @@ public function savePaymentInformation( $shippingAddress = $quote->getShippingAddress(); if ($shippingAddress && $shippingAddress->getShippingMethod()) { $shippingRate = $shippingAddress->getShippingRateByCode($shippingAddress->getShippingMethod()); - $shippingAddress->setLimitCarrier( - $shippingRate ? $shippingRate->getCarrier() : $shippingAddress->getShippingMethod() - ); + if ($shippingRate) { + $shippingAddress->setLimitCarrier($shippingRate->getCarrier()); + } } } $this->paymentMethodManagement->set($cartId, $paymentMethod); diff --git a/app/code/Magento/Checkout/Model/Session.php b/app/code/Magento/Checkout/Model/Session.php index a654c78853d7a..7af00f1df8e95 100644 --- a/app/code/Magento/Checkout/Model/Session.php +++ b/app/code/Magento/Checkout/Model/Session.php @@ -7,6 +7,8 @@ use Magento\Customer\Api\Data\CustomerInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Quote\Api\Data\CartInterface; use Magento\Quote\Model\Quote; use Magento\Quote\Model\QuoteIdMaskFactory; use Psr\Log\LoggerInterface; @@ -21,9 +23,6 @@ */ class Session extends \Magento\Framework\Session\SessionManager { - /** - * Checkout state begin - */ const CHECKOUT_STATE_BEGIN = 'begin'; /** @@ -228,7 +227,7 @@ public function setLoadInactive($load = true) * * @return Quote * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws NoSuchEntityException * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ @@ -273,21 +272,21 @@ public function getQuote() */ $quote = $this->quoteRepository->get($this->getQuoteId()); } - } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + + if ($quote->getTotalsCollectedFlag() === false) { + $quote->collectTotals(); + } + } catch (NoSuchEntityException $e) { $this->setQuoteId(null); } } if (!$this->getQuoteId()) { if ($this->_customerSession->isLoggedIn() || $this->_customer) { - $customerId = $this->_customer - ? $this->_customer->getId() - : $this->_customerSession->getCustomerId(); - try { - $quote = $this->quoteRepository->getActiveForCustomer($customerId); - $this->setQuoteId($quote->getId()); - } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { - $this->logger->critical($e); + $quoteByCustomer = $this->getQuoteByCustomer(); + if ($quoteByCustomer !== null) { + $this->setQuoteId($quoteByCustomer->getId()); + $quote = $quoteByCustomer; } } else { $quote->setIsCheckoutCart(true); @@ -375,7 +374,7 @@ public function loadCustomerQuote() try { $customerQuote = $this->quoteRepository->getForCustomer($this->_customerSession->getCustomerId()); - } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + } catch (NoSuchEntityException $e) { $customerQuote = $this->quoteFactory->create(); } $customerQuote->setStoreId($this->_storeManager->getStore()->getId()); @@ -558,7 +557,7 @@ public function restoreQuote() $this->replaceQuote($quote)->unsLastRealOrderId(); $this->_eventManager->dispatch('restore_quote', ['order' => $order, 'quote' => $quote]); return true; - } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + } catch (NoSuchEntityException $e) { $this->logger->critical($e); } } @@ -588,4 +587,22 @@ protected function isQuoteMasked() { return $this->isQuoteMasked; } + + /** + * Returns quote for customer if there is any + */ + private function getQuoteByCustomer(): ?CartInterface + { + $customerId = $this->_customer + ? $this->_customer->getId() + : $this->_customerSession->getCustomerId(); + + try { + $quote = $this->quoteRepository->getActiveForCustomer($customerId); + } catch (NoSuchEntityException $e) { + $quote = null; + } + + return $quote; + } } diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertIsNotVisibleCartPagerTextActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertIsNotVisibleCartPagerTextActionGroup.xml new file mode 100644 index 0000000000000..2072cb6df1dc1 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertIsNotVisibleCartPagerTextActionGroup.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="AssertPagerTextIsNotVisibleActionGroup"> + <arguments> + <argument name="text" type="string"/> + </arguments> + <waitForPageLoad stepKey="waitForCheckoutPageLoad"/> + <dontSee userInput="{{text}}" selector="{{StorefrontCartToolbarSection.toolbarNumber}}" stepKey="VerifyMissingPagerText"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertMiniCartEmptyActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertMiniCartEmptyActionGroup.xml new file mode 100644 index 0000000000000..649421a53040b --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertMiniCartEmptyActionGroup.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="AssertMiniCartEmptyActionGroup"> + <annotations> + <description>Validates that the provided Product Count appears in the Storefront Header next to the Shopping Cart icon. Clicks on the Mini Shopping Cart icon. Validates that the 'No Items' message is present and correct in the Storefront Mini Shopping Cart.</description> + </annotations> + + <dontSeeElement selector="{{StorefrontMinicartSection.productCount}}" stepKey="dontSeeMinicartProductCount"/> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="expandMinicart"/> + <see selector="{{StorefrontMinicartSection.minicartContent}}" userInput="You have no items in your shopping cart." stepKey="seeEmptyCartMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertOneProductNameInMiniCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertOneProductNameInMiniCartActionGroup.xml new file mode 100644 index 0000000000000..de54c78fc28ff --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertOneProductNameInMiniCartActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertOneProductNameInMiniCartActionGroup"> + <annotations> + <description>Validates that the provided Product Name is present in the Storefront Mini Shopping Cart.</description> + </annotations> + <arguments> + <argument name="productName"/> + </arguments> + + <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> + <see selector="{{StorefrontMinicartSection.miniCartItemsText}}" userInput="{{productName}}" stepKey="seeInMiniCart"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontCheckoutPaymentSummarySubtotalActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontCheckoutPaymentSummarySubtotalActionGroup.xml new file mode 100644 index 0000000000000..bbf578fb8ca8f --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontCheckoutPaymentSummarySubtotalActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontCheckoutPaymentSummarySubtotalActionGroup"> + <arguments> + <argument name="orderSubtotal" type="string"/> + </arguments> + <waitForPageLoad time="30" stepKey="waitForCartFullyLoaded"/> + <waitForElementVisible selector="{{CheckoutPaymentSection.orderSummarySubtotal}}" time="30" stepKey="waitForOrderSummaryBlock"/> + <see selector="{{CheckoutPaymentSection.orderSummarySubtotal}}" userInput="{{orderSubtotal}}" stepKey="seeCorrectSubtotal"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontCheckoutPaymentSummaryTotalActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontCheckoutPaymentSummaryTotalActionGroup.xml new file mode 100644 index 0000000000000..626e9d7a98183 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontCheckoutPaymentSummaryTotalActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontCheckoutPaymentSummaryTotalActionGroup"> + <arguments> + <argument name="orderTotal" type="string"/> + </arguments> + <waitForPageLoad time="30" stepKey="waitForCartFullyLoaded"/> + <waitForElementVisible selector="{{CheckoutPaymentSection.orderSummaryTotal}}" time="30" stepKey="waitForOrderSummaryBlock"/> + <see selector="{{CheckoutPaymentSection.orderSummaryTotal}}" userInput="{{orderTotal}}" stepKey="seeCorrectOrderTotal"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontCheckoutPaymentSummaryTotalMissingActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontCheckoutPaymentSummaryTotalMissingActionGroup.xml new file mode 100644 index 0000000000000..874d2921022b1 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontCheckoutPaymentSummaryTotalMissingActionGroup.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="AssertStorefrontCheckoutPaymentSummaryTotalMissingActionGroup"> + <waitForPageLoad time="30" stepKey="waitForCartFullyLoaded"/> + <dontSeeElement selector="{{CheckoutPaymentSection.orderSummaryTotal}}" stepKey="seeTotalElement"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontMiniCartItemsActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontMiniCartItemsActionGroup.xml index 8933ebbc1dd84..5545d7ad437a1 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontMiniCartItemsActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontMiniCartItemsActionGroup.xml @@ -25,17 +25,4 @@ <seeElement selector="{{StorefrontMinicartSection.productImage}}" stepKey="seeProductImage"/> <see selector="{{StorefrontMinicartSection.productSubTotal}}" userInput="{{cartSubtotal}}" stepKey="seeSubTotal"/> </actionGroup> - - <actionGroup name="AssertStorefrontMiniCartProductDetailsAbsentActionGroup"> - <annotations> - <description>Validates that the provided Product details (Name, Price) are - not present in the Storefront Mini Shopping Cart.</description> - </annotations> - <arguments> - <argument name="productName" type="string"/> - <argument name="productPrice" type="string"/> - </arguments> - - <dontSee selector="{{StorefrontMinicartSection.productPriceByName(productName)}}" userInput="{{productPrice}}" stepKey="dontSeeProductPriceInMiniCart"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontMiniCartProductDetailsAbsentActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontMiniCartProductDetailsAbsentActionGroup.xml new file mode 100644 index 0000000000000..5d52feeaad9cc --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontMiniCartProductDetailsAbsentActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontMiniCartProductDetailsAbsentActionGroup"> + <annotations> + <description>Validates that the provided Product details (Name, Price) are + not present in the Storefront Mini Shopping Cart.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + <argument name="productPrice" type="string"/> + </arguments> + + <dontSee selector="{{StorefrontMinicartSection.productPriceByName(productName)}}" userInput="{{productPrice}}" stepKey="dontSeeProductPriceInMiniCart"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontNotCalculatedValueInShippingTotalInOrderSummaryActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontNotCalculatedValueInShippingTotalInOrderSummaryActionGroup.xml new file mode 100644 index 0000000000000..1ec42033a782b --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontNotCalculatedValueInShippingTotalInOrderSummaryActionGroup.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="AssertStorefrontNotCalculatedValueInShippingTotalInOrderSummaryActionGroup"> + <annotations> + <description>Validates value of the Shipping total is not calculated.</description> + </annotations> + + <arguments> + <argument name="value" defaultValue="Not yet calculated" type="string"/> + </arguments> + <waitForElementVisible selector="{{CheckoutOrderSummarySection.shippingTotalNotYetCalculated}}" time="30" stepKey="waitForShippingTotalToBeVisible"/> + <see selector="{{CheckoutOrderSummarySection.shippingTotalNotYetCalculated}}" userInput="{{value}}" stepKey="assertShippingTotalIsNotYetCalculated"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontOrderCannotBePlacedActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontOrderCannotBePlacedActionGroup.xml new file mode 100644 index 0000000000000..4f9555d84898d --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontOrderCannotBePlacedActionGroup.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="AssertStorefrontOrderCannotBePlacedActionGroup"> + <annotations> + <description>Validates order cannot be placed and checks error message.</description> + </annotations> + + <arguments> + <argument name="error" type="string"/> + </arguments> + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> + <click selector="{{CheckoutPaymentSection.placeOrderWithoutTimeout}}" stepKey="clickPlaceOrder"/> + <waitForElement selector="{{CheckoutCartMessageSection.errorMessage}}" time="30" stepKey="waitForErrorMessage"/> + <see selector="{{CheckoutCartMessageSection.errorMessage}}" userInput="{{error}}" stepKey="assertErrorMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontShippingLabelDescriptionInOrderSummaryActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontShippingLabelDescriptionInOrderSummaryActionGroup.xml new file mode 100644 index 0000000000000..6a8efdb507c3e --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontShippingLabelDescriptionInOrderSummaryActionGroup.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="AssertStorefrontShippingLabelDescriptionInOrderSummaryActionGroup"> + <annotations> + <description>Validates that the Shipping label description is present and correct.</description> + </annotations> + + <arguments> + <argument name="labelDescription" type="string"/> + </arguments> + <waitForElementVisible selector="{{CheckoutOrderSummarySection.orderSummaryShippingTotalLabelDescription}}" time="30" stepKey="waitForElement"/> + <see selector="{{CheckoutOrderSummarySection.orderSummaryShippingTotalLabelDescription}}" userInput="{{labelDescription}}" stepKey="seeShippingMethodLabelDescription"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertToolbarTextIsVisibleInCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertToolbarTextIsVisibleInCartActionGroup.xml new file mode 100644 index 0000000000000..6ede2a0dd5388 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertToolbarTextIsVisibleInCartActionGroup.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="AssertToolbarTextIsVisibleInCartActionGroup"> + <arguments> + <argument name="text" type="string"/> + </arguments> + <waitForPageLoad stepKey="waitForPageLoad"/> + <see userInput="{{text}}" selector="{{StorefrontCartToolbarSection.toolbarNumber}}" stepKey="VerifyPageText"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ChangeSummaryQuoteAddressActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ChangeSummaryQuoteAddressActionGroup.xml new file mode 100644 index 0000000000000..041d3530a7e82 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ChangeSummaryQuoteAddressActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ChangeSummaryQuoteAddressActionGroup"> + <annotations> + <description>Fills in the provided Address details (Country, State and Zip Code) under the 'Summary' section on the Storefront Shopping Cart page.</description> + </annotations> + <arguments> + <argument name="taxCode"/> + </arguments> + + <conditionalClick stepKey="openShippingDetails" selector="{{CheckoutCartSummarySection.shippingHeading}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false"/> + <selectOption stepKey="selectCountry" selector="{{CheckoutCartSummarySection.country}}" userInput="{{taxCode.country}}"/> + <selectOption stepKey="selectStateProvince" selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="{{taxCode.state}}"/> + <fillField stepKey="fillZip" selector="{{CheckoutCartSummarySection.postcode}}" userInput="{{taxCode.zip}}"/> + <waitForPageLoad stepKey="waitForFormUpdate"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckBillingAddressInCheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckBillingAddressInCheckoutActionGroup.xml new file mode 100644 index 0000000000000..d0128a9913fe9 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckBillingAddressInCheckoutActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CheckBillingAddressInCheckoutActionGroup"> + <annotations> + <description>Validates that the provided Customer and Address details are present on the Storefront Checkout page under the 'Payment Method' section.</description> + </annotations> + <arguments> + <argument name="customerVar"/> + <argument name="customerAddressVar"/> + </arguments> + + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> + <see userInput="{{customerVar.firstName}}" selector="{{CheckoutPaymentSection.billingAddress}}" stepKey="assertBillingAddressFirstName"/> + <see userInput="{{customerVar.lastName}}" selector="{{CheckoutPaymentSection.billingAddress}}" stepKey="assertBillingAddressLastName"/> + <see userInput="{{customerAddressVar.street[0]}}" selector="{{CheckoutPaymentSection.billingAddress}}" stepKey="assertBillingAddressStreet"/> + <see userInput="{{customerAddressVar.city}}" selector="{{CheckoutPaymentSection.billingAddress}}" stepKey="assertBillingAddressCity"/> + <see userInput="{{customerAddressVar.state}}" selector="{{CheckoutPaymentSection.billingAddress}}" stepKey="assertBillingAddressState"/> + <see userInput="{{customerAddressVar.postcode}}" selector="{{CheckoutPaymentSection.billingAddress}}" stepKey="assertBillingAddressPostcode"/> + <see userInput="{{customerAddressVar.telephone}}" selector="{{CheckoutPaymentSection.billingAddress}}" stepKey="assertBillingAddressTelephone"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckBillingAddressInCheckoutWithBillingAddressOnPaymentPageActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckBillingAddressInCheckoutWithBillingAddressOnPaymentPageActionGroup.xml new file mode 100644 index 0000000000000..adba81138c17d --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckBillingAddressInCheckoutWithBillingAddressOnPaymentPageActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CheckBillingAddressInCheckoutWithBillingAddressOnPaymentPageActionGroup"> + <annotations> + <description>Validates that the provided Customer and Address details appear on the Storefront Checkout page under the 'Billing Address' section.</description> + </annotations> + <arguments> + <argument name="customerVar"/> + <argument name="customerAddressVar"/> + </arguments> + + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> + <see userInput="{{customerVar.firstName}}" selector="{{CheckoutPaymentWithDisplayBillingAddressOnPaymentPageSection.billingAddressDetails}}" stepKey="assertBillingAddressDetailsFirstName"/> + <see userInput="{{customerVar.lastName}}" selector="{{CheckoutPaymentWithDisplayBillingAddressOnPaymentPageSection.billingAddressDetails}}" stepKey="assertBillingAddressDetailsLastName"/> + <see userInput="{{customerAddressVar.street[0]}}" selector="{{CheckoutPaymentWithDisplayBillingAddressOnPaymentPageSection.billingAddressDetails}}" stepKey="assertBillingAddressDetailsStreet"/> + <see userInput="{{customerAddressVar.city}}" selector="{{CheckoutPaymentWithDisplayBillingAddressOnPaymentPageSection.billingAddressDetails}}" stepKey="assertBillingAddressDetailsCity"/> + <see userInput="{{customerAddressVar.state}}" selector="{{CheckoutPaymentWithDisplayBillingAddressOnPaymentPageSection.billingAddressDetails}}" stepKey="assertBillingAddressDetailsState"/> + <see userInput="{{customerAddressVar.postcode}}" selector="{{CheckoutPaymentWithDisplayBillingAddressOnPaymentPageSection.billingAddressDetails}}" stepKey="assertBillingAddressDetailsPostcode"/> + <see userInput="{{customerAddressVar.telephone}}" selector="{{CheckoutPaymentWithDisplayBillingAddressOnPaymentPageSection.billingAddressDetails}}" stepKey="assertBillingAddressDetailsTelephone"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckOrderSummaryInCheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckOrderSummaryInCheckoutActionGroup.xml new file mode 100644 index 0000000000000..9db5021e97322 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckOrderSummaryInCheckoutActionGroup.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="CheckOrderSummaryInCheckoutActionGroup"> + <annotations> + <description>Validates that the provided Subtotal, Shipping Total, Shipping Method and Total are present and correct on the Storefront Checkout page under the 'Order Summary' section.</description> + </annotations> + <arguments> + <argument name="subtotal" type="string"/> + <argument name="shippingTotal" type="string"/> + <argument name="shippingMethod" type="string"/> + <argument name="total" type="string"/> + </arguments> + + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> + <see userInput="{{subtotal}}" selector="{{CheckoutPaymentSection.orderSummarySubtotal}}" stepKey="assertSubtotal"/> + <see userInput="{{shippingTotal}}" selector="{{CheckoutPaymentSection.orderSummaryShippingTotal}}" stepKey="assertShipping"/> + <see userInput="{{shippingMethod}}" selector="{{CheckoutPaymentSection.orderSummaryShippingMethod}}" stepKey="assertShippingMethod"/> + <see userInput="{{total}}" selector="{{CheckoutPaymentSection.orderSummaryTotal}}" stepKey="assertTotal"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckProductInCheckoutCartItemsActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckProductInCheckoutCartItemsActionGroup.xml new file mode 100644 index 0000000000000..85ec765ecd64f --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckProductInCheckoutCartItemsActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CheckProductInCheckoutCartItemsActionGroup"> + <annotations> + <description>Validates the provided Product appears in the Storefront Checkout 'Order Summary' section.</description> + </annotations> + <arguments> + <argument name="productVar"/> + </arguments> + + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> + <conditionalClick selector="{{CheckoutPaymentSection.cartItemsArea}}" dependentSelector="{{CheckoutPaymentSection.cartItemsArea}}" visible="true" stepKey="exposeMiniCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskForCartItem"/> + <waitForElement selector="{{CheckoutPaymentSection.cartItemsAreaActive}}" time="30" stepKey="waitForCartItemsAreaActive"/> + <see selector="{{CheckoutPaymentSection.cartItems}}" userInput="{{productVar.name}}" stepKey="seeProductInCart"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckSelectedShippingAddressInCheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckSelectedShippingAddressInCheckoutActionGroup.xml new file mode 100644 index 0000000000000..0c952af6d53fa --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckSelectedShippingAddressInCheckoutActionGroup.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="CheckSelectedShippingAddressInCheckoutActionGroup"> + <annotations> + <description>Validates that the provided Customer and Address details are listed on the Storefront Checkout page under the 'Shipping Address' section when multiple Addresses are present for a Customer.</description> + </annotations> + <arguments> + <argument name="customerVar"/> + <argument name="customerAddressVar"/> + </arguments> + + <waitForElement selector="{{CheckoutShippingSection.shippingTab}}" time="30" stepKey="waitForShippingSectionLoaded"/> + <see stepKey="VerifyFirstNameInSelectedAddress" selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{customerVar.firstname}}"/> + <see stepKey="VerifyLastNameInSelectedAddress" selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{customerVar.lastname}}"/> + <see stepKey="VerifyStreetInSelectedAddress" selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{customerAddressVar.street[0]}}"/> + <see stepKey="VerifyCityInSelectedAddress" selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{customerAddressVar.city}}"/> + <see stepKey="VerifyZipInSelectedAddress" selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{customerAddressVar.postcode}}"/> + <see stepKey="VerifyPhoneInSelectedAddress" selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{customerAddressVar.telephone}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckShipToInformationInCheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckShipToInformationInCheckoutActionGroup.xml new file mode 100644 index 0000000000000..f2ededd7f4b17 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckShipToInformationInCheckoutActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CheckShipToInformationInCheckoutActionGroup"> + <annotations> + <description>Validates that the provided Customer and Address details are present and correct on the Storefront Checkout page under the 'Ship To' section.</description> + </annotations> + <arguments> + <argument name="customerVar"/> + <argument name="customerAddressVar"/> + </arguments> + + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> + <see userInput="{{customerVar.firstname}}" selector="{{CheckoutPaymentSection.shipToInformation}}" stepKey="assertShipToInformationFirstName"/> + <see userInput="{{customerVar.lastname}}" selector="{{CheckoutPaymentSection.shipToInformation}}" stepKey="assertShipToInformationLastName"/> + <see userInput="{{customerAddressVar.street[0]}}" selector="{{CheckoutPaymentSection.shipToInformation}}" stepKey="assertShipToInformationStreet"/> + <see userInput="{{customerAddressVar.city}}" selector="{{CheckoutPaymentSection.shipToInformation}}" stepKey="assertShipToInformationCity"/> + <see userInput="{{customerAddressVar.state}}" selector="{{CheckoutPaymentSection.shipToInformation}}" stepKey="assertShipToInformationState"/> + <see userInput="{{customerAddressVar.postcode}}" selector="{{CheckoutPaymentSection.shipToInformation}}" stepKey="assertShipToInformationPostcode"/> + <see userInput="{{customerAddressVar.telephone}}" selector="{{CheckoutPaymentSection.shipToInformation}}" stepKey="assertShipToInformationTelephone"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckShippingMethodInCheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckShippingMethodInCheckoutActionGroup.xml new file mode 100644 index 0000000000000..968ee8b46d0ba --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckShippingMethodInCheckoutActionGroup.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="CheckShippingMethodInCheckoutActionGroup"> + <annotations> + <description>Validates that the provided Shipping Method Name is present on the Storefront Checkout page under the 'Shipping Method' section.</description> + </annotations> + <arguments> + <argument name="shippingMethod"/> + </arguments> + + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> + <see userInput="{{shippingMethod}}" selector="{{CheckoutPaymentSection.shippingMethodInformation}}" stepKey="assertshippingMethodInformation"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckTotalsSortOrderInSummarySectionActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckTotalsSortOrderInSummarySectionActionGroup.xml new file mode 100644 index 0000000000000..4bd2d40ab94f1 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckTotalsSortOrderInSummarySectionActionGroup.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="CheckTotalsSortOrderInSummarySectionActionGroup"> + <annotations> + <description>Validates that the provided Element Name appears at the provided Position in the Storefront Checkout 'Order Summary' section.</description> + </annotations> + <arguments> + <argument name="elementName" type="string"/> + <argument name="positionNumber" type="string"/> + </arguments> + + <see userInput="{{elementName}}" selector="{{CheckoutCartSummarySection.elementPosition(positionNumber)}}" stepKey="assertElementPosition"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml deleted file mode 100644 index e0519a126aa48..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml +++ /dev/null @@ -1,424 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!-- Checkout select Flat Rate shipping method --> - <actionGroup name="CheckoutSelectFlatRateShippingMethodActionGroup"> - <annotations> - <description>Clicks on the 'Flat Rate' Shipping Method on the Storefront Checkout page.</description> - </annotations> - - <conditionalClick selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('Flat Rate')}}" dependentSelector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('Flat Rate')}}" visible="true" stepKey="selectFlatRateShippingMethod"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskForNextButton"/> - </actionGroup> - - <!-- Go to checkout from minicart --> - <actionGroup name="GoToCheckoutFromMinicartActionGroup"> - <annotations> - <description>Clicks on the Storefront Mini Shopping Cart icon. Clicks on 'Proceed to Checkout'.</description> - </annotations> - - <waitForElementNotVisible selector="{{StorefrontMinicartSection.emptyCart}}" stepKey="waitUpdateQuantity"/> - <wait time="5" stepKey="waitMinicartRendering"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> - <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> - </actionGroup> - - <!-- Go to checkout from cart --> - <actionGroup name="GoToCheckoutFromCartActionGroup"> - <annotations> - <description>Clicks on the 'View and Edit Cart' link in the Storefront Mini Shopping Cart. Validates that the Storefront Shopping Cart URL is present and correct. Clicks on 'Proceed to Checkout'.</description> - </annotations> - - <waitForElementNotVisible selector="{{StorefrontMinicartSection.emptyCart}}" stepKey="waitUpdateQuantity"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - <seeInCurrentUrl url="{{CheckoutCartPage.url}}" stepKey="assertCheckoutCartUrl"/> - <click selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="goToCheckout"/> - </actionGroup> - - <!-- Guest checkout filling shipping section --> - <actionGroup name="GuestCheckoutFillingShippingSectionActionGroup"> - <annotations> - <description>Fills in the provided Customer/Address (Including Region) details on the Storefront Checkout page under the 'Shipping Address' section. Selects the provided Shipping Method. Clicks on Next. Validates that the URL is present and correct.</description> - </annotations> - <arguments> - <argument name="customerVar" defaultValue="CustomerEntityOne"/> - <argument name="customerAddressVar" defaultValue="CustomerAddressSimple"/> - <!--First available shipping method will be selected if value is not passed for shippingMethod--> - <argument name="shippingMethod" defaultValue="" type="string"/> - </arguments> - - <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{customerVar.email}}" stepKey="enterEmail"/> - <fillField selector="{{CheckoutShippingSection.firstName}}" userInput="{{customerVar.firstname}}" stepKey="enterFirstName"/> - <fillField selector="{{CheckoutShippingSection.lastName}}" userInput="{{customerVar.lastname}}" stepKey="enterLastName"/> - <fillField selector="{{CheckoutShippingSection.street}}" userInput="{{customerAddressVar.street[0]}}" stepKey="enterStreet"/> - <fillField selector="{{CheckoutShippingSection.city}}" userInput="{{customerAddressVar.city}}" stepKey="enterCity"/> - <selectOption selector="{{CheckoutShippingSection.region}}" userInput="{{customerAddressVar.state}}" stepKey="selectRegion"/> - <fillField selector="{{CheckoutShippingSection.postcode}}" userInput="{{customerAddressVar.postcode}}" stepKey="enterPostcode"/> - <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{customerAddressVar.telephone}}" stepKey="enterTelephone"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - <waitForElement selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('shippingMethod')}}" stepKey="waitForShippingMethod"/> - <click selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('shippingMethod')}}" stepKey="selectShippingMethod"/> - <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> - <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> - <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> - <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> - </actionGroup> - - <actionGroup name="GuestCheckoutFillShippingNoWaitForPaymentActionGroup" extends="GuestCheckoutFillingShippingSectionActionGroup"> - <annotations> - <description>EXTENDS: GuestCheckoutFillingShippingSectionActionGroup. Removed 'waitForPaymentSectionLoaded' and 'assertCheckoutPaymentUrl'.</description> - </annotations> - - <remove keyForRemoval="waitForPaymentSectionLoaded"/> - <remove keyForRemoval="assertCheckoutPaymentUrl"/> - </actionGroup> - - <!-- Guest checkout filling shipping section without region --> - <actionGroup name="GuestCheckoutFillingShippingSectionWithoutRegionActionGroup"> - <annotations> - <description>Fills in the provided Customer/Address (Excluding Region) details on the Storefront Checkout page under the 'Shipping Address' section. Selects the provided Shipping Method. Clicks on Next. Validates that the URL is present and correct.</description> - </annotations> - <arguments> - <argument name="customerVar"/> - <argument name="customerAddressVar"/> - </arguments> - - <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{customerVar.email}}" stepKey="enterEmail"/> - <fillField selector="{{CheckoutShippingSection.firstName}}" userInput="{{customerVar.firstname}}" stepKey="enterFirstName"/> - <fillField selector="{{CheckoutShippingSection.lastName}}" userInput="{{customerVar.lastname}}" stepKey="enterLastName"/> - <fillField selector="{{CheckoutShippingSection.street}}" userInput="{{customerAddressVar.street[0]}}" stepKey="enterStreet"/> - <fillField selector="{{CheckoutShippingSection.city}}" userInput="{{customerAddressVar.city}}" stepKey="enterCity"/> - <fillField selector="{{CheckoutShippingSection.postcode}}" userInput="{{customerAddressVar.postcode}}" stepKey="enterPostcode"/> - <selectOption selector="{{CheckoutShippingSection.country}}" userInput="{{customerAddressVar.country_id}}" stepKey="enterCountry"/> - <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{customerAddressVar.telephone}}" stepKey="enterTelephone"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - <click selector="{{CheckoutShippingSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/> - <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> - <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> - <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> - <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> - </actionGroup> - - <!-- Guest checkout filling shipping section with unavailable payments--> - <actionGroup name="GuestCheckoutFillingShippingSectionUnavailablePaymentActionGroup"> - <annotations> - <description>Fills in the provided Customer/Address (Including Region) details on the Storefront Checkout page under the 'Shipping Address' section. Selects the 1st Shipping Method. Clicks on Next. Validates that the Payment Error Message and URL are present and correct.</description> - </annotations> - <arguments> - <argument name="customerVar"/> - <argument name="customerAddressVar"/> - </arguments> - - <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{customerVar.email}}" stepKey="enterEmail"/> - <fillField selector="{{CheckoutShippingSection.firstName}}" userInput="{{customerVar.firstname}}" stepKey="enterFirstName"/> - <fillField selector="{{CheckoutShippingSection.lastName}}" userInput="{{customerVar.lastname}}" stepKey="enterLastName"/> - <fillField selector="{{CheckoutShippingSection.street}}" userInput="{{customerAddressVar.street[0]}}" stepKey="enterStreet"/> - <fillField selector="{{CheckoutShippingSection.city}}" userInput="{{customerAddressVar.city}}" stepKey="enterCity"/> - <selectOption selector="{{CheckoutShippingSection.region}}" userInput="{{customerAddressVar.state}}" stepKey="selectRegion"/> - <fillField selector="{{CheckoutShippingSection.postcode}}" userInput="{{customerAddressVar.postcode}}" stepKey="enterPostcode"/> - <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{customerAddressVar.telephone}}" stepKey="enterTelephone"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - <click selector="{{CheckoutShippingSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/> - <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> - <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> - <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> - <waitForElementVisible selector="{{CheckoutPaymentSection.noQuotes}}" stepKey="waitMessage"/> - <see userInput="No Payment method available." stepKey="checkMessage"/> - </actionGroup> - - <actionGroup name="GuestCheckoutWithSpecificCountryOptionForPaymentMethodActionGroup" extends="GuestCheckoutFillingShippingSectionUnavailablePaymentActionGroup"> - <annotations> - <description>EXTENDS: GuestCheckoutFillingShippingSectionUnavailablePaymentActionGroup. Removes 'checkMessage'. Validates that the provided Payment Method Name is NOT present on the Storefront Checkout page.</description> - </annotations> - <arguments> - <argument name="paymentMethod" type="string"/> - </arguments> - - <remove keyForRemoval="checkMessage"/> - <dontSee selector="{{CheckoutPaymentSection.paymentMethodByName(paymentMethod)}}" stepKey="paymentMethodDoesNotAvailable"/> - </actionGroup> - - <!-- Logged in user checkout filling shipping section --> - <actionGroup name="LoggedInUserCheckoutFillingShippingSectionActionGroup"> - <annotations> - <description>Fills in the provided Customer/Address (Including Region) details on the Storefront Checkout page under the 'Shipping Address' section. Selects the 1st Shipping Method. Clicks on Next. Validates that the Payment Error Message and URL are present and correct.</description> - </annotations> - <arguments> - <argument name="customerVar" defaultValue="CustomerEntityOne"/> - <argument name="customerAddressVar" defaultValue="CustomerAddressSimple"/> - </arguments> - - <fillField selector="{{CheckoutShippingSection.firstName}}" userInput="{{customerVar.firstname}}" stepKey="enterFirstName"/> - <fillField selector="{{CheckoutShippingSection.lastName}}" userInput="{{customerVar.lastname}}" stepKey="enterLastName"/> - <fillField selector="{{CheckoutShippingSection.street}}" userInput="{{customerAddressVar.street[0]}}" stepKey="enterStreet"/> - <fillField selector="{{CheckoutShippingSection.city}}" userInput="{{customerAddressVar.city}}" stepKey="enterCity"/> - <selectOption selector="{{CheckoutShippingSection.region}}" userInput="{{customerAddressVar.state}}" stepKey="selectRegion"/> - <fillField selector="{{CheckoutShippingSection.postcode}}" userInput="{{customerAddressVar.postcode}}" stepKey="enterPostcode"/> - <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{customerAddressVar.telephone}}" stepKey="enterTelephone"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - <click selector="{{CheckoutShippingSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/> - <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> - <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> - <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> - <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> - </actionGroup> - - <!-- Logged in user checkout filling shipping section --> - <actionGroup name="LoggedInUserCheckoutAddNewShippingSectionWithoutRegionActionGroup"> - <annotations> - <description>Fills in the provided Customer/Address (Excluding Region) details on the Storefront Checkout page under the 'Shipping Address' section. Selects the 1st Shipping Method. Clicks on Next. Validates that the Payment Error Message and URL are present and correct.</description> - </annotations> - <arguments> - <argument name="customerVar"/> - <argument name="customerAddressVar"/> - </arguments> - - <fillField selector="{{CheckoutShippingSection.firstName}}" userInput="{{customerVar.firstname}}" stepKey="enterFirstName"/> - <fillField selector="{{CheckoutShippingSection.lastName}}" userInput="{{customerVar.lastname}}" stepKey="enterLastName"/> - <fillField selector="{{CheckoutShippingSection.street}}" userInput="{{customerAddressVar.street[0]}}" stepKey="enterStreet"/> - <fillField selector="{{CheckoutShippingSection.city}}" userInput="{{customerAddressVar.city}}" stepKey="enterCity"/> - <fillField selector="{{CheckoutShippingSection.postcode}}" userInput="{{customerAddressVar.postcode}}" stepKey="enterPostcode"/> - <selectOption selector="{{CheckoutShippingSection.country}}" userInput="{{customerAddressVar.country_id}}" stepKey="enterCountry"/> - <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{customerAddressVar.telephone}}" stepKey="enterTelephone"/> - <click selector="{{CheckoutShippingSection.saveAddress}}" stepKey="clickSaveAddress"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - <click selector="{{CheckoutShippingSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/> - <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> - <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> - <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> - <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> - </actionGroup> - - <!-- Place order with logged the user --> - <actionGroup name="PlaceOrderWithLoggedUserActionGroup"> - <annotations> - <description>Clicks on 'Proceed to Checkout' on the Storefront Shopping Cart page. Selects the provided Shipping Method. Clicks on Next. Clicks on Place Order. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <!--First available shipping method will be selected if value is not passed for shippingMethod--> - <argument name="shippingMethod" defaultValue="" type="string"/> - </arguments> - - <waitForElementVisible selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="waitProceedToCheckout"/> - <click selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="clickProceedToCheckout"/> - <click selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('shippingMethod')}}" stepKey="selectShippingMethod"/> - <waitForElement selector="{{CheckoutShippingSection.next}}" stepKey="waitForNextButton"/> - <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> - <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" stepKey="waitForPaymentSectionLoaded"/> - <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> - <waitForElementVisible selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="waitForPlaceOrderButton"/> - <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> - <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage"/> - </actionGroup> - - <!-- Check product in checkout cart items --> - <actionGroup name="CheckProductInCheckoutCartItemsActionGroup"> - <annotations> - <description>Validates the provided Product appears in the Storefront Checkout 'Order Summary' section.</description> - </annotations> - <arguments> - <argument name="productVar"/> - </arguments> - - <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> - <conditionalClick selector="{{CheckoutPaymentSection.cartItemsArea}}" dependentSelector="{{CheckoutPaymentSection.cartItemsArea}}" visible="true" stepKey="exposeMiniCart"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskForCartItem"/> - <waitForElement selector="{{CheckoutPaymentSection.cartItemsAreaActive}}" time="30" stepKey="waitForCartItemsAreaActive"/> - <see selector="{{CheckoutPaymentSection.cartItems}}" userInput="{{productVar.name}}" stepKey="seeProductInCart"/> - </actionGroup> - - <!-- Check order summary in checkout --> - <actionGroup name="CheckOrderSummaryInCheckoutActionGroup"> - <annotations> - <description>Validates that the provided Subtotal, Shipping Total, Shipping Method and Total are present and correct on the Storefront Checkout page under the 'Order Summary' section.</description> - </annotations> - <arguments> - <argument name="subtotal" type="string"/> - <argument name="shippingTotal" type="string"/> - <argument name="shippingMethod" type="string"/> - <argument name="total" type="string"/> - </arguments> - - <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> - <see userInput="{{subtotal}}" selector="{{CheckoutPaymentSection.orderSummarySubtotal}}" stepKey="assertSubtotal"/> - <see userInput="{{shippingTotal}}" selector="{{CheckoutPaymentSection.orderSummaryShippingTotal}}" stepKey="assertShipping"/> - <see userInput="{{shippingMethod}}" selector="{{CheckoutPaymentSection.orderSummaryShippingMethod}}" stepKey="assertShippingMethod"/> - <see userInput="{{total}}" selector="{{CheckoutPaymentSection.orderSummaryTotal}}" stepKey="assertTotal"/> - </actionGroup> - - <actionGroup name="CheckTotalsSortOrderInSummarySection"> - <annotations> - <description>Validates that the provided Element Name appears at the provided Position in the Storefront Checkout 'Order Summary' section.</description> - </annotations> - <arguments> - <argument name="elementName" type="string"/> - <argument name="positionNumber" type="string"/> - </arguments> - - <see userInput="{{elementName}}" selector="{{CheckoutCartSummarySection.elementPosition(positionNumber)}}" stepKey="assertElementPosition"/> - </actionGroup> - - <!-- Check ship to information in checkout --> - <actionGroup name="CheckShipToInformationInCheckoutActionGroup"> - <annotations> - <description>Validates that the provided Customer and Address details are present and correct on the Storefront Checkout page under the 'Ship To' section.</description> - </annotations> - <arguments> - <argument name="customerVar"/> - <argument name="customerAddressVar"/> - </arguments> - - <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> - <see userInput="{{customerVar.firstname}}" selector="{{CheckoutPaymentSection.shipToInformation}}" stepKey="assertShipToInformationFirstName"/> - <see userInput="{{customerVar.lastname}}" selector="{{CheckoutPaymentSection.shipToInformation}}" stepKey="assertShipToInformationLastName"/> - <see userInput="{{customerAddressVar.street[0]}}" selector="{{CheckoutPaymentSection.shipToInformation}}" stepKey="assertShipToInformationStreet"/> - <see userInput="{{customerAddressVar.city}}" selector="{{CheckoutPaymentSection.shipToInformation}}" stepKey="assertShipToInformationCity"/> - <see userInput="{{customerAddressVar.state}}" selector="{{CheckoutPaymentSection.shipToInformation}}" stepKey="assertShipToInformationState"/> - <see userInput="{{customerAddressVar.postcode}}" selector="{{CheckoutPaymentSection.shipToInformation}}" stepKey="assertShipToInformationPostcode"/> - <see userInput="{{customerAddressVar.telephone}}" selector="{{CheckoutPaymentSection.shipToInformation}}" stepKey="assertShipToInformationTelephone"/> - </actionGroup> - - <!-- Check shipping method in checkout --> - <actionGroup name="CheckShippingMethodInCheckoutActionGroup"> - <annotations> - <description>Validates that the provided Shipping Method Name is present on the Storefront Checkout page under the 'Shipping Method' section.</description> - </annotations> - <arguments> - <argument name="shippingMethod"/> - </arguments> - - <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> - <see userInput="{{shippingMethod}}" selector="{{CheckoutPaymentSection.shippingMethodInformation}}" stepKey="assertshippingMethodInformation"/> - </actionGroup> - - <!-- Checkout select Check/Money Order payment --> - <actionGroup name="CheckoutSelectCheckMoneyOrderPaymentActionGroup"> - <annotations> - <description>Selects the 'Check / Money Order' Payment Method on the Storefront Checkout page.</description> - </annotations> - - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <conditionalClick selector="{{StorefrontCheckoutPaymentMethodSection.checkPaymentMethodByName('Check / Money order')}}" dependentSelector="{{StorefrontCheckoutPaymentMethodSection.checkPaymentMethodByName('Check / Money order')}}" visible="true" stepKey="selectCheckmoPaymentMethod"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskAfterPaymentMethodSelection"/> - </actionGroup> - - <!-- Check selected shipping address information on shipping information step --> - <actionGroup name="CheckSelectedShippingAddressInCheckoutActionGroup"> - <annotations> - <description>Validates that the provided Customer and Address details are listed on the Storefront Checkout page under the 'Shipping Address' section when multiple Addresses are present for a Customer.</description> - </annotations> - <arguments> - <argument name="customerVar"/> - <argument name="customerAddressVar"/> - </arguments> - - <waitForElement selector="{{CheckoutShippingSection.shippingTab}}" time="30" stepKey="waitForShippingSectionLoaded"/> - <see stepKey="VerifyFirstNameInSelectedAddress" selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{customerVar.firstname}}"/> - <see stepKey="VerifyLastNameInSelectedAddress" selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{customerVar.lastname}}"/> - <see stepKey="VerifyStreetInSelectedAddress" selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{customerAddressVar.street[0]}}"/> - <see stepKey="VerifyCityInSelectedAddress" selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{customerAddressVar.city}}"/> - <see stepKey="VerifyZipInSelectedAddress" selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{customerAddressVar.postcode}}"/> - <see stepKey="VerifyPhoneInSelectedAddress" selector="{{CheckoutShippingSection.selectedShippingAddress}}" userInput="{{customerAddressVar.telephone}}"/> - </actionGroup> - - <!-- Check billing address in checkout --> - <actionGroup name="CheckBillingAddressInCheckoutActionGroup"> - <annotations> - <description>Validates that the provided Customer and Address details are present on the Storefront Checkout page under the 'Payment Method' section.</description> - </annotations> - <arguments> - <argument name="customerVar"/> - <argument name="customerAddressVar"/> - </arguments> - - <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> - <see userInput="{{customerVar.firstName}}" selector="{{CheckoutPaymentSection.billingAddress}}" stepKey="assertBillingAddressFirstName"/> - <see userInput="{{customerVar.lastName}}" selector="{{CheckoutPaymentSection.billingAddress}}" stepKey="assertBillingAddressLastName"/> - <see userInput="{{customerAddressVar.street[0]}}" selector="{{CheckoutPaymentSection.billingAddress}}" stepKey="assertBillingAddressStreet"/> - <see userInput="{{customerAddressVar.city}}" selector="{{CheckoutPaymentSection.billingAddress}}" stepKey="assertBillingAddressCity"/> - <see userInput="{{customerAddressVar.state}}" selector="{{CheckoutPaymentSection.billingAddress}}" stepKey="assertBillingAddressState"/> - <see userInput="{{customerAddressVar.postcode}}" selector="{{CheckoutPaymentSection.billingAddress}}" stepKey="assertBillingAddressPostcode"/> - <see userInput="{{customerAddressVar.telephone}}" selector="{{CheckoutPaymentSection.billingAddress}}" stepKey="assertBillingAddressTelephone"/> - </actionGroup> - - <!-- Check billing address in checkout with billing address on payment page --> - <actionGroup name="CheckBillingAddressInCheckoutWithBillingAddressOnPaymentPageActionGroup"> - <annotations> - <description>Validates that the provided Customer and Address details appear on the Storefront Checkout page under the 'Billing Address' section.</description> - </annotations> - <arguments> - <argument name="customerVar"/> - <argument name="customerAddressVar"/> - </arguments> - - <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> - <see userInput="{{customerVar.firstName}}" selector="{{CheckoutPaymentWithDisplayBillingAddressOnPaymentPageSection.billingAddressDetails}}" stepKey="assertBillingAddressDetailsFirstName"/> - <see userInput="{{customerVar.lastName}}" selector="{{CheckoutPaymentWithDisplayBillingAddressOnPaymentPageSection.billingAddressDetails}}" stepKey="assertBillingAddressDetailsLastName"/> - <see userInput="{{customerAddressVar.street[0]}}" selector="{{CheckoutPaymentWithDisplayBillingAddressOnPaymentPageSection.billingAddressDetails}}" stepKey="assertBillingAddressDetailsStreet"/> - <see userInput="{{customerAddressVar.city}}" selector="{{CheckoutPaymentWithDisplayBillingAddressOnPaymentPageSection.billingAddressDetails}}" stepKey="assertBillingAddressDetailsCity"/> - <see userInput="{{customerAddressVar.state}}" selector="{{CheckoutPaymentWithDisplayBillingAddressOnPaymentPageSection.billingAddressDetails}}" stepKey="assertBillingAddressDetailsState"/> - <see userInput="{{customerAddressVar.postcode}}" selector="{{CheckoutPaymentWithDisplayBillingAddressOnPaymentPageSection.billingAddressDetails}}" stepKey="assertBillingAddressDetailsPostcode"/> - <see userInput="{{customerAddressVar.telephone}}" selector="{{CheckoutPaymentWithDisplayBillingAddressOnPaymentPageSection.billingAddressDetails}}" stepKey="assertBillingAddressDetailsTelephone"/> - </actionGroup> - - <!-- Checkout place order --> - <actionGroup name="CheckoutPlaceOrderActionGroup"> - <annotations> - <description>Clicks on 'Place Order'. Validates that the provided Order ID and Email You messages are present and correct.</description> - </annotations> - <arguments> - <argument name="orderNumberMessage"/> - <argument name="emailYouMessage"/> - </arguments> - - <waitForElementVisible selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> - <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> - <see selector="{{CheckoutSuccessMainSection.success}}" userInput="{{orderNumberMessage}}" stepKey="seeOrderNumber"/> - <see selector="{{CheckoutSuccessMainSection.success}}" userInput="{{emailYouMessage}}" stepKey="seeEmailYou"/> - </actionGroup> - - <!--Verify country options in checkout top destination section--> - <actionGroup name="VerifyTopDestinationsCountry"> - <annotations> - <description>Validates that the provided Country is listed at the provided Index in 'Country' drop down menu on the Storefront Shopping Cart page under the 'Summary' section.</description> - </annotations> - <arguments> - <argument name="country" type="string"/> - <argument name="placeNumber"/> - </arguments> - - <conditionalClick selector="{{CheckoutCartSummarySection.shippingHeading}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="openShippingDetails"/> - <see selector="{{CheckoutCartSummarySection.countryParameterized('placeNumber')}}" userInput="{{country}}" stepKey="seeCountry"/> - </actionGroup> - - <actionGroup name="StorefrontSignOutActionGroup"> - <annotations> - <description>Clicks on the Customer Account link. Clicks on 'Sign Out'. Validates that the Signed Out message is present and correct.</description> - </annotations> - - <click selector="{{StoreFrontSignOutSection.customerAccount}}" stepKey="clickCustomerButton"/> - <click selector="{{StoreFrontSignOutSection.signOut}}" stepKey="clickToSignOut"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <see userInput="You are signed out" stepKey="signOut"/> - </actionGroup> - - <!--Click Place Order button--> - <actionGroup name="ClickPlaceOrderActionGroup"> - <annotations> - <description>Clicks on the 'Place Order' button. Validates that the Success Message is present and correct.</description> - </annotations> - - <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> - <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> - <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutPlaceOrderActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutPlaceOrderActionGroup.xml new file mode 100644 index 0000000000000..48c515a3dd197 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutPlaceOrderActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CheckoutPlaceOrderActionGroup"> + <annotations> + <description>Clicks on 'Place Order'. Validates that the provided Order ID and Email You messages are present and correct.</description> + </annotations> + <arguments> + <argument name="orderNumberMessage"/> + <argument name="emailYouMessage"/> + </arguments> + + <waitForElementVisible selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + <see selector="{{CheckoutSuccessMainSection.success}}" userInput="{{orderNumberMessage}}" stepKey="seeOrderNumber"/> + <see selector="{{CheckoutSuccessMainSection.success}}" userInput="{{emailYouMessage}}" stepKey="seeEmailYou"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutSelectCheckMoneyOrderPaymentActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutSelectCheckMoneyOrderPaymentActionGroup.xml new file mode 100644 index 0000000000000..04e987fd10d98 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutSelectCheckMoneyOrderPaymentActionGroup.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="CheckoutSelectCheckMoneyOrderPaymentActionGroup"> + <annotations> + <description>Selects the 'Check / Money Order' Payment Method on the Storefront Checkout page.</description> + </annotations> + + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <conditionalClick selector="{{StorefrontCheckoutPaymentMethodSection.checkPaymentMethodByName('Check / Money order')}}" dependentSelector="{{StorefrontCheckoutPaymentMethodSection.checkPaymentMethodByName('Check / Money order')}}" visible="true" stepKey="selectCheckmoPaymentMethod"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskAfterPaymentMethodSelection"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutSelectFlatRateShippingMethodActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutSelectFlatRateShippingMethodActionGroup.xml new file mode 100644 index 0000000000000..a5dcec3b7cb37 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutSelectFlatRateShippingMethodActionGroup.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"> + <!-- Checkout select Flat Rate shipping method --> + <actionGroup name="CheckoutSelectFlatRateShippingMethodActionGroup"> + <annotations> + <description>Clicks on the 'Flat Rate' Shipping Method on the Storefront Checkout page.</description> + </annotations> + + <conditionalClick selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('Flat Rate')}}" dependentSelector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('Flat Rate')}}" visible="true" stepKey="selectFlatRateShippingMethod"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskForNextButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ClickPlaceOrderActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ClickPlaceOrderActionGroup.xml new file mode 100644 index 0000000000000..079b89a879b77 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ClickPlaceOrderActionGroup.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="ClickPlaceOrderActionGroup"> + <annotations> + <description>Clicks on the 'Place Order' button. Validates that the Success Message is present and correct.</description> + </annotations> + + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ClickViewAndEditCartFromMiniCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ClickViewAndEditCartFromMiniCartActionGroup.xml new file mode 100644 index 0000000000000..70aa5b49e4b98 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ClickViewAndEditCartFromMiniCartActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ClickViewAndEditCartFromMiniCartActionGroup"> + <annotations> + <description>Clicks on the Storefront Mini Shopping Cart icon. Clicks on the 'View and Edit Cart' link. Validates that the URL is present and correct. PLEASE NOTE: The URL is Hardcoded.</description> + </annotations> + + <scrollTo selector="{{StorefrontMinicartSection.showCart}}" stepKey="scrollToMiniCart"/> + <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> + <click selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="viewAndEditCart"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <seeInCurrentUrl url="checkout/cart" stepKey="seeInCurrentUrl"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillGuestCheckoutShippingAddressFormActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillGuestCheckoutShippingAddressFormActionGroup.xml index 80fd604e752e9..527afdc26a5f4 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillGuestCheckoutShippingAddressFormActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillGuestCheckoutShippingAddressFormActionGroup.xml @@ -21,7 +21,4 @@ <fillField selector="{{CheckoutShippingSection.postcode}}" userInput="{{customerAddress.postcode}}" stepKey="SetCustomerZipCode"/> <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{customerAddress.telephone}}" stepKey="SetCustomerPhoneNumber"/> </actionGroup> - <actionGroup name="FillGuestCheckoutShippingAddressWithCountryActionGroup" extends="FillGuestCheckoutShippingAddressFormActionGroup"> - <selectOption selector="{{CheckoutShippingSection.country}}" userInput="{{customerAddress.country_id}}" stepKey="selectCustomerCountry" after="SetCustomerCity"/> - </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillGuestCheckoutShippingAddressWithCountryActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillGuestCheckoutShippingAddressWithCountryActionGroup.xml new file mode 100644 index 0000000000000..c5685f4a3a31f --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillGuestCheckoutShippingAddressWithCountryActionGroup.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="FillGuestCheckoutShippingAddressWithCountryActionGroup" extends="FillGuestCheckoutShippingAddressFormActionGroup"> + <selectOption selector="{{CheckoutShippingSection.country}}" userInput="{{customerAddress.country_id}}" stepKey="selectCustomerCountry" after="SetCustomerCity"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GoToCheckoutFromCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GoToCheckoutFromCartActionGroup.xml new file mode 100644 index 0000000000000..66db3895483c7 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GoToCheckoutFromCartActionGroup.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="GoToCheckoutFromCartActionGroup"> + <annotations> + <description>Clicks on the 'View and Edit Cart' link in the Storefront Mini Shopping Cart. Validates that the Storefront Shopping Cart URL is present and correct. Clicks on 'Proceed to Checkout'.</description> + </annotations> + + <waitForElementNotVisible selector="{{StorefrontMinicartSection.emptyCart}}" stepKey="waitUpdateQuantity"/> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + <seeInCurrentUrl url="{{CheckoutCartPage.url}}" stepKey="assertCheckoutCartUrl"/> + <click selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="goToCheckout"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GoToCheckoutFromMinicartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GoToCheckoutFromMinicartActionGroup.xml new file mode 100644 index 0000000000000..d6bc6b6a0acb0 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GoToCheckoutFromMinicartActionGroup.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="GoToCheckoutFromMinicartActionGroup"> + <annotations> + <description>Clicks on the Storefront Mini Shopping Cart icon. Clicks on 'Proceed to Checkout'.</description> + </annotations> + + <waitForElementNotVisible selector="{{StorefrontMinicartSection.emptyCart}}" stepKey="waitUpdateQuantity"/> + <wait time="5" stepKey="waitMinicartRendering"/> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> + <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml index 77734cc75497f..22f9f100ed08f 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml @@ -28,74 +28,4 @@ <fillField selector="{{CheckoutPaymentSection.guestPostcode}}" userInput="{{customerAddressVar.postcode}}" stepKey="enterPostcode"/> <fillField selector="{{CheckoutPaymentSection.guestTelephone}}" userInput="{{customerAddressVar.telephone}}" stepKey="enterTelephone"/> </actionGroup> - <actionGroup name="StorefrontCheckoutFillNewBillingAddressActionGroup" extends="GuestCheckoutFillNewBillingAddressActionGroup"> - <remove keyForRemoval="enterEmail"/> - <remove keyForRemoval="waitForLoading3"/> - </actionGroup> - - <actionGroup name="LoggedInCheckoutFillNewBillingAddressActionGroup"> - <annotations> - <description>Fills in the provided Address details on the Storefront Checkout Address sections based on the provided Class Prefix (i.e. '._show', '[aria-hidden=false]').</description> - </annotations> - <arguments> - <argument name="Address"/> - <!-- the classPrefix argument is to specifically select the inputs of the correct form - this is to prevent having 3 action groups doing essentially the same thing --> - <argument name="classPrefix" type="string" defaultValue=""/> - </arguments> - - <fillField stepKey="fillFirstName" selector="{{classPrefix}} {{CheckoutShippingSection.firstName}}" userInput="{{Address.firstname}}"/> - <fillField stepKey="fillLastName" selector="{{classPrefix}} {{CheckoutShippingSection.lastName}}" userInput="{{Address.lastname}}"/> - <fillField stepKey="fillCompany" selector="{{classPrefix}} {{CheckoutShippingSection.company}}" userInput="{{Address.company}}"/> - <fillField stepKey="fillPhoneNumber" selector="{{classPrefix}} {{CheckoutShippingSection.telephone}}" userInput="{{Address.telephone}}"/> - <fillField stepKey="fillStreetAddress1" selector="{{classPrefix}} {{CheckoutShippingSection.street}}" userInput="{{Address.street[0]}}"/> - <fillField stepKey="fillStreetAddress2" selector="{{classPrefix}} {{CheckoutShippingSection.street2}}" userInput="{{Address.street[1]}}"/> - <fillField stepKey="fillCityName" selector="{{classPrefix}} {{CheckoutShippingSection.city}}" userInput="{{Address.city}}"/> - <selectOption stepKey="selectState" selector="{{classPrefix}} {{CheckoutShippingSection.region}}" userInput="{{Address.state}}"/> - <fillField stepKey="fillZip" selector="{{classPrefix}} {{CheckoutShippingSection.postcode}}" userInput="{{Address.postcode}}"/> - <selectOption stepKey="selectCounty" selector="{{classPrefix}} {{CheckoutShippingSection.country}}" userInput="{{Address.country_id}}"/> - <waitForPageLoad stepKey="waitForFormUpdate2"/> - </actionGroup> - - <!--Filling address without second address field and without state field--> - <actionGroup name="LoggedInCheckoutWithOneAddressFieldWithoutStateField" extends="LoggedInCheckoutFillNewBillingAddressActionGroup"> - <annotations> - <description>EXTENDS: LoggedInCheckoutFillNewBillingAddressActionGroup. Removes 'fillStreetAddress2' and 'selectState'.</description> - </annotations> - - <remove keyForRemoval="fillStreetAddress2"/> - <remove keyForRemoval="selectState"/> - </actionGroup> - - <actionGroup name="clearCheckoutAddressPopupFieldsActionGroup"> - <annotations> - <description>Clears the fields for the Customer/Address fields on the Storefront Checkout Address sections based on the provided Class Prefix (i.e. '._show', '[aria-hidden=false]').</description> - </annotations> - <arguments> - <argument name="classPrefix" type="string" defaultValue=""/> - </arguments> - - <clearField selector="{{classPrefix}} {{CheckoutShippingSection.firstName}}" stepKey="clearFieldFirstName"/> - <clearField selector="{{classPrefix}} {{CheckoutShippingSection.lastName}}" stepKey="clearFieldLastName"/> - <clearField selector="{{classPrefix}} {{CheckoutShippingSection.company}}" stepKey="clearFieldCompany"/> - <clearField selector="{{classPrefix}} {{CheckoutShippingSection.street}}" stepKey="clearFieldStreetAddress1"/> - <clearField selector="{{classPrefix}} {{CheckoutShippingSection.street2}}" stepKey="clearFieldStreetAddress2"/> - <clearField selector="{{classPrefix}} {{CheckoutShippingSection.city}}" stepKey="clearFieldCityName"/> - <selectOption selector="{{classPrefix}} {{CheckoutShippingSection.region}}" userInput="" stepKey="clearFieldRegion"/> - <clearField selector="{{classPrefix}} {{CheckoutShippingSection.postcode}}" stepKey="clearFieldZip"/> - <selectOption selector="{{classPrefix}} {{CheckoutShippingSection.country}}" userInput="" stepKey="clearFieldCounty"/> - <clearField selector="{{classPrefix}} {{CheckoutShippingSection.telephone}}" stepKey="clearFieldPhoneNumber"/> - </actionGroup> - - <actionGroup name="GuestCheckoutSelectPaymentAndFillNewBillingAddressActionGroup" extends="GuestCheckoutFillNewBillingAddressActionGroup"> - <annotations> - <description>EXTENDS: GuestCheckoutFillNewBillingAddressActionGroup. Clicks on the provided Payment Method on the Storefront Checkout page.</description> - </annotations> - <arguments> - <argument name="paymentMethod" type="string"/> - </arguments> - - <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" after="waitForLoading3" stepKey="waitForPaymentSectionLoaded"/> - <conditionalClick selector="{{CheckoutPaymentSection.paymentMethodByName(paymentMethod)}}" dependentSelector="{{CheckoutPaymentSection.billingAddress}}" visible="false" before="enterFirstName" stepKey="clickCheckMoneyOrderPayment"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillShippingNoWaitForPaymentActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillShippingNoWaitForPaymentActionGroup.xml new file mode 100644 index 0000000000000..ca9b7adf6c387 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillShippingNoWaitForPaymentActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="GuestCheckoutFillShippingNoWaitForPaymentActionGroup" extends="GuestCheckoutFillingShippingSectionActionGroup"> + <annotations> + <description>EXTENDS: GuestCheckoutFillingShippingSectionActionGroup. Removed 'waitForPaymentSectionLoaded' and 'assertCheckoutPaymentUrl'.</description> + </annotations> + + <remove keyForRemoval="waitForPaymentSectionLoaded"/> + <remove keyForRemoval="assertCheckoutPaymentUrl"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionActionGroup.xml new file mode 100644 index 0000000000000..f020cb42725c8 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionActionGroup.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="GuestCheckoutFillingShippingSectionActionGroup"> + <annotations> + <description>Fills in the provided Customer/Address (Including Region) details on the Storefront Checkout page under the 'Shipping Address' section. Selects the provided Shipping Method. Clicks on Next. Validates that the URL is present and correct.</description> + </annotations> + <arguments> + <argument name="customerVar" defaultValue="CustomerEntityOne"/> + <argument name="customerAddressVar" defaultValue="CustomerAddressSimple"/> + <!--First available shipping method will be selected if value is not passed for shippingMethod--> + <argument name="shippingMethod" defaultValue="" type="string"/> + </arguments> + + <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{customerVar.email}}" stepKey="enterEmail"/> + <fillField selector="{{CheckoutShippingSection.firstName}}" userInput="{{customerVar.firstname}}" stepKey="enterFirstName"/> + <fillField selector="{{CheckoutShippingSection.lastName}}" userInput="{{customerVar.lastname}}" stepKey="enterLastName"/> + <fillField selector="{{CheckoutShippingSection.street}}" userInput="{{customerAddressVar.street[0]}}" stepKey="enterStreet"/> + <fillField selector="{{CheckoutShippingSection.city}}" userInput="{{customerAddressVar.city}}" stepKey="enterCity"/> + <selectOption selector="{{CheckoutShippingSection.region}}" userInput="{{customerAddressVar.state}}" stepKey="selectRegion"/> + <fillField selector="{{CheckoutShippingSection.postcode}}" userInput="{{customerAddressVar.postcode}}" stepKey="enterPostcode"/> + <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{customerAddressVar.telephone}}" stepKey="enterTelephone"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + <waitForElement selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('shippingMethod')}}" stepKey="waitForShippingMethod"/> + <click selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('shippingMethod')}}" stepKey="selectShippingMethod"/> + <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> + <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionUnavailablePaymentActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionUnavailablePaymentActionGroup.xml new file mode 100644 index 0000000000000..9ce14338f1223 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionUnavailablePaymentActionGroup.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="GuestCheckoutFillingShippingSectionUnavailablePaymentActionGroup"> + <annotations> + <description>Fills in the provided Customer/Address (Including Region) details on the Storefront Checkout page under the 'Shipping Address' section. Selects the 1st Shipping Method. Clicks on Next. Validates that the Payment Error Message and URL are present and correct.</description> + </annotations> + <arguments> + <argument name="customerVar"/> + <argument name="customerAddressVar"/> + </arguments> + + <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{customerVar.email}}" stepKey="enterEmail"/> + <fillField selector="{{CheckoutShippingSection.firstName}}" userInput="{{customerVar.firstname}}" stepKey="enterFirstName"/> + <fillField selector="{{CheckoutShippingSection.lastName}}" userInput="{{customerVar.lastname}}" stepKey="enterLastName"/> + <fillField selector="{{CheckoutShippingSection.street}}" userInput="{{customerAddressVar.street[0]}}" stepKey="enterStreet"/> + <fillField selector="{{CheckoutShippingSection.city}}" userInput="{{customerAddressVar.city}}" stepKey="enterCity"/> + <selectOption selector="{{CheckoutShippingSection.region}}" userInput="{{customerAddressVar.state}}" stepKey="selectRegion"/> + <fillField selector="{{CheckoutShippingSection.postcode}}" userInput="{{customerAddressVar.postcode}}" stepKey="enterPostcode"/> + <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{customerAddressVar.telephone}}" stepKey="enterTelephone"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + <click selector="{{CheckoutShippingSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/> + <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> + <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> + <waitForElementVisible selector="{{CheckoutPaymentSection.noQuotes}}" stepKey="waitMessage"/> + <see userInput="No Payment method available." stepKey="checkMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionWithoutRegionActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionWithoutRegionActionGroup.xml new file mode 100644 index 0000000000000..3db019c44dd0d --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillingShippingSectionWithoutRegionActionGroup.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="GuestCheckoutFillingShippingSectionWithoutRegionActionGroup"> + <annotations> + <description>Fills in the provided Customer/Address (Excluding Region) details on the Storefront Checkout page under the 'Shipping Address' section. Selects the provided Shipping Method. Clicks on Next. Validates that the URL is present and correct.</description> + </annotations> + <arguments> + <argument name="customerVar"/> + <argument name="customerAddressVar"/> + </arguments> + + <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{customerVar.email}}" stepKey="enterEmail"/> + <fillField selector="{{CheckoutShippingSection.firstName}}" userInput="{{customerVar.firstname}}" stepKey="enterFirstName"/> + <fillField selector="{{CheckoutShippingSection.lastName}}" userInput="{{customerVar.lastname}}" stepKey="enterLastName"/> + <fillField selector="{{CheckoutShippingSection.street}}" userInput="{{customerAddressVar.street[0]}}" stepKey="enterStreet"/> + <fillField selector="{{CheckoutShippingSection.city}}" userInput="{{customerAddressVar.city}}" stepKey="enterCity"/> + <fillField selector="{{CheckoutShippingSection.postcode}}" userInput="{{customerAddressVar.postcode}}" stepKey="enterPostcode"/> + <selectOption selector="{{CheckoutShippingSection.country}}" userInput="{{customerAddressVar.country_id}}" stepKey="enterCountry"/> + <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{customerAddressVar.telephone}}" stepKey="enterTelephone"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + <click selector="{{CheckoutShippingSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/> + <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> + <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutSelectPaymentAndFillNewBillingAddressActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutSelectPaymentAndFillNewBillingAddressActionGroup.xml new file mode 100644 index 0000000000000..73de73a2d1ed6 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutSelectPaymentAndFillNewBillingAddressActionGroup.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="GuestCheckoutSelectPaymentAndFillNewBillingAddressActionGroup" extends="GuestCheckoutFillNewBillingAddressActionGroup"> + <annotations> + <description>EXTENDS: GuestCheckoutFillNewBillingAddressActionGroup. Clicks on the provided Payment Method on the Storefront Checkout page.</description> + </annotations> + <arguments> + <argument name="paymentMethod" type="string"/> + </arguments> + + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" after="waitForLoading3" stepKey="waitForPaymentSectionLoaded"/> + <conditionalClick selector="{{CheckoutPaymentSection.paymentMethodByName(paymentMethod)}}" dependentSelector="{{CheckoutPaymentSection.billingAddress}}" visible="false" before="enterFirstName" stepKey="clickCheckMoneyOrderPayment"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutWithSpecificCountryOptionForPaymentMethodActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutWithSpecificCountryOptionForPaymentMethodActionGroup.xml new file mode 100644 index 0000000000000..afa09150cfccd --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutWithSpecificCountryOptionForPaymentMethodActionGroup.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="GuestCheckoutWithSpecificCountryOptionForPaymentMethodActionGroup" extends="GuestCheckoutFillingShippingSectionUnavailablePaymentActionGroup"> + <annotations> + <description>EXTENDS: GuestCheckoutFillingShippingSectionUnavailablePaymentActionGroup. Removes 'checkMessage'. Validates that the provided Payment Method Name is NOT present on the Storefront Checkout page.</description> + </annotations> + <arguments> + <argument name="paymentMethod" type="string"/> + </arguments> + + <remove keyForRemoval="checkMessage"/> + <dontSee selector="{{CheckoutPaymentSection.paymentMethodByName(paymentMethod)}}" stepKey="paymentMethodDoesNotAvailable"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoggedInCheckoutFillNewBillingAddressActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoggedInCheckoutFillNewBillingAddressActionGroup.xml new file mode 100644 index 0000000000000..29a71ac938ef6 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoggedInCheckoutFillNewBillingAddressActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="LoggedInCheckoutFillNewBillingAddressActionGroup"> + <annotations> + <description>Fills in the provided Address details on the Storefront Checkout Address sections based on the provided Class Prefix (i.e. '._show', '[aria-hidden=false]').</description> + </annotations> + <arguments> + <argument name="Address"/> + <!-- the classPrefix argument is to specifically select the inputs of the correct form + this is to prevent having 3 action groups doing essentially the same thing --> + <argument name="classPrefix" type="string" defaultValue=""/> + </arguments> + + <fillField stepKey="fillFirstName" selector="{{classPrefix}} {{CheckoutShippingSection.firstName}}" userInput="{{Address.firstname}}"/> + <fillField stepKey="fillLastName" selector="{{classPrefix}} {{CheckoutShippingSection.lastName}}" userInput="{{Address.lastname}}"/> + <fillField stepKey="fillCompany" selector="{{classPrefix}} {{CheckoutShippingSection.company}}" userInput="{{Address.company}}"/> + <fillField stepKey="fillPhoneNumber" selector="{{classPrefix}} {{CheckoutShippingSection.telephone}}" userInput="{{Address.telephone}}"/> + <fillField stepKey="fillStreetAddress1" selector="{{classPrefix}} {{CheckoutShippingSection.street}}" userInput="{{Address.street[0]}}"/> + <fillField stepKey="fillStreetAddress2" selector="{{classPrefix}} {{CheckoutShippingSection.street2}}" userInput="{{Address.street[1]}}"/> + <fillField stepKey="fillCityName" selector="{{classPrefix}} {{CheckoutShippingSection.city}}" userInput="{{Address.city}}"/> + <selectOption stepKey="selectState" selector="{{classPrefix}} {{CheckoutShippingSection.region}}" userInput="{{Address.state}}"/> + <fillField stepKey="fillZip" selector="{{classPrefix}} {{CheckoutShippingSection.postcode}}" userInput="{{Address.postcode}}"/> + <selectOption stepKey="selectCounty" selector="{{classPrefix}} {{CheckoutShippingSection.country}}" userInput="{{Address.country_id}}"/> + <waitForPageLoad stepKey="waitForFormUpdate2"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoggedInCheckoutWithOneAddressFieldWithoutStateFieldActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoggedInCheckoutWithOneAddressFieldWithoutStateFieldActionGroup.xml new file mode 100644 index 0000000000000..208dfc37cec74 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoggedInCheckoutWithOneAddressFieldWithoutStateFieldActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="LoggedInCheckoutWithOneAddressFieldWithoutStateFieldActionGroup" extends="LoggedInCheckoutFillNewBillingAddressActionGroup"> + <annotations> + <description>EXTENDS: LoggedInCheckoutFillNewBillingAddressActionGroup. Removes 'fillStreetAddress2' and 'selectState'.</description> + </annotations> + + <remove keyForRemoval="fillStreetAddress2"/> + <remove keyForRemoval="selectState"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoggedInUserCheckoutAddNewShippingSectionWithoutRegionActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoggedInUserCheckoutAddNewShippingSectionWithoutRegionActionGroup.xml new file mode 100644 index 0000000000000..0c4cea142b4e6 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoggedInUserCheckoutAddNewShippingSectionWithoutRegionActionGroup.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="LoggedInUserCheckoutAddNewShippingSectionWithoutRegionActionGroup"> + <annotations> + <description>Fills in the provided Customer/Address (Excluding Region) details on the Storefront Checkout page under the 'Shipping Address' section. Selects the 1st Shipping Method. Clicks on Next. Validates that the Payment Error Message and URL are present and correct.</description> + </annotations> + <arguments> + <argument name="customerVar"/> + <argument name="customerAddressVar"/> + </arguments> + + <fillField selector="{{CheckoutShippingSection.firstName}}" userInput="{{customerVar.firstname}}" stepKey="enterFirstName"/> + <fillField selector="{{CheckoutShippingSection.lastName}}" userInput="{{customerVar.lastname}}" stepKey="enterLastName"/> + <fillField selector="{{CheckoutShippingSection.street}}" userInput="{{customerAddressVar.street[0]}}" stepKey="enterStreet"/> + <fillField selector="{{CheckoutShippingSection.city}}" userInput="{{customerAddressVar.city}}" stepKey="enterCity"/> + <fillField selector="{{CheckoutShippingSection.postcode}}" userInput="{{customerAddressVar.postcode}}" stepKey="enterPostcode"/> + <selectOption selector="{{CheckoutShippingSection.country}}" userInput="{{customerAddressVar.country_id}}" stepKey="enterCountry"/> + <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{customerAddressVar.telephone}}" stepKey="enterTelephone"/> + <click selector="{{CheckoutShippingSection.saveAddress}}" stepKey="clickSaveAddress"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + <click selector="{{CheckoutShippingSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/> + <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> + <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoggedInUserCheckoutFillingShippingSectionActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoggedInUserCheckoutFillingShippingSectionActionGroup.xml new file mode 100644 index 0000000000000..60188224871eb --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoggedInUserCheckoutFillingShippingSectionActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="LoggedInUserCheckoutFillingShippingSectionActionGroup"> + <annotations> + <description>Fills in the provided Customer/Address (Including Region) details on the Storefront Checkout page under the 'Shipping Address' section. Selects the 1st Shipping Method. Clicks on Next. Validates that the Payment Error Message and URL are present and correct.</description> + </annotations> + <arguments> + <argument name="customerVar" defaultValue="CustomerEntityOne"/> + <argument name="customerAddressVar" defaultValue="CustomerAddressSimple"/> + </arguments> + + <fillField selector="{{CheckoutShippingSection.firstName}}" userInput="{{customerVar.firstname}}" stepKey="enterFirstName"/> + <fillField selector="{{CheckoutShippingSection.lastName}}" userInput="{{customerVar.lastname}}" stepKey="enterLastName"/> + <fillField selector="{{CheckoutShippingSection.street}}" userInput="{{customerAddressVar.street[0]}}" stepKey="enterStreet"/> + <fillField selector="{{CheckoutShippingSection.city}}" userInput="{{customerAddressVar.city}}" stepKey="enterCity"/> + <selectOption selector="{{CheckoutShippingSection.region}}" userInput="{{customerAddressVar.state}}" stepKey="selectRegion"/> + <fillField selector="{{CheckoutShippingSection.postcode}}" userInput="{{customerAddressVar.postcode}}" stepKey="enterPostcode"/> + <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{customerAddressVar.telephone}}" stepKey="enterTelephone"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + <click selector="{{CheckoutShippingSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/> + <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> + <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/PlaceOrderWithLoggedUserActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/PlaceOrderWithLoggedUserActionGroup.xml new file mode 100644 index 0000000000000..2bd1ee0f7d809 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/PlaceOrderWithLoggedUserActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="PlaceOrderWithLoggedUserActionGroup"> + <annotations> + <description>Clicks on 'Proceed to Checkout' on the Storefront Shopping Cart page. Selects the provided Shipping Method. Clicks on Next. Clicks on Place Order. Validates that the Success Message is present and correct.</description> + </annotations> + <arguments> + <!--First available shipping method will be selected if value is not passed for shippingMethod--> + <argument name="shippingMethod" defaultValue="" type="string"/> + </arguments> + + <waitForElementVisible selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="waitProceedToCheckout"/> + <click selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="clickProceedToCheckout"/> + <click selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('shippingMethod')}}" stepKey="selectShippingMethod"/> + <waitForElement selector="{{CheckoutShippingSection.next}}" stepKey="waitForNextButton"/> + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" stepKey="waitForPaymentSectionLoaded"/> + <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> + <waitForElementVisible selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="waitForPlaceOrderButton"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/RemoveProductFromMiniCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/RemoveProductFromMiniCartActionGroup.xml new file mode 100644 index 0000000000000..e6626ec357d0e --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/RemoveProductFromMiniCartActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="RemoveProductFromMiniCartActionGroup"> + <annotations> + <description>Removed the provided Product from the Storefront Mini Shopping Cart.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + </arguments> + + <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForMiniCartOpen"/> + <click selector="{{StorefrontMinicartSection.deleteMiniCartItemByName(productName)}}" stepKey="clickDelete"/> + <waitForElementVisible selector="{{StoreFrontRemoveItemModalSection.message}}" stepKey="waitForConfirmationModal"/> + <see selector="{{StoreFrontRemoveItemModalSection.message}}" userInput="Are you sure you would like to remove this item from the shopping cart?" stepKey="seeDeleteConfirmationMessage"/> + <click selector="{{StoreFrontRemoveItemModalSection.ok}}" stepKey="confirmDelete"/> + <waitForPageLoad stepKey="waitForDeleteToFinish"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddCategoryProductToCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddCategoryProductToCartActionGroup.xml new file mode 100644 index 0000000000000..7fbcb96c20d9e --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddCategoryProductToCartActionGroup.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"> + <!-- Add Product to Cart from the category page and check message and product count in Minicart --> + <actionGroup name="StorefrontAddCategoryProductToCartActionGroup"> + <annotations> + <description>Adds the provided Product to the Cart from a Storefront Category page. Validates that the Success Message is present and correct. Validates that the Mini Shopping Cart contains the provided Product Count.</description> + </annotations> + <arguments> + <argument name="product"/> + <argument name="productCount"/> + </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"/> + <seeLink stepKey="assertLinkToShoppingCart" url="{{_ENV.MAGENTO_BASE_URL}}checkout/cart/" userInput="shopping cart"/> + <waitForText userInput="{{productCount}}" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddCategoryProductToCartWithQuantityActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddCategoryProductToCartWithQuantityActionGroup.xml new file mode 100644 index 0000000000000..c4fe115b70ae4 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddCategoryProductToCartWithQuantityActionGroup.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="StorefrontAddCategoryProductToCartWithQuantityActionGroup"> + <annotations> + <description>Adds the provided Product to the Cart from a Storefront Category page. Validates that the Success Message is present and correct. Updates the Product Quantity using the provided Product Quantity. Validates that the provided Quantity is present and correct in the Mini Shopping Cart.</description> + </annotations> + <arguments> + <argument name="product"/> + <argument name="quantity" defaultValue="1" type="string"/> + <argument name="checkQuantity" defaultValue="1" type="string"/> + </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"/> + <seeLink stepKey="assertLinkToShoppingCart" url="{{_ENV.MAGENTO_BASE_URL}}checkout/cart/" userInput="shopping cart"/> + <waitForText userInput="{{checkQuantity}}" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> + <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> + <fillField selector="{{StorefrontMinicartSection.itemQuantity(product.name)}}" userInput="{{quantity}}" stepKey="setProductQtyToFiftyInMiniCart"/> + <click selector="{{StorefrontMinicartSection.itemQuantityUpdate(product.name)}}" stepKey="updateQtyInMiniCart"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddProductToCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddProductToCartActionGroup.xml new file mode 100644 index 0000000000000..8c1ef9d973297 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddProductToCartActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAddProductToCartActionGroup"> + <annotations> + <description>Clicks on Add to Cart on a Storefront Product page. Validates that the Success Message is present and correct. Validates that the provided Product Count is present and correct in the Mini Shopping Cart.</description> + </annotations> + <arguments> + <argument name="product"/> + <argument name="productCount" type="string"/> + </arguments> + + <click selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="clickAddToCart"/> + <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" stepKey="waitForSuccessMessage"/> + <see selector="{{StorefrontProductPageSection.messagesBlock}}" userInput="You added {{product.name}} to your shopping cart." stepKey="assertSuccessMessage"/> + <seeLink stepKey="assertLinkToShoppingCart" url="{{_ENV.MAGENTO_BASE_URL}}checkout/cart/" userInput="shopping cart"/> + <waitForText userInput="{{productCount}}" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckCartActionGroup.xml new file mode 100644 index 0000000000000..bbad2579a47d2 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckCartActionGroup.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCheckCartActionGroup"> + <annotations> + <description>Goes to the Storefront Shopping Cart page. Validates that the provided Subtotal, Shipping, Shipping Method and Total are present and correct.</description> + </annotations> + <arguments> + <argument name="subtotal" type="string"/> + <argument name="shipping" type="string"/> + <argument name="shippingMethod" type="string" defaultValue="Flat Rate - Fixed"/> + <argument name="total" type="string"/> + </arguments> + + <seeInCurrentUrl url="{{CheckoutCartPage.url}}" stepKey="assertUrl"/> + <waitForPageLoad stepKey="waitForCartPage"/> + <conditionalClick selector="{{CheckoutCartSummarySection.shippingHeading}}" dependentSelector="{{CheckoutCartSummarySection.shippingMethodForm}}" visible="false" stepKey="openEstimateShippingSection"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="waitForShippingSection"/> + <checkOption selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectShippingMethod"/> + <scrollTo selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="scrollToSummary"/> + <see userInput="{{subtotal}}" selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="assertSubtotal"/> + <see userInput="({{shippingMethod}})" selector="{{CheckoutCartSummarySection.shippingMethod}}" stepKey="assertShippingMethod"/> + <reloadPage stepKey="reloadPage" after="assertShippingMethod" /> + <waitForPageLoad stepKey="WaitForPageLoaded" after="reloadPage" /> + <waitForText userInput="{{shipping}}" selector="{{CheckoutCartSummarySection.shipping}}" time="45" stepKey="assertShipping" after="WaitForPageLoaded"/> + <see userInput="{{total}}" selector="{{CheckoutCartSummarySection.total}}" stepKey="assertTotal" after="assertShipping"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckCartSimpleProductActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckCartSimpleProductActionGroup.xml new file mode 100644 index 0000000000000..083963f4cc487 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckCartSimpleProductActionGroup.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="StorefrontCheckCartSimpleProductActionGroup"> + <annotations> + <description>Validates that the provided Product details (Name and Price) are present and correct in the Mini Shopping Cart. Validates that the provided Product Quantity is present and correct in the Mini Shopping Cart.</description> + </annotations> + <arguments> + <argument name="product"/> + <argument name="productQuantity"/> + </arguments> + + <seeElement selector="{{CheckoutCartProductSection.ProductLinkByName(product.name)}}" stepKey="assertProductName"/> + <see userInput="${{product.price}}.00" selector="{{CheckoutCartProductSection.ProductPriceByName(product.name)}}" stepKey="assertProductPrice"/> + <seeInField userInput="{{productQuantity}}" selector="{{CheckoutCartProductSection.ProductQuantityByName(product.name)}}" stepKey="assertProductQuantity"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckCartTotalWithDiscountCategoryActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckCartTotalWithDiscountCategoryActionGroup.xml new file mode 100644 index 0000000000000..c5d2d054f2e8f --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckCartTotalWithDiscountCategoryActionGroup.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="StorefrontCheckCartTotalWithDiscountCategoryActionGroup" extends="StorefrontCheckCartActionGroup"> + <annotations> + <description>EXTENDS: StorefrontCheckCartActionGroup. Validates that the provided Discount is present in the Storefront Shopping Cart.</description> + </annotations> + <arguments> + <argument name="discount" type="string" defaultValue="0"/> + </arguments> + <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForDiscount"/> + <see selector="{{CheckoutCartSummarySection.discountAmount}}" userInput="-${{discount}}" stepKey="assertDiscount"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutFillNewBillingAddressActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutFillNewBillingAddressActionGroup.xml new file mode 100644 index 0000000000000..e8949a1864663 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutFillNewBillingAddressActionGroup.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="StorefrontCheckoutFillNewBillingAddressActionGroup" extends="GuestCheckoutFillNewBillingAddressActionGroup"> + <remove keyForRemoval="enterEmail"/> + <remove keyForRemoval="waitForLoading3"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutForwardFromShippingStepActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutForwardFromShippingStepActionGroup.xml new file mode 100644 index 0000000000000..524e3f784ed3f --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutForwardFromShippingStepActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCheckoutForwardFromShippingStepActionGroup"> + <annotations> + <description>Clicks next on Checkout Shipping step</description> + </annotations> + <waitForElementVisible selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml deleted file mode 100644 index 6dc5cfd044924..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontMiniCartActionGroup.xml +++ /dev/null @@ -1,65 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="clickViewAndEditCartFromMiniCart"> - <annotations> - <description>Clicks on the Storefront Mini Shopping Cart icon. Clicks on the 'View and Edit Cart' link. Validates that the URL is present and correct. PLEASE NOTE: The URL is Hardcoded.</description> - </annotations> - - <scrollTo selector="{{StorefrontMinicartSection.showCart}}" stepKey="scrollToMiniCart"/> - <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> - <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> - <click selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="viewAndEditCart"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> - <seeInCurrentUrl url="checkout/cart" stepKey="seeInCurrentUrl"/> - </actionGroup> - - <actionGroup name="assertOneProductNameInMiniCart"> - <annotations> - <description>Validates that the provided Product Name is present in the Storefront Mini Shopping Cart.</description> - </annotations> - <arguments> - <argument name="productName"/> - </arguments> - - <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> - <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> - <see selector="{{StorefrontMinicartSection.miniCartItemsText}}" userInput="{{productName}}" stepKey="seeInMiniCart"/> - </actionGroup> - - <!--Remove an item from the cart using minicart--> - <actionGroup name="removeProductFromMiniCart"> - <annotations> - <description>Removed the provided Product from the Storefront Mini Shopping Cart.</description> - </annotations> - <arguments> - <argument name="productName" type="string"/> - </arguments> - - <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> - <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForMiniCartOpen"/> - <click selector="{{StorefrontMinicartSection.deleteMiniCartItemByName(productName)}}" stepKey="clickDelete"/> - <waitForElementVisible selector="{{StoreFrontRemoveItemModalSection.message}}" stepKey="waitForConfirmationModal"/> - <see selector="{{StoreFrontRemoveItemModalSection.message}}" userInput="Are you sure you would like to remove this item from the shopping cart?" stepKey="seeDeleteConfirmationMessage"/> - <click selector="{{StoreFrontRemoveItemModalSection.ok}}" stepKey="confirmDelete"/> - <waitForPageLoad stepKey="waitForDeleteToFinish"/> - </actionGroup> - - <!--Check that the minicart is empty--> - <actionGroup name="assertMiniCartEmpty"> - <annotations> - <description>Validates that the provided Product Count appears in the Storefront Header next to the Shopping Cart icon. Clicks on the Mini Shopping Cart icon. Validates that the 'No Items' message is present and correct in the Storefront Mini Shopping Cart.</description> - </annotations> - - <dontSeeElement selector="{{StorefrontMinicartSection.productCount}}" stepKey="dontSeeMinicartProductCount"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="expandMinicart"/> - <see selector="{{StorefrontMinicartSection.minicartContent}}" userInput="You have no items in your shopping cart." stepKey="seeEmptyCartMessage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenCartFromMinicartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenCartFromMinicartActionGroup.xml new file mode 100644 index 0000000000000..3161d1a63b6f8 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenCartFromMinicartActionGroup.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="StorefrontOpenCartFromMinicartActionGroup"> + <annotations> + <description>Clicks on the Storefront Mini Shopping Cart icon. Click on 'View and Edit Cart'.</description> + </annotations> + + <waitForElement selector="{{StorefrontMinicartSection.showCart}}" stepKey="waitForShowMinicart"/> + <waitForElement selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForCartLink"/> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickShowMinicart"/> + <click selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="clickCart"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenCartPageActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenCartPageActionGroup.xml new file mode 100644 index 0000000000000..fe1e48e00c5bb --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenCartPageActionGroup.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="StorefrontOpenCartPageActionGroup"> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="openCartPage" /> + <waitForPageLoad stepKey="waitForPageLoaded" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMinicartAndCheckSimpleProductActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMinicartAndCheckSimpleProductActionGroup.xml new file mode 100644 index 0000000000000..28e97dfca5158 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontOpenMinicartAndCheckSimpleProductActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontOpenMinicartAndCheckSimpleProductActionGroup"> + <annotations> + <description>Clicks on the Storefront Mini Shopping Cart icon. Validates that the provided Product is present and correct in the Mini Shopping Cart.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <waitForElement selector="{{StorefrontMinicartSection.productLinkByName(product.name)}}" stepKey="waitForMinicartProduct"/> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickShowMinicart"/> + <see userInput="${{product.price}}.00" selector="{{StorefrontMinicartSection.productPriceByName(product.name)}}" stepKey="assertProductPrice"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml deleted file mode 100644 index c0a160fcb2a71..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml +++ /dev/null @@ -1,149 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!-- Add Product to Cart from the category page and check message and product count in Minicart --> - <actionGroup name="StorefrontAddCategoryProductToCartActionGroup"> - <annotations> - <description>Adds the provided Product to the Cart from a Storefront Category page. Validates that the Success Message is present and correct. Validates that the Mini Shopping Cart contains the provided Product Count.</description> - </annotations> - <arguments> - <argument name="product"/> - <argument name="productCount"/> - </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"/> - <seeLink stepKey="assertLinkToShoppingCart" url="{{_ENV.MAGENTO_BASE_URL}}checkout/cart/" userInput="shopping cart"/> - <waitForText userInput="{{productCount}}" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> - </actionGroup> - - <!-- Add Product to Cart from the category page with specified quantity and check message and product count in Minicart --> - <actionGroup name="StorefrontAddCategoryProductToCartWithQuantityActionGroup"> - <annotations> - <description>Adds the provided Product to the Cart from a Storefront Category page. Validates that the Success Message is present and correct. Updates the Product Quantity using the provided Product Quantity. Validates that the provided Quantity is present and correct in the Mini Shopping Cart.</description> - </annotations> - <arguments> - <argument name="product"/> - <argument name="quantity" defaultValue="1" type="string"/> - <argument name="checkQuantity" defaultValue="1" type="string"/> - </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"/> - <seeLink stepKey="assertLinkToShoppingCart" url="{{_ENV.MAGENTO_BASE_URL}}checkout/cart/" userInput="shopping cart"/> - <waitForText userInput="{{checkQuantity}}" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> - <conditionalClick selector="{{StorefrontMinicartSection.showCart}}" dependentSelector="{{StorefrontMinicartSection.miniCartOpened}}" visible="false" stepKey="openMiniCart"/> - <waitForElementVisible selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForViewAndEditCartVisible"/> - <fillField selector="{{StorefrontMinicartSection.itemQuantity(product.name)}}" userInput="{{quantity}}" stepKey="setProductQtyToFiftyInMiniCart"/> - <click selector="{{StorefrontMinicartSection.itemQuantityUpdate(product.name)}}" stepKey="updateQtyInMiniCart"/> - </actionGroup> - - <!-- Add Product to Cart from the product page and check message and product count in Minicart --> - <actionGroup name="StorefrontAddProductToCartActionGroup"> - <annotations> - <description>Clicks on Add to Cart on a Storefront Product page. Validates that the Success Message is present and correct. Validates that the provided Product Count is present and correct in the Mini Shopping Cart.</description> - </annotations> - <arguments> - <argument name="product"/> - <argument name="productCount" type="string"/> - </arguments> - - <click selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="clickAddToCart"/> - <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" stepKey="waitForSuccessMessage"/> - <see selector="{{StorefrontProductPageSection.messagesBlock}}" userInput="You added {{product.name}} to your shopping cart." stepKey="assertSuccessMessage"/> - <seeLink stepKey="assertLinkToShoppingCart" url="{{_ENV.MAGENTO_BASE_URL}}checkout/cart/" userInput="shopping cart"/> - <waitForText userInput="{{productCount}}" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/> - </actionGroup> - - <!-- Open the Minicart and check Simple Product --> - <actionGroup name="StorefrontOpenMinicartAndCheckSimpleProductActionGroup"> - <annotations> - <description>Clicks on the Storefront Mini Shopping Cart icon. Validates that the provided Product is present and correct in the Mini Shopping Cart.</description> - </annotations> - <arguments> - <argument name="product"/> - </arguments> - - <waitForElement selector="{{StorefrontMinicartSection.productLinkByName(product.name)}}" stepKey="waitForMinicartProduct"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickShowMinicart"/> - <see userInput="${{product.price}}.00" selector="{{StorefrontMinicartSection.productPriceByName(product.name)}}" stepKey="assertProductPrice"/> - </actionGroup> - - <!-- Check Simple Product in the Cart --> - <actionGroup name="StorefrontCheckCartSimpleProductActionGroup"> - <annotations> - <description>Validates that the provided Product details (Name and Price) are present and correct in the Mini Shopping Cart. Validates that the provided Product Quantity is present and correct in the Mini Shopping Cart.</description> - </annotations> - <arguments> - <argument name="product"/> - <argument name="productQuantity"/> - </arguments> - - <seeElement selector="{{CheckoutCartProductSection.ProductLinkByName(product.name)}}" stepKey="assertProductName"/> - <see userInput="${{product.price}}.00" selector="{{CheckoutCartProductSection.ProductPriceByName(product.name)}}" stepKey="assertProductPrice"/> - <seeInField userInput="{{productQuantity}}" selector="{{CheckoutCartProductSection.ProductQuantityByName(product.name)}}" stepKey="assertProductQuantity"/> - </actionGroup> - - <!-- Check the Cart --> - <actionGroup name="StorefrontCheckCartActionGroup"> - <annotations> - <description>Goes to the Storefront Shopping Cart page. Validates that the provided Subtotal, Shipping, Shipping Method and Total are present and correct.</description> - </annotations> - <arguments> - <argument name="subtotal" type="string"/> - <argument name="shipping" type="string"/> - <argument name="shippingMethod" type="string"/> - <argument name="total" type="string"/> - </arguments> - - <seeInCurrentUrl url="{{CheckoutCartPage.url}}" stepKey="assertUrl"/> - <waitForPageLoad stepKey="waitForCartPage"/> - <conditionalClick selector="{{CheckoutCartSummarySection.shippingHeading}}" dependentSelector="{{CheckoutCartSummarySection.shippingMethodForm}}" visible="false" stepKey="openEstimateShippingSection"/> - <waitForElementVisible selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="waitForShippingSection"/> - <checkOption selector="{{CheckoutCartSummarySection.flatRateShippingMethod}}" stepKey="selectShippingMethod"/> - <see userInput="{{subtotal}}" selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="assertSubtotal"/> - <see userInput="({{shippingMethod}})" selector="{{CheckoutCartSummarySection.shippingMethod}}" stepKey="assertShippingMethod"/> - <reloadPage stepKey="reloadPage" after="assertShippingMethod" /> - <waitForPageLoad stepKey="WaitForPageLoaded" after="reloadPage" /> - <waitForText userInput="{{shipping}}" selector="{{CheckoutCartSummarySection.shipping}}" time="45" stepKey="assertShipping" after="WaitForPageLoaded"/> - <see userInput="{{total}}" selector="{{CheckoutCartSummarySection.total}}" stepKey="assertTotal" after="assertShipping"/> - </actionGroup> - - <!-- Open the Cart from Minicart--> - <actionGroup name="StorefrontOpenCartFromMinicartActionGroup"> - <annotations> - <description>Clicks on the Storefront Mini Shopping Cart icon. Click on 'View and Edit Cart'.</description> - </annotations> - - <waitForElement selector="{{StorefrontMinicartSection.showCart}}" stepKey="waitForShowMinicart"/> - <waitForElement selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="waitForCartLink"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickShowMinicart"/> - <click selector="{{StorefrontMinicartSection.viewAndEditCart}}" stepKey="clickCart"/> - </actionGroup> - - <actionGroup name="changeSummaryQuoteAddress"> - <annotations> - <description>Fills in the provided Address details (Country, State and Zip Code) under the 'Summary' section on the Storefront Shopping Cart page.</description> - </annotations> - <arguments> - <argument name="taxCode"/> - </arguments> - - <conditionalClick stepKey="openShippingDetails" selector="{{CheckoutCartSummarySection.shippingHeading}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false"/> - <selectOption stepKey="selectCountry" selector="{{CheckoutCartSummarySection.country}}" userInput="{{taxCode.country}}"/> - <selectOption stepKey="selectStateProvince" selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="{{taxCode.state}}"/> - <fillField stepKey="fillZip" selector="{{CheckoutCartSummarySection.postcode}}" userInput="{{taxCode.zip}}"/> - <waitForPageLoad stepKey="waitForFormUpdate"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontRemoveCartItemActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontRemoveCartItemActionGroup.xml new file mode 100644 index 0000000000000..f2d4088370a2b --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontRemoveCartItemActionGroup.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontRemoveCartItemActionGroup"> + <click selector="{{CheckoutCartProductSection.RemoveItem}}" stepKey="deleteProductFromCart"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSignOutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSignOutActionGroup.xml new file mode 100644 index 0000000000000..8eac90fff2268 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSignOutActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontSignOutActionGroup"> + <annotations> + <description>Clicks on the Customer Account link. Clicks on 'Sign Out'. Validates that the Signed Out message is present and correct.</description> + </annotations> + + <click selector="{{StoreFrontSignOutSection.customerAccount}}" stepKey="clickCustomerButton"/> + <click selector="{{StoreFrontSignOutSection.signOut}}" stepKey="clickToSignOut"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <see userInput="You are signed out" stepKey="signOut"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/VerifyCheckoutPaymentOrderSummaryActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/VerifyCheckoutPaymentOrderSummaryActionGroup.xml index 6d8d390d36701..0ae71884745c6 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/VerifyCheckoutPaymentOrderSummaryActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/VerifyCheckoutPaymentOrderSummaryActionGroup.xml @@ -23,27 +23,4 @@ <see selector="{{CheckoutPaymentSection.orderSummaryShippingTotal}}" userInput="{{orderSummaryShippingTotal}}" stepKey="seeCorrectShipping"/> <see selector="{{CheckoutPaymentSection.orderSummaryTotal}}" userInput="{{orderSummaryTotal}}" stepKey="seeCorrectOrderTotal"/> </actionGroup> - <!-- Assert Order Summary SubTotal You should be on checkout page --> - <actionGroup name="AssertStorefrontCheckoutPaymentSummarySubtotalActionGroup"> - <arguments> - <argument name="orderSubtotal" type="string"/> - </arguments> - <waitForPageLoad time="30" stepKey="waitForCartFullyLoaded"/> - <waitForElementVisible selector="{{CheckoutPaymentSection.orderSummarySubtotal}}" time="30" stepKey="waitForOrderSummaryBlock"/> - <see selector="{{CheckoutPaymentSection.orderSummarySubtotal}}" userInput="{{orderSubtotal}}" stepKey="seeCorrectSubtotal"/> - </actionGroup> - <!-- Assert Order Summary Total You should be on checkout page --> - <actionGroup name="AssertStorefrontCheckoutPaymentSummaryTotalActionGroup"> - <arguments> - <argument name="orderTotal" type="string"/> - </arguments> - <waitForPageLoad time="30" stepKey="waitForCartFullyLoaded"/> - <waitForElementVisible selector="{{CheckoutPaymentSection.orderSummaryTotal}}" time="30" stepKey="waitForOrderSummaryBlock"/> - <see selector="{{CheckoutPaymentSection.orderSummaryTotal}}" userInput="{{orderTotal}}" stepKey="seeCorrectOrderTotal"/> - </actionGroup> - <!-- Assert Order Summary Total Is Not Shown You should be on checkout page --> - <actionGroup name="AssertStorefrontCheckoutPaymentSummaryTotalMissingActionGroup"> - <waitForPageLoad time="30" stepKey="waitForCartFullyLoaded"/> - <dontSeeElement selector="{{CheckoutPaymentSection.orderSummaryTotal}}" stepKey="seeTotalElement"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/VerifyTopDestinationsCountryActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/VerifyTopDestinationsCountryActionGroup.xml new file mode 100644 index 0000000000000..74f48ff2fc155 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/VerifyTopDestinationsCountryActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="VerifyTopDestinationsCountryActionGroup"> + <annotations> + <description>Validates that the provided Country is listed at the provided Index in 'Country' drop down menu on the Storefront Shopping Cart page under the 'Summary' section.</description> + </annotations> + <arguments> + <argument name="country" type="string"/> + <argument name="placeNumber"/> + </arguments> + + <conditionalClick selector="{{CheckoutCartSummarySection.shippingHeading}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="openShippingDetails"/> + <see selector="{{CheckoutCartSummarySection.countryParameterized('placeNumber')}}" userInput="{{country}}" stepKey="seeCountry"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/clearCheckoutAddressPopupFieldsActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/clearCheckoutAddressPopupFieldsActionGroup.xml new file mode 100644 index 0000000000000..f19d5b5e9cd41 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/clearCheckoutAddressPopupFieldsActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="clearCheckoutAddressPopupFieldsActionGroup"> + <annotations> + <description>Clears the fields for the Customer/Address fields on the Storefront Checkout Address sections based on the provided Class Prefix (i.e. '._show', '[aria-hidden=false]').</description> + </annotations> + <arguments> + <argument name="classPrefix" type="string" defaultValue=""/> + </arguments> + + <clearField selector="{{classPrefix}} {{CheckoutShippingSection.firstName}}" stepKey="clearFieldFirstName"/> + <clearField selector="{{classPrefix}} {{CheckoutShippingSection.lastName}}" stepKey="clearFieldLastName"/> + <clearField selector="{{classPrefix}} {{CheckoutShippingSection.company}}" stepKey="clearFieldCompany"/> + <clearField selector="{{classPrefix}} {{CheckoutShippingSection.street}}" stepKey="clearFieldStreetAddress1"/> + <clearField selector="{{classPrefix}} {{CheckoutShippingSection.street2}}" stepKey="clearFieldStreetAddress2"/> + <clearField selector="{{classPrefix}} {{CheckoutShippingSection.city}}" stepKey="clearFieldCityName"/> + <selectOption selector="{{classPrefix}} {{CheckoutShippingSection.region}}" userInput="" stepKey="clearFieldRegion"/> + <clearField selector="{{classPrefix}} {{CheckoutShippingSection.postcode}}" stepKey="clearFieldZip"/> + <selectOption selector="{{classPrefix}} {{CheckoutShippingSection.country}}" userInput="" stepKey="clearFieldCounty"/> + <clearField selector="{{classPrefix}} {{CheckoutShippingSection.telephone}}" stepKey="clearFieldPhoneNumber"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutCartPage.xml b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutCartPage.xml index a77b07a129dce..cf7f2baeb4b26 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutCartPage.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Page/CheckoutCartPage.xml @@ -9,6 +9,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="CheckoutCartPage" url="/checkout/cart" module="Magento_Checkout" area="storefront"> + <section name="StorefrontCartToolbarSection"/> <section name="CheckoutCartProductSection"/> <section name="CheckoutCartSummarySection"/> <section name="CheckoutCartCrossSellSection"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml index 1b85f3b045c5d..af9d81249e8ac 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml @@ -33,6 +33,7 @@ selector="//table[@id='shopping-cart-table']//tbody//tr[contains(@class,'item-actions')]//a[contains(@class,'action-delete')]"/> <element name="removeProductByName" type="text" selector="//*[contains(text(), '{{productName}}')]/ancestor::tbody//a[@class='action action-delete']" parameterized="true" timeout="30"/> <element name="productName" type="text" selector="//tbody[@class='cart item']//strong[@class='product-item-name']"/> + <element name="moveToWishlistByProductName" type="button" selector="//a[contains(text(), '{{productName}}')]/ancestor::tbody/tr//a[contains(@class, 'towishlist')]" parameterized="true"/> <element name="nthItemOption" type="block" selector=".item:nth-of-type({{numElement}}) .item-options" parameterized="true"/> <element name="nthEditButton" type="block" selector=".item:nth-of-type({{numElement}}) .action-edit" parameterized="true"/> <element name="nthBundleOptionName" type="text" selector=".product-item-details .item-options:nth-of-type({{numOption}}) dt" parameterized="true"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml index 20b71608cd038..de71fc3f8ad0e 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml @@ -9,6 +9,8 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="CheckoutCartSummarySection"> + <element name="orderTotal" type="input" selector=".grand.totals .amount .price"/> + <element name="subTotal" type="input" selector="span[data-th='Subtotal']"/> <element name="expandShoppingCartSummary" type="button" selector="//*[contains(@class, 'items-in-cart')][not(contains(@class, 'active'))]"/> <element name="elementPosition" type="text" selector=".data.table.totals > tbody tr:nth-of-type({{value}}) > th" parameterized="true"/> <element name="subtotal" type="text" selector="//*[@id='cart-totals']//tr[@class='totals sub']//td//span[@class='price']"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutOrderSummarySection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutOrderSummarySection.xml index d3ad2aed96946..026265656379a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutOrderSummarySection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutOrderSummarySection.xml @@ -19,5 +19,7 @@ <element name="additionalAddress" type="text" selector=".block.block-addresses-list"/> <element name="miniCartTabClosed" type="button" selector=".title[aria-expanded='false']" timeout="30"/> <element name="itemsQtyInCart" type="text" selector=".items-in-cart > .title > strong > span"/> + <element name="orderSummaryShippingTotalLabelDescription" type="text" selector=".shipping.totals .label.description"/> + <element name="shippingTotalNotYetCalculated" type="text" selector=".shipping.totals .not-calculated"/> </section> </sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml index be8519f920b90..16fd373d3ae4d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -31,6 +31,7 @@ <element name="cartItemsAreaActive" type="textarea" selector="div.block.items-in-cart.active" timeout="30"/> <element name="checkMoneyOrderPayment" type="radio" selector="input#checkmo.radio" timeout="30"/> <element name="placeOrder" type="button" selector=".payment-method._active button.action.primary.checkout" timeout="30"/> + <element name="placeOrderWithoutTimeout" type="button" selector=".payment-method._active button.action.primary.checkout"/> <element name="paymentSectionTitle" type="text" selector="//*[@id='checkout-payment-method-load']//div[@data-role='title']" /> <element name="orderSummarySubtotal" type="text" selector="//tr[@class='totals sub']//span[@class='price']" /> <element name="orderSummaryShippingTotal" type="text" selector="//tr[@class='totals shipping excl']//span[@class='price']" /> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml index 08a9d671a8d02..c486e13ecf58b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml @@ -14,6 +14,7 @@ <element name="orderNumber" type="text" selector="div.checkout-success > p:nth-child(1) > span"/> <element name="orderNumber22" type="text" selector=".order-number>strong"/> <element name="orderLink" type="text" selector="a[href*=order_id].order-number" timeout="30"/> + <element name="orderLinks" type="text" selector="a[href*=order_id]" timeout="30"/> <element name="orderNumberText" type="text" selector=".checkout-success > p:nth-child(1)"/> <element name="continueShoppingButton" type="button" selector=".action.primary.continue" timeout="30"/> <element name="createAnAccount" type="button" selector="[data-bind*="i18n: 'Create an Account'"]" timeout="30"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCartToolbarSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCartToolbarSection.xml new file mode 100644 index 0000000000000..ff40449369530 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCartToolbarSection.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="StorefrontCartToolbarSection"> + <element name="toolbarNumber" type="text" selector="div.toolbar > .pager > .toolbar-amount > .toolbar-number" /> + <element name="toolbarPager" type="text" selector="div.toolbar > .pager > .pages" /> + <element name="toolbarNextPage" type="text" selector="div.toolbar > .pager > .pages > .pages-item-next" /> + <element name="toolbarPreviousPage" type="text" selector="div.toolbar > .pager > .pages > .pages-item-previous" /> + </section> +</sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml index e00906386e46b..80ed4f90c2cd0 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/StorefrontMiniCartSection.xml @@ -25,6 +25,7 @@ <element name="goToCheckout" type="button" selector="#top-cart-btn-checkout" timeout="30"/> <element name="viewAndEditCart" type="button" selector=".action.viewcart" timeout="30"/> <element name="miniCartItemsText" type="text" selector=".minicart-items"/> + <element name="editMiniCartItem" type="button" selector=".action.edit" timeout="30"/> <element name="deleteMiniCartItem" type="button" selector=".action.delete" timeout="30"/> <element name="deleteMiniCartItemByName" type="button" selector="//ol[@id='mini-cart']//div[contains(., '{{var}}')]//a[contains(@class, 'delete')]" parameterized="true"/> <element name="miniCartSubtotalField" type="text" selector=".block-minicart .amount span.price"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml index 163e71c50053f..5f898492ad016 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml @@ -28,7 +28,7 @@ <after> <!--Logout from customer account--> - <amOnPage url="customer/account/logout/" stepKey="logoutCustomerOne"/> + <amOnPage url="{{StorefrontCustomerLogoutPage.url}}" stepKey="logoutCustomerOne"/> <waitForPageLoad stepKey="waitLogoutCustomerOne"/> <actionGroup ref="logout" stepKey="adminLogout"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> @@ -45,7 +45,7 @@ <waitForPageLoad stepKey="waitForCatalogPageLoad"/> <!--Add Product to Shopping Cart--> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> <argument name="productName" value="$$createSimpleProduct.name$$"/> </actionGroup> @@ -71,7 +71,7 @@ <waitForPageLoad stepKey="waitForCatalogPageLoad2"/> <!--Add Product to Shopping Cart--> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage2"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage2"> <argument name="productName" value="$$createSimpleProduct.name$$"/> </actionGroup> @@ -96,7 +96,7 @@ <waitForPageLoad stepKey="waitForCatalogPageLoad3"/> <!--Add Product to Shopping Cart--> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage3"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage3"> <argument name="productName" value="$$createSimpleProduct.name$$"/> </actionGroup> @@ -156,7 +156,7 @@ <waitForPageLoad stepKey="waitForCatalogPageLoad"/> <!--Add Product to Shopping Cart--> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> <argument name="productName" value="$$createSimpleProduct.name$$"/> </actionGroup> @@ -193,7 +193,7 @@ <waitForPageLoad stepKey="waitForCatalogPageLoad2"/> <!--Add Product to Shopping Cart--> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage2"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage2"> <argument name="productName" value="$$createSimpleProduct.name$$"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/CheckNotVisibleProductInMinicartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/CheckNotVisibleProductInMinicartTest.xml index 4b4ca1935fd78..54ac1143b3573 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/CheckNotVisibleProductInMinicartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/CheckNotVisibleProductInMinicartTest.xml @@ -27,13 +27,13 @@ <waitForPageLoad stepKey="waitForCatalogPageLoad"/> <!--Add simple product1 to Shopping Cart--> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage1"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage1"> <argument name="productName" value="$$createSimpleProduct1.name$$"/> </actionGroup> <!--Check simple product1 in minicart--> <comment userInput="Check simple product 1 in minicart" stepKey="commentCheckSimpleProduct1InMinicart" after="addToCartFromStorefrontProductPage1"/> - <actionGroup ref="assertOneProductNameInMiniCart" stepKey="assertProduct1NameInMiniCart"> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="assertProduct1NameInMiniCart"> <argument name="productName" value="$$createSimpleProduct1.name$$"/> </actionGroup> @@ -47,19 +47,19 @@ <waitForPageLoad stepKey="waitForCatalogPageLoad2"/> <!--Add simple product2 to Shopping Cart for updating cart items--> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage2"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage2"> <argument name="productName" value="$$createSimpleProduct2.name$$"/> </actionGroup> <!--Check simple product1 in minicart--> <comment userInput="Check hidden simple product 1 in minicart" stepKey="commentCheckHiddenSimpleProduct1InMinicart" after="addToCartFromStorefrontProductPage2"/> - <actionGroup ref="assertOneProductNameInMiniCart" stepKey="assertHiddenProduct1NameInMiniCart"> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="assertHiddenProduct1NameInMiniCart"> <argument name="productName" value="$$createSimpleProduct1.name$$"/> </actionGroup> <!--Check simple product2 in minicart--> <comment userInput="Check hidden simple product 2 in minicart" stepKey="commentCheckSimpleProduct2InMinicart" after="addToCartFromStorefrontProductPage2"/> - <actionGroup ref="assertOneProductNameInMiniCart" stepKey="assertProduct2NameInMiniCart"> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="assertProduct2NameInMiniCart"> <argument name="productName" value="$$createSimpleProduct2.name$$"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutSpecificDestinationsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutSpecificDestinationsTest.xml index f3807388399b8..1bfb1804f41a7 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutSpecificDestinationsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutSpecificDestinationsTest.xml @@ -28,13 +28,13 @@ </before> <!--Go to configuration general page--> - <actionGroup ref="NavigateToConfigurationGeneralPage" stepKey="navigateToConfigurationGeneralPage"/> + <actionGroup ref="NavigateToConfigurationGeneralPageActionGroup" stepKey="navigateToConfigurationGeneralPage"/> <!--Open country options section--> <conditionalClick selector="{{CountryOptionsSection.countryOptions}}" dependentSelector="{{CountryOptionsSection.countryOptionsOpen}}" visible="false" stepKey="clickOnStoreInformation"/> <!--Select top destinations country--> - <actionGroup ref="SelectTopDestinationsCountry" stepKey="selectTopDestinationsCountry"> + <actionGroup ref="SelectTopDestinationsCountryActionGroup" stepKey="selectTopDestinationsCountry"> <argument name="countries" value="Countries"/> </actionGroup> @@ -51,19 +51,19 @@ <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart"/> <!--Verify country options in checkout top destination section--> - <actionGroup ref="VerifyTopDestinationsCountry" stepKey="verifyTopDestinationsCountry"> + <actionGroup ref="VerifyTopDestinationsCountryActionGroup" stepKey="verifyTopDestinationsCountry"> <argument name="country" value="Bahamas"/> <argument name="placeNumber" value="2"/> </actionGroup> <!--Go to configuration general page--> - <actionGroup ref="NavigateToConfigurationGeneralPage" stepKey="navigateToConfigurationGeneralPage2"/> + <actionGroup ref="NavigateToConfigurationGeneralPageActionGroup" stepKey="navigateToConfigurationGeneralPage2"/> <!--Open country options section--> <conditionalClick selector="{{CountryOptionsSection.countryOptions}}" dependentSelector="{{CountryOptionsSection.countryOptionsOpen}}" visible="false" stepKey="clickOnStoreInformation2"/> <!--Deselect top destinations country--> - <actionGroup ref="UnSelectTopDestinationsCountry" stepKey="unSelectTopDestinationsCountry"> + <actionGroup ref="UnSelectTopDestinationsCountryActionGroup" stepKey="unSelectTopDestinationsCountry"> <argument name="countries" value="Countries"/> </actionGroup> @@ -71,7 +71,7 @@ <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart2"/> <!--Verify country options is shown by default--> - <actionGroup ref="VerifyTopDestinationsCountry" stepKey="verifyTopDestinationsCountry2"> + <actionGroup ref="VerifyTopDestinationsCountryActionGroup" stepKey="verifyTopDestinationsCountry2"> <argument name="country" value="Afghanistan"/> <argument name="placeNumber" value="2"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/ConfiguringInstantPurchaseFunctionalityTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/ConfiguringInstantPurchaseFunctionalityTest.xml index 0897e20f1b17d..aea0657cc2e3b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/ConfiguringInstantPurchaseFunctionalityTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/ConfiguringInstantPurchaseFunctionalityTest.xml @@ -64,7 +64,7 @@ </actionGroup> <!-- Add product to cart --> - <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart"> <argument name="product" value="$$createProduct$$"/> </actionGroup> <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/DefaultBillingAddressShouldBeCheckedOnPaymentPageTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/DefaultBillingAddressShouldBeCheckedOnPaymentPageTest.xml index 166f5022d5aeb..af7718eae69ed 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/DefaultBillingAddressShouldBeCheckedOnPaymentPageTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/DefaultBillingAddressShouldBeCheckedOnPaymentPageTest.xml @@ -38,13 +38,13 @@ <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> </after> <!-- Add simple product to cart and go to checkout--> - <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart"> <argument name="product" value="$$createProduct$$"/> </actionGroup> <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> <!-- Click "+ New Address" and Fill new address--> <click selector="{{CheckoutShippingSection.newAddressButton}}" stepKey="addAddress"/> - <actionGroup ref="LoggedInCheckoutWithOneAddressFieldWithoutStateField" stepKey="changeAddress"> + <actionGroup ref="LoggedInCheckoutWithOneAddressFieldWithoutStateFieldActionGroup" stepKey="changeAddress"> <argument name="Address" value="UK_Not_Default_Address"/> <argument name="classPrefix" value="._show"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/DeleteBundleDynamicProductFromShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/DeleteBundleDynamicProductFromShoppingCartTest.xml index 5ad4c764026f7..ef5f5b640b0a2 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/DeleteBundleDynamicProductFromShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/DeleteBundleDynamicProductFromShoppingCartTest.xml @@ -55,7 +55,7 @@ <!-- Add product to the cart --> <click selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="clickCustomizeAndAddToCart"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProductToCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProductToCart"> <argument name="productName" value="$$createBundleDynamicProduct.name$$"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/DeleteBundleFixedProductFromShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/DeleteBundleFixedProductFromShoppingCartTest.xml index d1008c5831983..e141d0628cc4d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/DeleteBundleFixedProductFromShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/DeleteBundleFixedProductFromShoppingCartTest.xml @@ -47,7 +47,7 @@ <!-- Add product to the cart --> <click selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="clickCustomizeAndAddToCart"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProductToCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProductToCart"> <argument name="productName" value="$$createFixedBundleProduct.name$$"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/DeleteDownloadableProductFromShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/DeleteDownloadableProductFromShoppingCartTest.xml index e16ef70c23e3d..988e3b8d3129d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/DeleteDownloadableProductFromShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/DeleteDownloadableProductFromShoppingCartTest.xml @@ -39,7 +39,7 @@ <!-- Add downloadable product to the cart --> <amOnPage url="{{StorefrontProductPage.url($$createDownloadableProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToDownloadableProductPage"/> <waitForPageLoad stepKey="waitForDownloadableProductPageLoad"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartDownloadableProductFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartDownloadableProductFromStorefrontProductPage"> <argument name="productName" value="$$createDownloadableProduct.name$$"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/DeleteVirtualProductFromShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/DeleteVirtualProductFromShoppingCartTest.xml index 97dcdd52127fd..969a827a8a461 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/DeleteVirtualProductFromShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/DeleteVirtualProductFromShoppingCartTest.xml @@ -33,7 +33,7 @@ <!-- Add virtual product to the cart --> <amOnPage url="{{StorefrontProductPage.url($$createVirtualProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToProductPage"/> <waitForPageLoad stepKey="waitForProductPageLoad"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartVirtualProductFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartVirtualProductFromStorefrontProductPage"> <argument name="productName" value="$$createVirtualProduct.name$$"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index 4281a0eb77da8..7002479279a78 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -19,7 +19,7 @@ <argument name="category" value="$$createCategory$$"/> <argument name="productCount" value="3"/> </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="cartAssertSimpleProduct1" after="cartAssertCategory"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct1" after="cartAssertCategory"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -27,7 +27,7 @@ <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabSimpleProduct1ImageSrc" stepKey="cartAssertSimpleProduct1ImageNotDefault" after="cartGrabSimpleProduct1ImageSrc"/> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickSimpleProduct1" after="cartAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loaded" after="cartClickSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="cartAssertProduct1Page" after="waitForCartSimpleProduct1loaded"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertProduct1Page" after="waitForCartSimpleProduct1loaded"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -46,7 +46,7 @@ <argument name="category" value="$$createCategory$$"/> <argument name="productCount" value="3"/> </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="cartAssertSimpleProduct2" after="cartAssertCategory1ForSimpleProduct2"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct2" after="cartAssertCategory1ForSimpleProduct2"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -69,7 +69,7 @@ <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabSimpleProduct1ImageSrc" stepKey="cartMinicartAssertSimpleProduct1ImageNotDefault" after="cartMinicartGrabSimpleProduct1ImageSrc"/> <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartMinicartClickSimpleProduct1" after="cartMinicartAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct1loaded" after="cartMinicartClickSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="cartAssertMinicartProduct1Page" after="waitForMinicartSimpleProduct1loaded"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct1Page" after="waitForMinicartSimpleProduct1loaded"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -85,7 +85,7 @@ <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabSimpleProduct2ImageSrc" stepKey="cartMinicartAssertSimpleProduct2ImageNotDefault" after="cartMinicartGrabSimpleProduct2ImageSrc"/> <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartMinicartClickSimpleProduct2" after="cartMinicartAssertSimpleProduct2ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct2loaded" after="cartMinicartClickSimpleProduct2"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="cartAssertMinicartProduct2Page" after="waitForMinicartSimpleProduct2loaded"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct2Page" after="waitForMinicartSimpleProduct2loaded"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -114,7 +114,7 @@ <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabSimpleProduct1ImageSrc" stepKey="cartCartAssertSimpleProduct1ImageNotDefault" after="cartCartGrabSimpleProduct1ImageSrc"/> <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickCartSimpleProduct1" after="cartCartAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loadedAgain" after="cartClickCartSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="cartAssertCartProduct1Page" after="waitForCartSimpleProduct1loadedAgain"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct1Page" after="waitForCartSimpleProduct1loadedAgain"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -134,7 +134,7 @@ <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabSimpleProduct2ImageSrc" stepKey="cartCartAssertSimpleProduct2ImageNotDefault" after="cartCartGrabSimpleProduct2ImageSrc"/> <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartClickCartSimpleProduct2" after="cartCartAssertSimpleProduct2ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct2loaded" after="cartClickCartSimpleProduct2"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="cartAssertCartProduct2Page" after="waitForCartSimpleProduct2loaded"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct2Page" after="waitForCartSimpleProduct2loaded"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -207,7 +207,7 @@ <argument name="category" value="$$createCategory$$"/> <argument name="productCount" value="3"/> </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="cartAssertSimpleProduct1" after="cartAssertCategory"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct1" after="cartAssertCategory"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -215,7 +215,7 @@ <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabSimpleProduct1ImageSrc" stepKey="cartAssertSimpleProduct1ImageNotDefault" after="cartGrabSimpleProduct1ImageSrc"/> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickSimpleProduct1" after="cartAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loaded" after="cartClickSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="cartAssertProduct1Page" after="waitForCartSimpleProduct1loaded"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertProduct1Page" after="waitForCartSimpleProduct1loaded"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -234,7 +234,7 @@ <argument name="category" value="$$createCategory$$"/> <argument name="productCount" value="3"/> </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="cartAssertSimpleProduct2" after="cartAssertCategory1ForSimpleProduct2"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct2" after="cartAssertCategory1ForSimpleProduct2"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -257,7 +257,7 @@ <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabSimpleProduct1ImageSrc" stepKey="cartMinicartAssertSimpleProduct1ImageNotDefault" after="cartMinicartGrabSimpleProduct1ImageSrc"/> <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartMinicartClickSimpleProduct1" after="cartMinicartAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct1loaded" after="cartMinicartClickSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="cartAssertMinicartProduct1Page" after="waitForMinicartSimpleProduct1loaded"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct1Page" after="waitForMinicartSimpleProduct1loaded"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -273,7 +273,7 @@ <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabSimpleProduct2ImageSrc" stepKey="cartMinicartAssertSimpleProduct2ImageNotDefault" after="cartMinicartGrabSimpleProduct2ImageSrc"/> <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartMinicartClickSimpleProduct2" after="cartMinicartAssertSimpleProduct2ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct2loaded" after="cartMinicartClickSimpleProduct2"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="cartAssertMinicartProduct2Page" after="waitForMinicartSimpleProduct2loaded"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct2Page" after="waitForMinicartSimpleProduct2loaded"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -302,7 +302,7 @@ <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabSimpleProduct1ImageSrc" stepKey="cartCartAssertSimpleProduct1ImageNotDefault" after="cartCartGrabSimpleProduct1ImageSrc"/> <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickCartSimpleProduct1" after="cartCartAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loadedAgain" after="cartClickCartSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="cartAssertCartProduct1Page" after="waitForCartSimpleProduct1loadedAgain"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct1Page" after="waitForCartSimpleProduct1loadedAgain"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -322,7 +322,7 @@ <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabSimpleProduct2ImageSrc" stepKey="cartCartAssertSimpleProduct2ImageNotDefault" after="cartCartGrabSimpleProduct2ImageSrc"/> <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartClickCartSimpleProduct2" after="cartCartAssertSimpleProduct2ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct2loaded" after="cartClickCartSimpleProduct2"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="cartAssertCartProduct2Page" after="waitForCartSimpleProduct2loaded"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct2Page" after="waitForCartSimpleProduct2loaded"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index 65627787e2a05..6df859c9972c3 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -19,7 +19,7 @@ <argument name="category" value="$$createCategory$$"/> <argument name="productCount" value="3"/> </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="cartAssertSimpleProduct1" after="cartAssertCategory"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct1" after="cartAssertCategory"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -27,7 +27,7 @@ <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabSimpleProduct1ImageSrc" stepKey="cartAssertSimpleProduct1ImageNotDefault" after="cartGrabSimpleProduct1ImageSrc"/> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickSimpleProduct1" after="cartAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loaded" after="cartClickSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="cartAssertProduct1Page" after="waitForCartSimpleProduct1loaded"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertProduct1Page" after="waitForCartSimpleProduct1loaded"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -46,7 +46,7 @@ <argument name="category" value="$$createCategory$$"/> <argument name="productCount" value="3"/> </actionGroup> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="cartAssertSimpleProduct2" after="cartAssertCategory1ForSimpleProduct2"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="cartAssertSimpleProduct2" after="cartAssertCategory1ForSimpleProduct2"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -69,7 +69,7 @@ <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabSimpleProduct1ImageSrc" stepKey="cartMinicartAssertSimpleProduct1ImageNotDefault" after="cartMinicartGrabSimpleProduct1ImageSrc"/> <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartMinicartClickSimpleProduct1" after="cartMinicartAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct1loaded" after="cartMinicartClickSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="cartAssertMinicartProduct1Page" after="waitForMinicartSimpleProduct1loaded"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct1Page" after="waitForMinicartSimpleProduct1loaded"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -85,7 +85,7 @@ <assertNotRegExp expected="'/placeholder\/thumbnail\.jpg/'" actual="$cartMinicartGrabSimpleProduct2ImageSrc" stepKey="cartMinicartAssertSimpleProduct2ImageNotDefault" after="cartMinicartGrabSimpleProduct2ImageSrc"/> <click selector="{{StorefrontMinicartSection.productLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartMinicartClickSimpleProduct2" after="cartMinicartAssertSimpleProduct2ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForMinicartSimpleProduct2loaded" after="cartMinicartClickSimpleProduct2"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="cartAssertMinicartProduct2Page" after="waitForMinicartSimpleProduct2loaded"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertMinicartProduct2Page" after="waitForMinicartSimpleProduct2loaded"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -114,7 +114,7 @@ <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabSimpleProduct1ImageSrc" stepKey="cartCartAssertSimpleProduct1ImageNotDefault" after="cartCartGrabSimpleProduct1ImageSrc"/> <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct1.name$$)}}" stepKey="cartClickCartSimpleProduct1" after="cartCartAssertSimpleProduct1ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct1loadedAgain" after="cartClickCartSimpleProduct1"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="cartAssertCartProduct1Page" after="waitForCartSimpleProduct1loadedAgain"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct1Page" after="waitForCartSimpleProduct1loadedAgain"> <argument name="product" value="$$createSimpleProduct1$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> @@ -134,7 +134,7 @@ <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartCartGrabSimpleProduct2ImageSrc" stepKey="cartCartAssertSimpleProduct2ImageNotDefault" after="cartCartGrabSimpleProduct2ImageSrc"/> <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createSimpleProduct2.name$$)}}" stepKey="cartClickCartSimpleProduct2" after="cartCartAssertSimpleProduct2ImageNotDefault"/> <waitForLoadingMaskToDisappear stepKey="waitForCartSimpleProduct2loaded" after="cartClickCartSimpleProduct2"/> - <actionGroup ref="StorefrontCheckSimpleProduct" stepKey="cartAssertCartProduct2Page" after="waitForCartSimpleProduct2loaded"> + <actionGroup ref="StorefrontCheckSimpleProductActionGroup" stepKey="cartAssertCartProduct2Page" after="waitForCartSimpleProduct2loaded"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <!-- @TODO: Move Image check to action group after MQE-697 is fixed --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml index 89028e146c358..e2d1a1b9139c8 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml @@ -36,7 +36,7 @@ </actionGroup> <!-- Add simple product to cart --> - <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart1"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart1"> <argument name="product" value="$$product$$"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingDefaultAddressTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingDefaultAddressTest.xml index aa3665a81bbde..f2501fdf4c0c6 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingDefaultAddressTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingDefaultAddressTest.xml @@ -45,12 +45,12 @@ <!-- Add Simple Product to cart --> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToSimpleProductPage"/> <waitForPageLoad stepKey="waitForSimpleProductPageLoad"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> <argument name="productName" value="$$createSimpleProduct.name$$"/> </actionGroup> <!-- Go to shopping cart --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <actionGroup ref="FillShippingZipForm" stepKey="fillShippingZipForm"> <argument name="address" value="US_Address_CA"/> </actionGroup> @@ -87,7 +87,7 @@ <!-- Open created order in backend --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> <waitForPageLoad stepKey="waitForOrdersPageLoad"/> - <actionGroup ref="OpenOrderById" stepKey="filterOrderGridById"> + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNewAddressTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNewAddressTest.xml index bafad6f28a680..b072989bc968c 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNewAddressTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNewAddressTest.xml @@ -45,12 +45,12 @@ <!-- Add Simple Product to cart --> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToSimpleProductPage"/> <waitForPageLoad stepKey="waitForSimpleProductPageLoad"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> <argument name="productName" value="$$createSimpleProduct.name$$"/> </actionGroup> <!-- Go to shopping cart --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <actionGroup ref="FillShippingZipForm" stepKey="fillShippingZipForm"> <argument name="address" value="US_Address_CA"/> </actionGroup> @@ -100,7 +100,7 @@ <!-- Open created order in backend --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> <waitForPageLoad stepKey="waitForOrdersPageLoad"/> - <actionGroup ref="OpenOrderById" stepKey="filterOrderGridById"> + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml index 2c341a5c4c1ab..9b0da574d6307 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml @@ -45,12 +45,12 @@ <!-- Add Simple Product to cart --> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToSimpleProductPage"/> <waitForPageLoad stepKey="waitForSimpleProductPageLoad"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> <argument name="productName" value="$$createSimpleProduct.name$$"/> </actionGroup> <!-- Go to shopping cart --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <actionGroup ref="FillShippingZipForm" stepKey="fillShippingZipForm"> <argument name="address" value="US_Address_CA"/> </actionGroup> @@ -88,7 +88,7 @@ <!-- Open created order in backend --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> <waitForPageLoad stepKey="waitForOrdersPageLoad"/> - <actionGroup ref="OpenOrderById" stepKey="filterOrderGridById"> + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutUsingSignInLinkTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutUsingSignInLinkTest.xml index 990459d7c81b7..80cf9c14b578e 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutUsingSignInLinkTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutUsingSignInLinkTest.xml @@ -45,12 +45,12 @@ <!-- Add Simple Product to cart --> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToSimpleProductPage"/> <waitForPageLoad stepKey="waitForSimpleProductPageLoad"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> <argument name="productName" value="$$createSimpleProduct.name$$"/> </actionGroup> <!-- Go to shopping cart --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <actionGroup ref="FillShippingZipForm" stepKey="fillShippingZipForm"> <argument name="address" value="US_Address_CA"/> </actionGroup> @@ -81,7 +81,7 @@ <!-- Open created order in backend --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> <waitForPageLoad stepKey="waitForOrdersPageLoad"/> - <actionGroup ref="OpenOrderById" stepKey="filterOrderGridById"> + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml index e85a47ab7a91d..797172de5de45 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml @@ -116,7 +116,7 @@ <!-- Add Simple Product to cart --> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToSimpleProductPage"/> <waitForPageLoad stepKey="waitForSimpleProductPageLoad"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> <argument name="productName" value="$$createSimpleProduct.name$$"/> </actionGroup> @@ -131,14 +131,14 @@ <!-- Add Virtual Product to cart --> <amOnPage url="{{StorefrontProductPage.url($$createVirtualProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToVirtualProductPage"/> <waitForPageLoad stepKey="waitForVirtualProductPageLoad"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartVirtualProductFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartVirtualProductFromStorefrontProductPage"> <argument name="productName" value="$$createVirtualProduct.name$$"/> </actionGroup> <!-- Add Downloadable Product to cart --> <amOnPage url="{{StorefrontProductPage.url($$createDownloadableProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToDownloadableProductPage"/> <waitForPageLoad stepKey="waitForDownloadableProductPageLoad"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartDownloadableProductFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartDownloadableProductFromStorefrontProductPage"> <argument name="productName" value="$$createDownloadableProduct.name$$"/> </actionGroup> @@ -146,7 +146,7 @@ <amOnPage url="{{StorefrontProductPage.url($$createGroupedProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToGroupedProductPage"/> <waitForPageLoad stepKey="waitForGroupedProductPageLoad"/> <fillField selector="{{StorefrontProductPageSection.qtyInput}}" userInput="1" stepKey="fillFieldQtyInput"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartGroupedProductFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartGroupedProductFromStorefrontProductPage"> <argument name="productName" value="$$createGroupedProduct.name$$"/> </actionGroup> @@ -154,12 +154,12 @@ <amOnPage url="{{StorefrontProductPage.url($$createFixedBundleProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToBundleProductPage"/> <waitForPageLoad stepKey="waitForFixedBundleProductPageLoad"/> <click selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="clickCustomizeAndAddToCart"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFixedBundleProductFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFixedBundleProductFromStorefrontProductPage"> <argument name="productName" value="$$createFixedBundleProduct.name$$"/> </actionGroup> <!--Go to shopping cart--> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <actionGroup ref="FillShippingZipForm" stepKey="fillShippingZipForm"> <argument name="address" value="US_Address_CA"/> </actionGroup> @@ -197,7 +197,7 @@ <!-- Open created order --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> <waitForPageLoad stepKey="waitForOrderPageLoad"/> - <actionGroup ref="OpenOrderById" stepKey="filterOrderGridById"> + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml index 84cdb8abd9344..3a9bec5cdfcf3 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/ShoppingCartAndMiniShoppingCartPerCustomerTest.xml @@ -66,7 +66,7 @@ <waitForPageLoad stepKey="waitForFirstProductPageLoad"/> <!-- Add the product to the shopping cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addFirstProductToCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addFirstProductToCart"> <argument name="productName" value="$$createSimpleProduct.name$$"/> </actionGroup> @@ -102,7 +102,7 @@ <waitForPageLoad stepKey="waitForProductPage"/> <!-- Add the product to the shopping cart --> - <actionGroup ref="AddProductWithQtyToCartFromStorefrontProductPage" stepKey="addProductToCart"> + <actionGroup ref="AddProductWithQtyToCartFromStorefrontProductPageActionGroup" stepKey="addProductToCart"> <argument name="productName" value="$$createSimpleProduct.name$$"/> <argument name="productQty" value="{{quoteQty2Price123.qty}}"/> </actionGroup> @@ -137,10 +137,10 @@ <!-- Assert products in mini cart for first customer --> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStoreFrontHomePage"/> <waitForPageLoad stepKey="waitForHomePageLoad"/> - <actionGroup ref="assertOneProductNameInMiniCart" stepKey="assertFirstProductInMiniCart"> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="assertFirstProductInMiniCart"> <argument name="productName" value="$$createSimpleProduct.name$$"/> </actionGroup> - <actionGroup ref="assertOneProductNameInMiniCart" stepKey="assertSecondProductInMiniCart"> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="assertSecondProductInMiniCart"> <argument name="productName" value="$$createSimpleProductWithCustomOptions.name$$"/> </actionGroup> <actionGroup ref="AssertMiniShoppingCartSubTotalActionGroup" stepKey="assertMiniCartSubTotal"> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartWithoutAnySelectedOptionTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartWithoutAnySelectedOptionTest.xml index d108dc3657a40..450bfff27125a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartWithoutAnySelectedOptionTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartWithoutAnySelectedOptionTest.xml @@ -31,7 +31,7 @@ </after> <!-- Open Product page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct1PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct1PageAndVerifyProduct"> <argument name="product" value="$$createProduct$$"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontCheckCustomerInfoCreatedByGuestTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontCheckCustomerInfoCreatedByGuestTest.xml index 693c05684f292..c08a930ba6224 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontCheckCustomerInfoCreatedByGuestTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontCheckCustomerInfoCreatedByGuestTest.xml @@ -36,7 +36,7 @@ <amOnPage url="$$product.name$$.html" stepKey="navigateToProductPage"/> <waitForPageLoad stepKey="waitForProductPage"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> <argument name="productName" value="$$product.name$$"/> </actionGroup> <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml index 330a026bb9426..8a2990c5869c9 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontFreeShippingRecalculationAfterCouponCodeAddedTest.xml @@ -34,7 +34,7 @@ <createData entity="FreeShippingMethodsSettingConfig" stepKey="freeShippingMethodsSettingConfig"/> <createData entity="MinimumOrderAmount90" stepKey="minimumOrderAmount90"/> <magentoCLI command="cache:flush" stepKey="flushCache1"/> - <actionGroup ref="AdminCreateCartPriceRuleWithCouponCode" stepKey="createCartPriceRule"> + <actionGroup ref="AdminCreateCartPriceRuleWithCouponCodeActionGroup" stepKey="createCartPriceRule"> <argument name="ruleName" value="CatPriceRule"/> <argument name="couponCode" value="CatPriceRule.coupon_code"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml index fb80b4880a6f4..f17716af8fd56 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml @@ -32,12 +32,12 @@ <!--Add product to cart--> <amOnPage url="$$createProduct.name$$.html" stepKey="navigateToProductPage"/> <waitForPageLoad stepKey="waitForProductPage"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> <argument name="productName" value="$$createProduct.name$$"/> </actionGroup> <!--Go to Shopping cart and check Qty--> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCart"/> <grabValueFrom selector="{{CheckoutCartProductSection.ProductQuantityByName($$createProduct.name$$)}}" stepKey="grabQtyShoppingCart"/> <assertEquals expected="1" actual="$grabQtyShoppingCart" stepKey="assertQtyShoppingCart"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartTest.xml index 071311b78b3fa..5e5278a256194 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartTest.xml @@ -57,7 +57,7 @@ </after> <!--Open Product page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPageAndVerifyProduct"> <argument name="product" value="$$createBundleProduct$$"/> </actionGroup> @@ -82,7 +82,7 @@ </actionGroup> <!-- Select Mini Cart and select 'View And Edit Cart' --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="selectViewAndEditCart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="selectViewAndEditCart"/> <!--Assert Shopping Cart Summary--> <actionGroup ref="AssertStorefrontShoppingCartSummaryWithShippingActionGroup" stepKey="AssertCartSummary" > diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartWithDisableMiniCartSidebarTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartWithDisableMiniCartSidebarTest.xml index d67800e21afc2..5d5e2b3a91f49 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartWithDisableMiniCartSidebarTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartWithDisableMiniCartSidebarTest.xml @@ -61,7 +61,7 @@ </after> <!--Open Product page in StoreFront and assert product and price range --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPageAndVerifyProduct"> <argument name="product" value="$$createBundleProduct$$"/> </actionGroup> <actionGroup ref="AssertStorefrontElementVisibleActionGroup" stepKey="seePriceRangeIsVisible"> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml index e3090d6cb311b..e716ba294f578 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml @@ -131,7 +131,7 @@ </actionGroup> <!-- Select Mini Cart and select 'View And Edit Cart' --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="selectViewAndEditCart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="selectViewAndEditCart"/> <!--Assert Shopping Cart Summary --> <actionGroup ref="AssertStorefrontShoppingCartSummaryWithShippingActionGroup" stepKey="AssertCartSummary" > diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml index ec9852a6a939d..3c1421f2616aa 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml @@ -47,7 +47,7 @@ </actionGroup> <!-- Select Mini Cart and select 'View And Edit Cart' --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="selectViewAndEditCart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="selectViewAndEditCart"/> <!--Assert Shopping Cart Summary--> <actionGroup ref="AssertStorefrontShoppingCartSummaryItemsActionGroup" stepKey="AssertCartSummary" > diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddGroupedProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddGroupedProductToShoppingCartTest.xml index b3762c7a582b7..0fc4cee5e0582 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddGroupedProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddGroupedProductToShoppingCartTest.xml @@ -64,7 +64,7 @@ </actionGroup> <!-- Select Mini Cart and select 'View And Edit Cart' --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="selectViewAndEditCart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="selectViewAndEditCart"/> <!--Assert Product1 items in cart --> <actionGroup ref="AssertStorefrontCheckoutCartItemsActionGroup" stepKey="assertSimpleProduct1ItemsInCheckOutCart"> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddOneBundleMultiSelectOptionToTheShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddOneBundleMultiSelectOptionToTheShoppingCartTest.xml index 9b8e5a4521115..af12aecb6345a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddOneBundleMultiSelectOptionToTheShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddOneBundleMultiSelectOptionToTheShoppingCartTest.xml @@ -57,7 +57,7 @@ </after> <!--Open Product page in StoreFront and assert product details --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPageAndVerifyProduct"> <argument name="product" value="$$createBundleProduct$$"/> </actionGroup> <actionGroup ref="AssertStorefrontElementVisibleActionGroup" stepKey="seePriceRangeIsVisible"> @@ -81,7 +81,7 @@ </actionGroup> <!-- Select Mini Cart and select 'View And Edit Cart' --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="selectViewAndEditCart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="selectViewAndEditCart"/> <!--Assert Shopping Cart Summary--> <actionGroup ref="AssertStorefrontShoppingCartSummaryWithShippingActionGroup" stepKey="AssertCartSummary" > diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartTest.xml index e90f69e88cec7..319183d4641e6 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartTest.xml @@ -30,7 +30,7 @@ <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> </after> <!-- Open Product page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct1PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct1PageAndVerifyProduct"> <argument name="product" value="$$createProduct$$"/> </actionGroup> @@ -111,4 +111,4 @@ <argument name="qty" value="2"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml index 71e99f41f79e1..14788250a9bca 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml @@ -57,7 +57,7 @@ </after> <!--Open Product page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPageAndVerifyProduct"> <argument name="product" value="$$createBundleProduct$$"/> </actionGroup> <actionGroup ref="AssertStorefrontElementVisibleActionGroup" stepKey="seePriceRangeIsVisible"> @@ -79,7 +79,7 @@ </actionGroup> <!-- Select Mini Cart and select 'View And Edit Cart' --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="selectViewAndEditCart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="selectViewAndEditCart"/> <!--Assert Shopping Cart Summary--> <actionGroup ref="AssertStorefrontShoppingCartSummaryWithShippingActionGroup" stepKey="AssertCartSummary" > diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml index bdfdfceab53f9..7edc37d510266 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml @@ -42,7 +42,7 @@ </after> <!-- Go to Storefront as Guest and add simple product to cart --> - <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart"> <argument name="product" value="$$createProduct$$"/> </actionGroup> @@ -80,7 +80,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!-- Verify total on order page --> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderById"> <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest.xml index 0327deaf18968..c3f173961f0c5 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndCheckoutItemsCountTest.xml @@ -33,13 +33,13 @@ <!-- Add simpleProduct1 to cart --> <amOnPage url="{{StorefrontProductPage.url($$simpleProduct1.custom_attributes[url_key]$)}}" stepKey="amOnProduct1Page"/> - <actionGroup ref="AddProductWithQtyToCartFromStorefrontProductPage" stepKey="addProduct1ToCart"> + <actionGroup ref="AddProductWithQtyToCartFromStorefrontProductPageActionGroup" stepKey="addProduct1ToCart"> <argument name="productName" value="$$simpleProduct1.name$$"/> <argument name="productQty" value="2"/> </actionGroup> <!-- Add simpleProduct2 to cart --> <amOnPage url="{{StorefrontProductPage.url($$simpleProduct2.custom_attributes[url_key]$)}}" stepKey="amOnProduct2Page"/> - <actionGroup ref="AddProductWithQtyToCartFromStorefrontProductPage" stepKey="addProduct2ToCart"> + <actionGroup ref="AddProductWithQtyToCartFromStorefrontProductPageActionGroup" stepKey="addProduct2ToCart"> <argument name="productName" value="$$simpleProduct2.name$$"/> <argument name="productQty" value="1"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndSummaryBlockItemDisplayWithDefaultDisplayLimitationTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndSummaryBlockItemDisplayWithDefaultDisplayLimitationTest.xml index beb2d40f94cad..0b52caa7165af 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndSummaryBlockItemDisplayWithDefaultDisplayLimitationTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartAndSummaryBlockItemDisplayWithDefaultDisplayLimitationTest.xml @@ -67,102 +67,102 @@ </after> <!-- Open Product1 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct1PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct1PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <!-- Add Product1 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct1ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct1ToTheCart"> <argument name="productName" value="$$simpleProduct1.name$$"/> </actionGroup> <!-- Open Product2 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct2PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct2PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <!-- Add Product2 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct2ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct2ToTheCart"> <argument name="productName" value="$$simpleProduct2.name$$"/> </actionGroup> <!-- Open Product3 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct3PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct3PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct3$$"/> </actionGroup> <!-- Add Product3 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct3ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct3ToTheCart"> <argument name="productName" value="$$simpleProduct3.name$$"/> </actionGroup> <!-- Open Product4 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct4PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct4PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct4$$"/> </actionGroup> <!-- Add Product4 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct4ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct4ToTheCart"> <argument name="productName" value="$$simpleProduct4.name$$"/> </actionGroup> <!-- Open Product5 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct5PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct5PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct5$$"/> </actionGroup> <!-- Add Product5 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct5ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct5ToTheCart"> <argument name="productName" value="$$simpleProduct5.name$$"/> </actionGroup> <!-- Open Product6 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct6PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct6PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct6$$"/> </actionGroup> <!-- Add Product6 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct6ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct6ToTheCart"> <argument name="productName" value="$$simpleProduct6.name$$"/> </actionGroup> <!-- Open Product7 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct7PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct7PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct7$$"/> </actionGroup> <!-- Add Product7 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct7ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct7ToTheCart"> <argument name="productName" value="$$simpleProduct7.name$$"/> </actionGroup> <!-- Open Product8 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct8PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct8PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct8$$"/> </actionGroup> <!-- Add Product8 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct8ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct8ToTheCart"> <argument name="productName" value="$$simpleProduct8.name$$"/> </actionGroup> <!-- Open Product9 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct9PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct9PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct9$$"/> </actionGroup> <!-- Add Product9 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct9ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct9ToTheCart"> <argument name="productName" value="$$simpleProduct9.name$$"/> </actionGroup> <!-- Open Product10 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPage10AndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPage10AndVerifyProduct"> <argument name="product" value="$$simpleProduct10$$"/> </actionGroup> <!-- Add Product10 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct10ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct10ToTheCart"> <argument name="productName" value="$$simpleProduct10.name$$"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartItemDisplayWhenMoreItemsAddedToTheCartThanDefaultDisplayLimitTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartItemDisplayWhenMoreItemsAddedToTheCartThanDefaultDisplayLimitTest.xml index 8b8aed3ac6204..a496ff68c0cd0 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartItemDisplayWhenMoreItemsAddedToTheCartThanDefaultDisplayLimitTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartItemDisplayWhenMoreItemsAddedToTheCartThanDefaultDisplayLimitTest.xml @@ -72,112 +72,112 @@ </after> <!-- Open Product1 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct1PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct1PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <!-- Add Product1 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct1ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct1ToTheCart"> <argument name="productName" value="$$simpleProduct1.name$$"/> </actionGroup> <!-- Open Product2 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct2PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct2PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <!-- Add Product2 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct2ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct2ToTheCart"> <argument name="productName" value="$$simpleProduct2.name$$"/> </actionGroup> <!-- Open Product3 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct3PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct3PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct3$$"/> </actionGroup> <!-- Add Product3 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct3ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct3ToTheCart"> <argument name="productName" value="$$simpleProduct3.name$$"/> </actionGroup> <!-- Open Product4 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct4PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct4PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct4$$"/> </actionGroup> <!-- Add Product4 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct4ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct4ToTheCart"> <argument name="productName" value="$$simpleProduct4.name$$"/> </actionGroup> <!-- Open Product5 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct5PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct5PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct5$$"/> </actionGroup> <!-- Add Product5 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct5ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct5ToTheCart"> <argument name="productName" value="$$simpleProduct5.name$$"/> </actionGroup> <!-- Open Product6 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct6PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct6PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct6$$"/> </actionGroup> <!-- Add Product6 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct6ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct6ToTheCart"> <argument name="productName" value="$$simpleProduct6.name$$"/> </actionGroup> <!-- Open Product7 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct7PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct7PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct7$$"/> </actionGroup> <!-- Add Product7 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct7ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct7ToTheCart"> <argument name="productName" value="$$simpleProduct7.name$$"/> </actionGroup> <!-- Open Product8 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct8PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct8PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct8$$"/> </actionGroup> <!-- Add Product8 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct8ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct8ToTheCart"> <argument name="productName" value="$$simpleProduct8.name$$"/> </actionGroup> <!-- Open Product9 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct9PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct9PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct9$$"/> </actionGroup> <!-- Add Product9 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct9ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct9ToTheCart"> <argument name="productName" value="$$simpleProduct9.name$$"/> </actionGroup> <!-- Open Product10 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPage10AndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPage10AndVerifyProduct"> <argument name="product" value="$$simpleProduct10$$"/> </actionGroup> <!-- Add Product10 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct10ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct10ToTheCart"> <argument name="productName" value="$$simpleProduct10.name$$"/> </actionGroup> <!-- Open Product11 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPage11AndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPage11AndVerifyProduct"> <argument name="product" value="$$simpleProduct11$$"/> </actionGroup> <!-- Add Product11 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct11ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct11ToTheCart"> <argument name="productName" value="$$simpleProduct11.name$$"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartItemDisplayWithDefaultDisplayLimitAndDefaultTotalQuantityTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartItemDisplayWithDefaultDisplayLimitAndDefaultTotalQuantityTest.xml index 2339789bd85d1..8e84deafea9f2 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartItemDisplayWithDefaultDisplayLimitAndDefaultTotalQuantityTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartItemDisplayWithDefaultDisplayLimitAndDefaultTotalQuantityTest.xml @@ -65,102 +65,102 @@ </after> <!-- Open Product1 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct1PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct1PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <!-- Add Product1 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct1ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct1ToTheCart"> <argument name="productName" value="$$simpleProduct1.name$$"/> </actionGroup> <!-- Open Product2 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct2PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct2PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <!-- Add Product2 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct2ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct2ToTheCart"> <argument name="productName" value="$$simpleProduct2.name$$"/> </actionGroup> <!-- Open Product3 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct3PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct3PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct3$$"/> </actionGroup> <!-- Add Product3 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct3ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct3ToTheCart"> <argument name="productName" value="$$simpleProduct3.name$$"/> </actionGroup> <!-- Open Product4 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct4PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct4PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct4$$"/> </actionGroup> <!-- Add Product4 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct4ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct4ToTheCart"> <argument name="productName" value="$$simpleProduct4.name$$"/> </actionGroup> <!-- Open Product5 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct5PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct5PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct5$$"/> </actionGroup> <!-- Add Product5 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct5ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct5ToTheCart"> <argument name="productName" value="$$simpleProduct5.name$$"/> </actionGroup> <!-- Open Product6 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct6PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct6PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct6$$"/> </actionGroup> <!-- Add Product6 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct6ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct6ToTheCart"> <argument name="productName" value="$$simpleProduct6.name$$"/> </actionGroup> <!-- Open Product7 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct7PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct7PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct7$$"/> </actionGroup> <!-- Add Product7 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct7ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct7ToTheCart"> <argument name="productName" value="$$simpleProduct7.name$$"/> </actionGroup> <!-- Open Product8 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct8PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct8PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct8$$"/> </actionGroup> <!-- Add Product8 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct8ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct8ToTheCart"> <argument name="productName" value="$$simpleProduct8.name$$"/> </actionGroup> <!-- Open Product9 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct9PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct9PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct9$$"/> </actionGroup> <!-- Add Product9 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct9ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct9ToTheCart"> <argument name="productName" value="$$simpleProduct9.name$$"/> </actionGroup> <!-- Open Product10 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPage10AndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPage10AndVerifyProduct"> <argument name="product" value="$$simpleProduct10$$"/> </actionGroup> <!-- Add Product10 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct10ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct10ToTheCart"> <argument name="productName" value="$$simpleProduct10.name$$"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml new file mode 100644 index 0000000000000..caec34c5ef1aa --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest.xml @@ -0,0 +1,143 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCheckPagerShoppingCartWithMoreThan20ProductsTest"> + <annotations> + <features value="Checkout"/> + <stories value="Check if the cart pager is visible with more than 20 cart items and the pager disappears if an item is removed from cart"/> + <title value="Test if the cart pager is visible with more than 20 cart items and the pager disappears if an item is removed from cart."/> + <description value="Test if the cart pager is visible with more than 20 cart items and the pager disappears if an item is removed from cart."/> + <severity value="MAJOR"/> + <testCaseId value="MC-14700"/> + <group value="shoppingCart"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!--Set the default number of items on cart which is 20--> + <magentoCLI stepKey="allowSpecificValue" command="config:set checkout/cart/number_items_to_display_pager 20" /> + + <createData entity="SimpleTwo" stepKey="simpleProduct1"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem1"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct2"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem2"> + <argument name="product" value="$simpleProduct2$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct3"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem3"> + <argument name="product" value="$simpleProduct3$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct4"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem4"> + <argument name="product" value="$simpleProduct4$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct5"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem5"> + <argument name="product" value="$simpleProduct5$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct6"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem6"> + <argument name="product" value="$simpleProduct6$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct7"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem7"> + <argument name="product" value="$simpleProduct7$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct8"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem8"> + <argument name="product" value="$simpleProduct8$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct9"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem9"> + <argument name="product" value="$simpleProduct9$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct10"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem10"> + <argument name="product" value="$$simpleProduct10$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct11"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem11"> + <argument name="product" value="$$simpleProduct11$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct12"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem12"> + <argument name="product" value="$$simpleProduct12$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct13"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem13"> + <argument name="product" value="$$simpleProduct13$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct14"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem14"> + <argument name="product" value="$$simpleProduct14$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct15"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem15"> + <argument name="product" value="$$simpleProduct15$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct16"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem16"> + <argument name="product" value="$$simpleProduct16$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct17"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem17"> + <argument name="product" value="$$simpleProduct17$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct18"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem18"> + <argument name="product" value="$$simpleProduct18$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct19"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem19"> + <argument name="product" value="$$simpleProduct19$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct20"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem20"> + <argument name="product" value="$$simpleProduct20$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct21"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem21"> + <argument name="product" value="$$simpleProduct21$$"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="simpleProduct1" stepKey="deleteCartItem1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteCartItem2"/> + <deleteData createDataKey="simpleProduct3" stepKey="deleteCartItem3"/> + <deleteData createDataKey="simpleProduct4" stepKey="deleteCartItem4"/> + <deleteData createDataKey="simpleProduct5" stepKey="deleteCartItem5"/> + <deleteData createDataKey="simpleProduct6" stepKey="deleteCartItem6"/> + <deleteData createDataKey="simpleProduct7" stepKey="deleteCartItem7"/> + <deleteData createDataKey="simpleProduct8" stepKey="deleteCartItem8"/> + <deleteData createDataKey="simpleProduct9" stepKey="deleteCartItem9"/> + <deleteData createDataKey="simpleProduct10" stepKey="deleteCartItem10"/> + <deleteData createDataKey="simpleProduct11" stepKey="deleteCartItem11"/> + <deleteData createDataKey="simpleProduct12" stepKey="deleteCartItem12"/> + <deleteData createDataKey="simpleProduct13" stepKey="deleteCartItem13"/> + <deleteData createDataKey="simpleProduct14" stepKey="deleteCartItem14"/> + <deleteData createDataKey="simpleProduct15" stepKey="deleteCartItem15"/> + <deleteData createDataKey="simpleProduct16" stepKey="deleteCartItem16"/> + <deleteData createDataKey="simpleProduct17" stepKey="deleteCartItem17"/> + <deleteData createDataKey="simpleProduct18" stepKey="deleteCartItem18"/> + <deleteData createDataKey="simpleProduct19" stepKey="deleteCartItem19"/> + <deleteData createDataKey="simpleProduct20" stepKey="deleteCartItem20"/> + <deleteData createDataKey="simpleProduct21" stepKey="deleteCartItem21"/> + </after> + <actionGroup ref="StorefrontOpenCartPageActionGroup" stepKey="goToCartPage" /> + <actionGroup ref="AssertToolbarTextIsVisibleInCartActionGroup" stepKey="VerifyPagerText"> + <argument name="text" value="Items 1 to 20 of 21 total"/> + </actionGroup> + <actionGroup ref="StorefrontRemoveCartItemActionGroup" stepKey="removeCartItem" /> + <actionGroup ref="AssertPagerTextIsNotVisibleActionGroup" stepKey="VerifyMissingPagerText2" > + <argument name="text" value="Items 1 to 20"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckSimpleProductCartItemDisplayWithDefaultLimitationTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckSimpleProductCartItemDisplayWithDefaultLimitationTest.xml index 084c89312cc7e..79e46d093c2f6 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckSimpleProductCartItemDisplayWithDefaultLimitationTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckSimpleProductCartItemDisplayWithDefaultLimitationTest.xml @@ -71,112 +71,112 @@ </after> <!-- Open Product1 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct1PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct1PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <!-- Add Product1 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct1ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct1ToTheCart"> <argument name="productName" value="$$simpleProduct1.name$$"/> </actionGroup> <!-- Open Product2 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct2PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct2PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <!-- Add Product2 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct2ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct2ToTheCart"> <argument name="productName" value="$$simpleProduct2.name$$"/> </actionGroup> <!-- Open Product3 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct3PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct3PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct3$$"/> </actionGroup> <!-- Add Product3 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct3ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct3ToTheCart"> <argument name="productName" value="$$simpleProduct3.name$$"/> </actionGroup> <!-- Open Product4 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct4PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct4PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct4$$"/> </actionGroup> <!-- Add Product4 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct4ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct4ToTheCart"> <argument name="productName" value="$$simpleProduct4.name$$"/> </actionGroup> <!-- Open Product5 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct5PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct5PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct5$$"/> </actionGroup> <!-- Add Product5 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct5ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct5ToTheCart"> <argument name="productName" value="$$simpleProduct5.name$$"/> </actionGroup> <!-- Open Product6 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct6PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct6PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct6$$"/> </actionGroup> <!-- Add Product6 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct6ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct6ToTheCart"> <argument name="productName" value="$$simpleProduct6.name$$"/> </actionGroup> <!-- Open Product7 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct7PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct7PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct7$$"/> </actionGroup> <!-- Add Product7 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct7ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct7ToTheCart"> <argument name="productName" value="$$simpleProduct7.name$$"/> </actionGroup> <!-- Open Product8 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct8PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct8PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct8$$"/> </actionGroup> <!-- Add Product8 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct8ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct8ToTheCart"> <argument name="productName" value="$$simpleProduct8.name$$"/> </actionGroup> <!-- Open Product9 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct9PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct9PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct9$$"/> </actionGroup> <!-- Add Product9 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct9ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct9ToTheCart"> <argument name="productName" value="$$simpleProduct9.name$$"/> </actionGroup> <!-- Open Product10 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPage10AndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPage10AndVerifyProduct"> <argument name="product" value="$$simpleProduct10$$"/> </actionGroup> <!-- Add Product10 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct10ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct10ToTheCart"> <argument name="productName" value="$$simpleProduct10.name$$"/> </actionGroup> <!-- Open Product11 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPage11AndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPage11AndVerifyProduct"> <argument name="product" value="$$simpleProduct11$$"/> </actionGroup> <!-- Add Product11 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct11ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct11ToTheCart"> <argument name="productName" value="$$simpleProduct11.name$$"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckVirtualProductCountDisplayWithCustomDisplayConfigurationTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckVirtualProductCountDisplayWithCustomDisplayConfigurationTest.xml index 1f63565899f88..9f3eacbf5f455 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckVirtualProductCountDisplayWithCustomDisplayConfigurationTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckVirtualProductCountDisplayWithCustomDisplayConfigurationTest.xml @@ -46,42 +46,42 @@ </after> <!-- Open Product1 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct1PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct1PageAndVerifyProduct"> <argument name="product" value="$$virtualProduct1$$"/> </actionGroup> <!-- Add Product1 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct1ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct1ToTheCart"> <argument name="productName" value="$$virtualProduct1.name$$"/> </actionGroup> <!-- Open Product2 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct2PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct2PageAndVerifyProduct"> <argument name="product" value="$$virtualProduct2$$"/> </actionGroup> <!-- Add Product2 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct2ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct2ToTheCart"> <argument name="productName" value="$$virtualProduct2.name$$"/> </actionGroup> <!-- Open Product3 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct3PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct3PageAndVerifyProduct"> <argument name="product" value="$$virtualProduct3$$"/> </actionGroup> <!-- Add Product3 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct3ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct3ToTheCart"> <argument name="productName" value="$$virtualProduct3.name$$"/> </actionGroup> <!-- Open Product4 page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct4PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct4PageAndVerifyProduct"> <argument name="product" value="$$virtualProduct4$$"/> </actionGroup> <!-- Add Product4 to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct4ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct4ToTheCart"> <argument name="productName" value="$$virtualProduct4.name$$"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutDisabledBundleProductTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutDisabledBundleProductTest.xml new file mode 100644 index 0000000000000..7405a3100728f --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutDisabledBundleProductTest.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="StorefrontCheckoutDisabledBundleProductTest"> + <annotations> + <features value="Checkout"/> + <stories value="Disabled bundle product is preventing customer to checkout for the first attempt"/> + <title value="Customer should be able to checkout if there is at least one available product in the cart"/> + <description value="Customer should be able to checkout if there is at least one available product in the cart"/> + <severity value="MINOR"/> + <testCaseId value="MC-29105"/> + <group value="checkout"/> + </annotations> + + <before> + <!-- Create category and simple product --> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- Create bundle product --> + <createData entity="ApiBundleProductPriceViewRange" stepKey="createBundleDynamicProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="createBundleDynamicProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createNewBundleLink"> + <requiredEntity createDataKey="createBundleDynamicProduct"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="createSimpleProduct"/> + </createData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="cacheFlush"/> + </before> + <after> + <!-- Delete category --> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <!-- Delete bundle product data --> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createBundleDynamicProduct" stepKey="deleteBundleProduct"/> + </after> + <!-- Add simple product to the cart --> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="cartAddSimpleProductToCart"> + <argument name="product" value="$$createSimpleProduct$$"/> + <argument name="productCount" value="1"/> + </actionGroup> + <!-- Go to bundle product page --> + <amOnPage url="{{StorefrontProductPage.url($$createBundleDynamicProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToProductPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <!-- Add bundle product to the cart --> + <click selector="{{StorefrontBundleProductActionSection.customizeAndAddToCartButton}}" stepKey="clickCustomizeAndAddToCart"/> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProductToCart"> + <argument name="productName" value="$$createBundleDynamicProduct.name$$"/> + </actionGroup> + <!-- Login to admin panel --> + <openNewTab stepKey="openNewTab"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- Find the first simple product that we just created using the product grid and go to its page--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> + <!-- Disabled bundle product from grid --> + <actionGroup ref="ChangeStatusProductUsingProductGridActionGroup" stepKey="disabledProductFromGrid"> + <argument name="product" value="$$createBundleDynamicProduct$$"/> + <argument name="status" value="Disable"/> + </actionGroup> + <closeTab stepKey="closeTab"/> + <!-- Go to cart page--> + <actionGroup ref="StorefrontOpenCartPageActionGroup" stepKey="openCartPage"/> + <!-- Assert checkout button exists on the page--> + <seeElement selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="seeCheckoutButton"/> + <!-- Assert no error message is not shown on the page--> + <dontSee userInput="Some of the products are out of stock." stepKey="seeNoItemsInShoppingCart"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndProductWithTierPricesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndProductWithTierPricesTest.xml index c5d1c34a93b32..07d29aa0aac4a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndProductWithTierPricesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndProductWithTierPricesTest.xml @@ -23,10 +23,10 @@ <createData entity="SimpleProduct2" stepKey="simpleProduct"> <field key="price">50.00</field> </createData> - <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheProduct"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="filterAndSelectTheProduct"> <argument name="productSku" value="$$simpleProduct.sku$$"/> </actionGroup> - <actionGroup ref="ProductSetAdvancedTierFixedPricing" stepKey="setTierPrice"> + <actionGroup ref="ProductSetAdvancedTierFixedPricingActionGroup" stepKey="setTierPrice"> <argument name="website" value=""/> <argument name="group" value=""/> <argument name="quantity" value="3"/> @@ -49,7 +49,7 @@ </after> <!--Open Product page in StoreFront and assert product and price range --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPageAndVerifyProduct"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> @@ -59,7 +59,7 @@ </actionGroup> <!--Open View and edit --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="clickMiniCart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="clickMiniCart"/> <!-- Fill the Estimate Shipping and Tax section --> <actionGroup ref="CheckoutFillEstimateShippingAndTaxActionGroup" stepKey="fillEstimateShippingAndTaxFields"/> @@ -111,7 +111,7 @@ <waitForPageLoad stepKey="waitForOrderIndexPageToLoad"/> <!-- Filter Order using orderId and assert order--> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$orderId"/> </actionGroup> <click selector="{{AdminOrdersGridSection.viewLink('$orderId')}}" stepKey="clickOnViewLink"/> @@ -127,4 +127,4 @@ <!-- Assert order buttons --> <actionGroup ref="AdminAssertOrderAvailableButtonsActionGroup" stepKey="assertOrderButtons"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndRegisterCustomerAfterCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndRegisterCustomerAfterCheckoutTest.xml index 34dc6617d25d5..2f19dcd2dc32f 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndRegisterCustomerAfterCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithDifferentShippingAndBillingAddressAndRegisterCustomerAfterCheckoutTest.xml @@ -35,7 +35,7 @@ </after> <!--Open Product page in StoreFront and assert product and price range --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPageAndVerifyProduct"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> @@ -45,7 +45,7 @@ </actionGroup> <!--Open View and edit --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="clickMiniCart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="clickMiniCart"/> <!-- Fill the Estimate Shipping and Tax section --> <actionGroup ref="CheckoutFillEstimateShippingAndTaxActionGroup" stepKey="fillEstimateShippingAndTaxFields"/> @@ -81,7 +81,7 @@ <waitForPageLoad stepKey="waitForPageLoad5"/> <!-- Filter Order using orderId and assert order--> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$orderId"/> </actionGroup> @@ -98,4 +98,4 @@ <!-- Assert order buttons --> <actionGroup ref="AdminAssertOrderAvailableButtonsActionGroup" stepKey="assertOrderButtons"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml index 6ccb05bf4c4f7..a9db81620d329 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml @@ -24,7 +24,7 @@ <field key="price">10.00</field> </createData> - <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheProduct"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="filterAndSelectTheProduct"> <argument name="productSku" value="$$simpleProduct.sku$$"/> </actionGroup> @@ -81,7 +81,7 @@ <requiredEntity createDataKey="createConfigChildProduct2"/> </createData> - <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheProduct2"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="filterAndSelectTheProduct2"> <argument name="productSku" value="$$createConfigChildProduct2.sku$$"/> </actionGroup> <actionGroup ref="AddSpecialPriceToProductActionGroup" stepKey="addSpecialPriceTopTheProduct2"> @@ -105,7 +105,7 @@ </after> <!--Open Product page in StoreFront and assert product and price range --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPageAndVerifyProduct"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> @@ -123,7 +123,7 @@ </actionGroup> <!--Open View and edit --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="clickMiniCart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="clickMiniCart"/> <!-- Fill the Estimate Shipping and Tax section --> <actionGroup ref="CheckoutFillEstimateShippingAndTaxActionGroup" stepKey="fillEstimateShippingAndTaxFields"/> @@ -151,7 +151,7 @@ <waitForPageLoad stepKey="waitForPageLoad5"/> <!-- Filter Order using orderId and assert order--> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$orderId"/> </actionGroup> @@ -168,4 +168,4 @@ <!-- Assert order buttons --> <actionGroup ref="AdminAssertOrderAvailableButtonsActionGroup" stepKey="assertOrderButtons"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutDisabledProductAndCouponTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutDisabledProductAndCouponTest.xml new file mode 100644 index 0000000000000..f8e1f32e93f52 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutDisabledProductAndCouponTest.xml @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCustomerCheckoutDisabledProductAndCouponTest"> + <annotations> + <features value="Checkout"/> + <stories value="Checkout via the Storefront"/> + <title value="Customer can login if product in his cart was disabled"/> + <description value="Customer can login with disabled product in the cart and a coupon applied"/> + <severity value="MINOR"/> + <testCaseId value="MC-21996"/> + <group value="checkout"/> + </annotations> + + <before> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="Simple_US_Customer" stepKey="createUSCustomer"/> + <!-- Create sales rule with coupon --> + <createData entity="SalesRuleSpecificCouponAndByPercent" stepKey="createSalesRule"/> + <createData entity="SimpleSalesRuleCoupon" stepKey="createCouponForCartPriceRule"> + <requiredEntity createDataKey="createSalesRule"/> + </createData> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createUSCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createSalesRule" stepKey="deleteSalesRule"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductListing"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> + </after> + + <!-- Login as Customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> + <argument name="Customer" value="$$createUSCustomer$$" /> + </actionGroup> + + <!-- Add product to shopping cart --> + <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.name$$)}}" stepKey="amOnSimpleProductPage"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="cartAddSimpleProductToCart"> + <argument name="product" value="$$createSimpleProduct$$"/> + <argument name="productCount" value="1"/> + </actionGroup> + + <!-- Open View and edit --> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="clickMiniCart1"/> + + <!-- Fill the Estimate Shipping and Tax section --> + <actionGroup ref="CheckoutFillEstimateShippingAndTaxActionGroup" stepKey="fillEstimateShippingAndTaxFields"/> + + <!-- Apply Coupon --> + <actionGroup ref="StorefrontApplyCouponActionGroup" stepKey="applyDiscount"> + <argument name="coupon" value="$$createCouponForCartPriceRule$$"/> + </actionGroup> + + <!-- Sign out Customer from storefront --> + <actionGroup ref="StorefrontSignOutActionGroup" stepKey="customerLogout"/> + + <!-- Login to admin panel --> + <openNewTab stepKey="openNewTab"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Find the first simple product that we just created using the product grid and go to its page--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> + + <!-- Disabled simple product from grid --> + <actionGroup ref="ChangeStatusProductUsingProductGridActionGroup" stepKey="disabledProductFromGrid"> + <argument name="product" value="$$createSimpleProduct$$"/> + <argument name="status" value="Disable"/> + </actionGroup> + <closeTab stepKey="closeTab"/> + + <!-- Login as Customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLoginSecondTime"> + <argument name="Customer" value="$$createUSCustomer$$" /> + </actionGroup> + + <!-- Check cart --> + <click selector="{{StorefrontMiniCartSection.show}}" stepKey="clickMiniCart2"/> + <dontSeeElement selector="{{StorefrontMiniCartSection.quantity}}" stepKey="dontSeeCartItem"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutOnLoginWhenGuestCheckoutIsDisabledTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutOnLoginWhenGuestCheckoutIsDisabledTest.xml index b0b72515611e8..1c3d3d4914fb4 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutOnLoginWhenGuestCheckoutIsDisabledTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutOnLoginWhenGuestCheckoutIsDisabledTest.xml @@ -39,7 +39,7 @@ </after> <!--Open Product page in StoreFront and assert product and price range --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPageAndVerifyProduct"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> @@ -49,7 +49,7 @@ </actionGroup> <!--Open View and edit --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="clickMiniCart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="clickMiniCart"/> <!-- Fill the Estimate Shipping and Tax section --> <actionGroup ref="CheckoutFillEstimateShippingAndTaxActionGroup" stepKey="fillEstimateShippingAndTaxFields"/> @@ -81,7 +81,7 @@ <waitForPageLoad stepKey="waitForPageLoad"/> <!-- Filter Order using orderId and assert order--> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$orderId"/> </actionGroup> @@ -91,4 +91,4 @@ <!-- Ship the order and assert the shipping status --> <actionGroup ref="AdminShipThePendingOrderActionGroup" stepKey="shipTheOrder"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml index 4e19de659be26..917a4ac58464f 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml @@ -116,15 +116,19 @@ <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> <!-- Add NY and CA tax rules --> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addNYTaxRate"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> <argument name="taxCode" value="SimpleTaxNY"/> </actionGroup> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addCATaxRate"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> <argument name="taxCode" value="SimpleTaxCA"/> </actionGroup> <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + + <!--TODO: REMOVE AFTER FIX MC-21717 --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush full_page" stepKey="flushCache"/> </before> <after> <!-- Go to the tax rule page and delete the row we created--> @@ -274,4 +278,4 @@ <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithNewCustomerRegistrationAndDisableGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithNewCustomerRegistrationAndDisableGuestCheckoutTest.xml index f7e54867b1ae4..e0fff27a583e7 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithNewCustomerRegistrationAndDisableGuestCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithNewCustomerRegistrationAndDisableGuestCheckoutTest.xml @@ -39,7 +39,7 @@ </after> <!--Open Product page in StoreFront and assert product and price range --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPageAndVerifyProduct"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> @@ -49,7 +49,7 @@ </actionGroup> <!--Open View and edit --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="clickMiniCart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="clickMiniCart"/> <click selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="goToCheckout"/> <waitForPageLoad stepKey="waitForPageToLoad"/> @@ -83,7 +83,7 @@ <see userInput="You saved the address." stepKey="verifyAddressAdded"/> <!-- Open Edit and View from cart --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="openViewAndEditOption"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="openViewAndEditOption"/> <!-- Proceed to checkout --> <click selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="goToCheckout1"/> @@ -119,7 +119,7 @@ <waitForPageLoad stepKey="waitForPageLoad"/> <!-- Filter Order using orderId and assert order--> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$orderId"/> </actionGroup> @@ -129,4 +129,4 @@ <!-- Ship the order and assert the shipping status --> <actionGroup ref="AdminShipThePendingOrderActionGroup" stepKey="shipTheOrder"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithoutRegionTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithoutRegionTest.xml index 0cc0dcf38e312..a6b9372679a11 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithoutRegionTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithoutRegionTest.xml @@ -25,12 +25,12 @@ </createData> <createData entity="Simple_GB_Customer" stepKey="createCustomer"/> <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> - <actionGroup ref="SelectCountriesWithRequiredRegion" stepKey="setCustomCountryWithRequiredRegion"> + <actionGroup ref="SelectCountriesWithRequiredRegionActionGroup" stepKey="setCustomCountryWithRequiredRegion"> <argument name="countries" value="CustomCountryWithRequiredRegion"/> </actionGroup> </before> <after> - <actionGroup ref="SelectCountriesWithRequiredRegion" stepKey="setDefaultCountriesWithRequiredRegion"> + <actionGroup ref="SelectCountriesWithRequiredRegionActionGroup" stepKey="setDefaultCountriesWithRequiredRegion"> <argument name="countries" value="DefaultCountriesWithRequiredRegions"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> @@ -43,7 +43,7 @@ <argument name="Customer" value="$$createCustomer$$" /> </actionGroup> - <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart"> <argument name="product" value="$$createProduct$$"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml index 3a0ba2302a6dc..afefbff5ea59a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerLoginDuringCheckoutTest.xml @@ -55,7 +55,7 @@ </actionGroup> <!-- Go to Checkout page --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <click selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="clickProceedToCheckout"/> <waitForPageLoad stepKey="waitForProceedToCheckout"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml index 651c5bd8d4375..95ea08e1e7d9a 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml @@ -39,14 +39,14 @@ </actionGroup> <!-- Add simple product to cart and go to checkout--> - <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart"> <argument name="product" value="$$createProduct$$"/> </actionGroup> <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart" /> <!-- Click "+ New Address" and Fill new address--> <click selector="{{CheckoutShippingSection.newAddressButton}}" stepKey="addAddress"/> - <actionGroup ref="LoggedInCheckoutWithOneAddressFieldWithoutStateField" stepKey="changeAddress"> + <actionGroup ref="LoggedInCheckoutWithOneAddressFieldWithoutStateFieldActionGroup" stepKey="changeAddress"> <argument name="Address" value="UK_Not_Default_Address"/> <argument name="classPrefix" value="._show"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteBundleProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteBundleProductFromMiniShoppingCartTest.xml index 8f3ddbb27f62f..8956ed7687f06 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteBundleProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteBundleProductFromMiniShoppingCartTest.xml @@ -50,7 +50,7 @@ </after> <!--Open Product page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPageAndVerifyProduct"> <argument name="product" value="$$createBundleProduct$$"/> </actionGroup> @@ -75,18 +75,18 @@ </actionGroup> <!-- Select Mini Cart and select 'View And Edit Cart' --> - <actionGroup ref="assertOneProductNameInMiniCart" stepKey="seeProductInMiniCart"> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="seeProductInMiniCart"> <argument name="productName" value="$$createBundleProduct.name$$"/> </actionGroup> <!--Remove an item from the cart using minicart--> - <actionGroup ref="removeProductFromMiniCart" stepKey="removeProductFromMiniCart"> + <actionGroup ref="RemoveProductFromMiniCartActionGroup" stepKey="removeProductFromMiniCart"> <argument name="productName" value="$$createBundleProduct.name$$"/> </actionGroup> <reloadPage stepKey="reloadPage"/> <!--Check the minicart is empty and verify AssertProductAbsentInMiniShoppingCart--> - <actionGroup ref="assertMiniCartEmpty" stepKey="miniCartEnpty"/> + <actionGroup ref="AssertMiniCartEmptyActionGroup" stepKey="miniCartEnpty"/> <dontSee selector="{{StorefrontMinicartSection.productLinkByName($$createBundleProduct.name$$)}}" stepKey="verifyAssertProductAbsentInMiniShoppingCart"/> </test> </tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromMiniShoppingCartTest.xml index f6357bcf4caa2..34264e5982651 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromMiniShoppingCartTest.xml @@ -83,18 +83,18 @@ </actionGroup> <!-- Select Mini Cart and select 'View And Edit Cart' --> - <actionGroup ref="assertOneProductNameInMiniCart" stepKey="seeProductInMiniCart"> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="seeProductInMiniCart"> <argument name="productName" value="$$createConfigProduct.name$$"/> </actionGroup> <!--Remove an item from the cart using minicart--> - <actionGroup ref="removeProductFromMiniCart" stepKey="removeProductFromMiniCart"> + <actionGroup ref="RemoveProductFromMiniCartActionGroup" stepKey="removeProductFromMiniCart"> <argument name="productName" value="$$createConfigProduct.name$$"/> </actionGroup> <reloadPage stepKey="reloadPage"/> <!--Check the minicart is empty and verify AssertProductAbsentInMiniShoppingCart--> - <actionGroup ref="assertMiniCartEmpty" stepKey="miniCartEnpty"/> + <actionGroup ref="AssertMiniCartEmptyActionGroup" stepKey="miniCartEnpty"/> <dontSee selector="{{StorefrontMinicartSection.productLinkByName($$createConfigProduct.name$$)}}" stepKey="verifyAssertProductAbsentInMiniShoppingCart"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml index 0fa503e1783b5..dd9259833cbc4 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteDownloadableProductFromMiniShoppingCartTest.xml @@ -46,7 +46,7 @@ </actionGroup> <!-- Select Mini Cart and select 'View And Edit Cart' --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="selectViewAndEditCart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="selectViewAndEditCart"/> <!-- Assert product details in Mini Cart --> <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> @@ -59,18 +59,18 @@ </actionGroup> <!-- Select Mini Cart and select 'View And Edit Cart' --> - <actionGroup ref="assertOneProductNameInMiniCart" stepKey="seeProductInMiniCart"> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="seeProductInMiniCart"> <argument name="productName" value="$$createDownloadableProduct.name$$"/> </actionGroup> <!--Remove an item from the cart using minicart--> - <actionGroup ref="removeProductFromMiniCart" stepKey="removeProductFromMiniCart"> + <actionGroup ref="RemoveProductFromMiniCartActionGroup" stepKey="removeProductFromMiniCart"> <argument name="productName" value="$$createDownloadableProduct.name$$"/> </actionGroup> <reloadPage stepKey="reloadPage"/> <!--Check the minicart is empty and verify AssertProductAbsentInMiniShoppingCart--> - <actionGroup ref="assertMiniCartEmpty" stepKey="miniCartEnpty"/> + <actionGroup ref="AssertMiniCartEmptyActionGroup" stepKey="miniCartEnpty"/> <dontSee selector="{{StorefrontMinicartSection.productLinkByName($$createDownloadableProduct.name$$)}}" stepKey="verifyAssertProductAbsentInMiniShoppingCart"/> </test> </tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteProductsWithCartItemsDisplayDefaultLimitationFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteProductsWithCartItemsDisplayDefaultLimitationFromMiniShoppingCartTest.xml index cca5268564b12..07215988c945d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteProductsWithCartItemsDisplayDefaultLimitationFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteProductsWithCartItemsDisplayDefaultLimitationFromMiniShoppingCartTest.xml @@ -67,92 +67,92 @@ </after> <!--Open Product1 page in StoreFront--> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct1PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct1PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <!--Add Product1 to the cart--> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct1ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct1ToTheCart"> <argument name="productName" value="$$simpleProduct1.name$$"/> </actionGroup> <!--Open Product2 page in StoreFront--> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct2PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct2PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <!--Add Product2 to the cart--> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct2ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct2ToTheCart"> <argument name="productName" value="$$simpleProduct2.name$$"/> </actionGroup> <!--Open Product3 page in StoreFront--> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct3PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct3PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct3$$"/> </actionGroup> <!--Add Product3 to the cart--> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct3ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct3ToTheCart"> <argument name="productName" value="$$simpleProduct3.name$$"/> </actionGroup> <!--Open Product4 page in StoreFront--> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct4PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct4PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct4$$"/> </actionGroup> <!--Add Product4 to the cart--> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct4ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct4ToTheCart"> <argument name="productName" value="$$simpleProduct4.name$$"/> </actionGroup> <!--Open Product5 page in StoreFront--> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct5PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct5PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct5$$"/> </actionGroup> <!--Add Product5 to the cart--> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct5ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct5ToTheCart"> <argument name="productName" value="$$simpleProduct5.name$$"/> </actionGroup> <!--Open Product6 page in StoreFront--> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct6PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct6PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct6$$"/> </actionGroup> <!--Add Product6 to the cart--> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct6ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct6ToTheCart"> <argument name="productName" value="$$simpleProduct6.name$$"/> </actionGroup> <!--Open Product7 page in StoreFront--> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct7PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct7PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct7$$"/> </actionGroup> <!--Add Product7 to the cart--> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct7ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct7ToTheCart"> <argument name="productName" value="$$simpleProduct7.name$$"/> </actionGroup> <!--Open Product8 page in StoreFront--> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct8PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct8PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct8$$"/> </actionGroup> <!--Add Product8 to the cart--> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct8ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct8ToTheCart"> <argument name="productName" value="$$simpleProduct8.name$$"/> </actionGroup> <!--Open Product9 page in StoreFront--> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProduct9PageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProduct9PageAndVerifyProduct"> <argument name="product" value="$$simpleProduct9$$"/> </actionGroup> <!--Add Product9 to the cart--> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct9ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct9ToTheCart"> <argument name="productName" value="$$simpleProduct9.name$$"/> </actionGroup> <!--Open Product10 page in StoreFront--> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPage10AndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPage10AndVerifyProduct"> <argument name="product" value="$$simpleProduct10$$"/> </actionGroup> <!--Add Product10 to the cart--> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct10ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct10ToTheCart"> <argument name="productName" value="$$simpleProduct10.name$$"/> </actionGroup> @@ -237,40 +237,40 @@ </actionGroup> <!--Remove products from minicart--> - <actionGroup ref="removeProductFromMiniCart" stepKey="removeProduct1FromMiniCart"> + <actionGroup ref="RemoveProductFromMiniCartActionGroup" stepKey="removeProduct1FromMiniCart"> <argument name="productName" value="$$simpleProduct10.name$$"/> </actionGroup> - <actionGroup ref="removeProductFromMiniCart" stepKey="removeProduct2FromMiniCart"> + <actionGroup ref="RemoveProductFromMiniCartActionGroup" stepKey="removeProduct2FromMiniCart"> <argument name="productName" value="$$simpleProduct9.name$$"/> </actionGroup> - <actionGroup ref="removeProductFromMiniCart" stepKey="removeProduct3FromMiniCart"> + <actionGroup ref="RemoveProductFromMiniCartActionGroup" stepKey="removeProduct3FromMiniCart"> <argument name="productName" value="$$simpleProduct8.name$$"/> </actionGroup> - <actionGroup ref="removeProductFromMiniCart" stepKey="removeProduct4FromMiniCart"> + <actionGroup ref="RemoveProductFromMiniCartActionGroup" stepKey="removeProduct4FromMiniCart"> <argument name="productName" value="$$simpleProduct7.name$$"/> </actionGroup> - <actionGroup ref="removeProductFromMiniCart" stepKey="removeProduct5FromMiniCart"> + <actionGroup ref="RemoveProductFromMiniCartActionGroup" stepKey="removeProduct5FromMiniCart"> <argument name="productName" value="$$simpleProduct6.name$$"/> </actionGroup> - <actionGroup ref="removeProductFromMiniCart" stepKey="removeProduct6FromMiniCart"> + <actionGroup ref="RemoveProductFromMiniCartActionGroup" stepKey="removeProduct6FromMiniCart"> <argument name="productName" value="$$simpleProduct5.name$$"/> </actionGroup> - <actionGroup ref="removeProductFromMiniCart" stepKey="removeProduct7FromMiniCart"> + <actionGroup ref="RemoveProductFromMiniCartActionGroup" stepKey="removeProduct7FromMiniCart"> <argument name="productName" value="$$simpleProduct4.name$$"/> </actionGroup> - <actionGroup ref="removeProductFromMiniCart" stepKey="removeProduct8FromMiniCart"> + <actionGroup ref="RemoveProductFromMiniCartActionGroup" stepKey="removeProduct8FromMiniCart"> <argument name="productName" value="$$simpleProduct3.name$$"/> </actionGroup> - <actionGroup ref="removeProductFromMiniCart" stepKey="removeProduct9FromMiniCart"> + <actionGroup ref="RemoveProductFromMiniCartActionGroup" stepKey="removeProduct9FromMiniCart"> <argument name="productName" value="$$simpleProduct2.name$$"/> </actionGroup> - <actionGroup ref="removeProductFromMiniCart" stepKey="removeProduct10FromMiniCart"> + <actionGroup ref="RemoveProductFromMiniCartActionGroup" stepKey="removeProduct10FromMiniCart"> <argument name="productName" value="$$simpleProduct1.name$$"/> </actionGroup> <reloadPage stepKey="reloadPage"/> <!--Check the minicart is empty and verify EmptyCartMessage and AssertProductAbsentInMiniShoppingCart--> - <actionGroup ref="assertMiniCartEmpty" stepKey="miniCartEnpty"/> + <actionGroup ref="AssertMiniCartEmptyActionGroup" stepKey="miniCartEnpty"/> <dontSee selector="{{StorefrontMinicartSection.productLinkByName($$simpleProduct1.name$$)}}" stepKey="verifyAssertProduct1AbsentInMiniShoppingCart"/> <dontSee selector="{{StorefrontMinicartSection.productLinkByName($$simpleProduct2.name$$)}}" stepKey="verifyAssertProduct2AbsentInMiniShoppingCart"/> <dontSee selector="{{StorefrontMinicartSection.productLinkByName($$simpleProduct3.name$$)}}" stepKey="verifyAssertProduct3AbsentInMiniShoppingCart"/> @@ -282,4 +282,4 @@ <dontSee selector="{{StorefrontMinicartSection.productLinkByName($$simpleProduct9.name$$)}}" stepKey="verifyAssertProduct9AbsentInMiniShoppingCart"/> <dontSee selector="{{StorefrontMinicartSection.productLinkByName($$simpleProduct10.name$$)}}" stepKey="verifyAssertProduct10AbsentInMiniShoppingCart"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleAndVirtualProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleAndVirtualProductFromMiniShoppingCartTest.xml index b8092ccdcdce7..b1aa1de71293f 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleAndVirtualProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleAndVirtualProductFromMiniShoppingCartTest.xml @@ -36,13 +36,13 @@ </after> <!-- Add Simple Product to the cart --> - <actionGroup ref="AddSimpleProductToCart" stepKey="addSimpleProductToCart"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addSimpleProductToCart"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> <!-- Add virtual Product to the cart --> <amOnPage url="{{StorefrontProductPage.url($$virtualProduct.name$$)}}" stepKey="amOnStorefrontVirtualProductPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct1ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct1ToTheCart"> <argument name="productName" value="$$virtualProduct.name$$"/> </actionGroup> @@ -64,25 +64,25 @@ </actionGroup> <!-- Select mini Cart and verify Simple and Virtual products names in cart--> - <actionGroup ref="assertOneProductNameInMiniCart" stepKey="seeSimpleProductInMiniCart"> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="seeSimpleProductInMiniCart"> <argument name="productName" value="$$simpleProduct.name$$"/> </actionGroup> - <actionGroup ref="assertOneProductNameInMiniCart" stepKey="seeVirtualProductInMiniCart"> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="seeVirtualProductInMiniCart"> <argument name="productName" value="$$virtualProduct.name$$"/> </actionGroup> <!--Remove Simple and Virtual products from mini cart--> - <actionGroup ref="removeProductFromMiniCart" stepKey="removeProductFromMiniCart"> + <actionGroup ref="RemoveProductFromMiniCartActionGroup" stepKey="removeProductFromMiniCart"> <argument name="productName" value="$$simpleProduct.name$$"/> </actionGroup> - <actionGroup ref="removeProductFromMiniCart" stepKey="removeVirtualProductFromMiniCart"> + <actionGroup ref="RemoveProductFromMiniCartActionGroup" stepKey="removeVirtualProductFromMiniCart"> <argument name="productName" value="$$virtualProduct.name$$"/> </actionGroup> <reloadPage stepKey="reloadPage"/> <!--Check the minicart is empty and verify EmptyCartMessage and AssertProductAbsentInMiniShoppingCart--> - <actionGroup ref="assertMiniCartEmpty" stepKey="miniCartEnpty"/> + <actionGroup ref="AssertMiniCartEmptyActionGroup" stepKey="miniCartEnpty"/> <dontSee selector="{{StorefrontMinicartSection.productLinkByName($$simpleProduct.name$$)}}" stepKey="verifyAssertSimpleProductAbsentInMiniShoppingCart"/> <dontSee selector="{{StorefrontMinicartSection.productLinkByName($$virtualProduct.name$$)}}" stepKey="verifyAssertVirtualProductAbsentInMiniShoppingCart"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleProductFromMiniShoppingCartTest.xml index 05198060f5de4..0108b6310f59d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteSimpleProductFromMiniShoppingCartTest.xml @@ -31,7 +31,7 @@ </after> <!-- Add Simple Product to the cart --> - <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> @@ -46,18 +46,18 @@ </actionGroup> <!-- Select Mini Cart and select 'View And Edit Cart' --> - <actionGroup ref="assertOneProductNameInMiniCart" stepKey="seeProductInMiniCart"> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="seeProductInMiniCart"> <argument name="productName" value="$$simpleProduct.name$$"/> </actionGroup> <!--Remove an item from the cart using minicart--> - <actionGroup ref="removeProductFromMiniCart" stepKey="removeProductFromMiniCart"> + <actionGroup ref="RemoveProductFromMiniCartActionGroup" stepKey="removeProductFromMiniCart"> <argument name="productName" value="$$simpleProduct.name$$"/> </actionGroup> <reloadPage stepKey="reloadPage"/> <!--Check the minicart is empty and verify AssertProductAbsentInMiniShoppingCart--> - <actionGroup ref="assertMiniCartEmpty" stepKey="miniCartEnpty"/> + <actionGroup ref="AssertMiniCartEmptyActionGroup" stepKey="miniCartEnpty"/> <dontSee selector="{{StorefrontMinicartSection.productLinkByName($$simpleProduct.name$$)}}" stepKey="verifyAssertProductAbsentInMiniShoppingCart"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutDataPersistTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutDataPersistTest.xml index 626f095604fa2..a43bbef57d0c8 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutDataPersistTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutDataPersistTest.xml @@ -30,7 +30,7 @@ </after> <!-- Add simple product to cart --> - <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart"> <argument name="product" value="$$createProduct$$"/> </actionGroup> <!-- Navigate to checkout --> @@ -40,7 +40,7 @@ <argument name="shippingMethod" value="Flat Rate"/> </actionGroup> <!-- Add simple product to cart --> - <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart1"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart1"> <argument name="product" value="$$createProduct$$"/> </actionGroup> <!-- Navigate to checkout --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml index b6b9fe1e1a117..a2007da317c00 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml @@ -61,7 +61,7 @@ <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnOrdersPage"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilter"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/> <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="fillOrderNum"/> <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchOrderNum"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnSearch"/> @@ -81,11 +81,8 @@ <title value="Guest Checkout when Cart sidebar disabled"/> <description value="Should be able to place an order as a Guest when Cart sidebar is disabled"/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-97001"/> + <testCaseId value="MC-16587"/> <group value="checkout"/> - <skip> - <issueId value="MC-17140"/> - </skip> </annotations> <before> <magentoCLI stepKey="disableSidebar" command="config:set checkout/sidebar/display 0" /> @@ -113,7 +110,8 @@ </createData> <magentoCLI stepKey="allowSpecificValue" command="config:set payment/checkmo/allowspecific 1" /> <magentoCLI stepKey="specificCountryValue" command="config:set payment/checkmo/specificcountry GB" /> - + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> <actionGroup ref="logout" stepKey="adminLogout"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutUsingFreeShippingAndTaxesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutUsingFreeShippingAndTaxesTest.xml index 2226e1ebc8292..b800ef758d65e 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutUsingFreeShippingAndTaxesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutUsingFreeShippingAndTaxesTest.xml @@ -129,7 +129,7 @@ <see selector="{{AdminTaxRuleGridSection.successMessage}}" userInput="You saved the tax rule." stepKey="assertTaxRuleSuccessMessage" /> <!--Open Product page in StoreFront and assert product and price range --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPageAndVerifyProduct"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> @@ -147,7 +147,7 @@ </actionGroup> <!--Open Product page in StoreFront --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openBundleProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openBundleProduct"> <argument name="product" value="$$createBundleProduct$$"/> </actionGroup> @@ -161,7 +161,7 @@ </actionGroup> <!--Open View and edit --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="clickMiniCart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="clickMiniCart"/> <!-- Fill the Estimate Shipping and Tax section --> <actionGroup ref="CheckoutFillEstimateShippingAndTaxActionGroup" stepKey="fillEstimateShippingAndTaxFields"> @@ -196,7 +196,7 @@ <waitForPageLoad stepKey="waitForPageLoad5"/> <!-- Filter Order using orderId and assert order--> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$orderId"/> </actionGroup> <click selector="{{AdminOrdersGridSection.viewLink('$orderId')}}" stepKey="clickOnViewLink"/> @@ -220,4 +220,4 @@ <argument name="customerGroup" value=""/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutWithCouponAndZeroSubtotalTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutWithCouponAndZeroSubtotalTest.xml index 33ec099aa2ace..97004ea91f5fd 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutWithCouponAndZeroSubtotalTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutWithCouponAndZeroSubtotalTest.xml @@ -31,17 +31,17 @@ </after> <!--Open Product page in StoreFront and assert product and price range --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openVirtualProductPageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openVirtualProductPageAndVerifyProduct"> <argument name="product" value="$$virtualProduct$$"/> </actionGroup> <!-- Add Product to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct1ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct1ToTheCart"> <argument name="productName" value="$$virtualProduct.name$$"/> </actionGroup> <!--Open View and edit --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="clickMiniCart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="clickMiniCart"/> <!-- Fill the Estimate Shipping and Tax section --> <actionGroup ref="CheckoutFillEstimateShippingAndTaxActionGroup" stepKey="fillEstimateShippingAndTaxFields"/> @@ -77,7 +77,7 @@ <waitForPageLoad stepKey="waitForPageLoad5"/> <!-- Filter Order using orderId and assert order--> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$orderId"/> </actionGroup> <click selector="{{AdminOrdersGridSection.viewLink('$orderId')}}" stepKey="clickOnViewLink"/> @@ -90,4 +90,4 @@ <see selector="{{AdminOrderTotalSection.grandTotal}}" userInput="$0.00" stepKey="seeGrandTotal"/> <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeOrderStatus"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontMissingPagerShoppingCartWith20ProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontMissingPagerShoppingCartWith20ProductsTest.xml new file mode 100644 index 0000000000000..32b0985c290a3 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontMissingPagerShoppingCartWith20ProductsTest.xml @@ -0,0 +1,134 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontMissingPagerShoppingCartWith20ProductsTest"> + <annotations> + <features value="Checkout"/> + <stories value="Check if the cart pager is missing with 20 cart items"/> + <title value="Test if the cart pager is missing with 20 cart items."/> + <description value="Test if the cart pager is missing with 20 cart items."/> + <severity value="MAJOR"/> + <testCaseId value="MC-14698"/> + <group value="shoppingCart"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!--Set the default number of items on cart which is 20--> + <magentoCLI stepKey="allowSpecificValue" command="config:set checkout/cart/number_items_to_display_pager 20" /> + <createData entity="SimpleTwo" stepKey="simpleProduct1"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem1"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct2"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem2"> + <argument name="product" value="$$simpleProduct2$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct3"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem3"> + <argument name="product" value="$$simpleProduct3$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct4"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem4"> + <argument name="product" value="$$simpleProduct4$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct5"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem5"> + <argument name="product" value="$$simpleProduct5$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct6"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem6"> + <argument name="product" value="$$simpleProduct6$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct7"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem7"> + <argument name="product" value="$$simpleProduct7$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct8"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem8"> + <argument name="product" value="$$simpleProduct8$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct9"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem9"> + <argument name="product" value="$$simpleProduct9$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct10"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem10"> + <argument name="product" value="$$simpleProduct10$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct11"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem11"> + <argument name="product" value="$$simpleProduct11$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct12"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem12"> + <argument name="product" value="$$simpleProduct12$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct13"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem13"> + <argument name="product" value="$$simpleProduct13$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct14"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem14"> + <argument name="product" value="$$simpleProduct14$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct15"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem15"> + <argument name="product" value="$$simpleProduct15$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct16"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem16"> + <argument name="product" value="$$simpleProduct16$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct17"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem17"> + <argument name="product" value="$$simpleProduct17$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct18"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem18"> + <argument name="product" value="$$simpleProduct18$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct19"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem19"> + <argument name="product" value="$$simpleProduct19$$"/> + </actionGroup> + <createData entity="SimpleTwo" stepKey="simpleProduct20"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="CartItem20"> + <argument name="product" value="$$simpleProduct20$$"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="simpleProduct1" stepKey="deleteCartItem1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteCartItem2"/> + <deleteData createDataKey="simpleProduct3" stepKey="deleteCartItem3"/> + <deleteData createDataKey="simpleProduct4" stepKey="deleteCartItem4"/> + <deleteData createDataKey="simpleProduct5" stepKey="deleteCartItem5"/> + <deleteData createDataKey="simpleProduct6" stepKey="deleteCartItem6"/> + <deleteData createDataKey="simpleProduct7" stepKey="deleteCartItem7"/> + <deleteData createDataKey="simpleProduct8" stepKey="deleteCartItem8"/> + <deleteData createDataKey="simpleProduct9" stepKey="deleteCartItem9"/> + <deleteData createDataKey="simpleProduct10" stepKey="deleteCartItem10"/> + <deleteData createDataKey="simpleProduct11" stepKey="deleteCartItem11"/> + <deleteData createDataKey="simpleProduct12" stepKey="deleteCartItem12"/> + <deleteData createDataKey="simpleProduct13" stepKey="deleteCartItem13"/> + <deleteData createDataKey="simpleProduct14" stepKey="deleteCartItem14"/> + <deleteData createDataKey="simpleProduct15" stepKey="deleteCartItem15"/> + <deleteData createDataKey="simpleProduct16" stepKey="deleteCartItem16"/> + <deleteData createDataKey="simpleProduct17" stepKey="deleteCartItem17"/> + <deleteData createDataKey="simpleProduct18" stepKey="deleteCartItem18"/> + <deleteData createDataKey="simpleProduct19" stepKey="deleteCartItem19"/> + <deleteData createDataKey="simpleProduct20" stepKey="deleteCartItem20"/> + </after> + <!-- Go to the shopping cart and check if the pager is missing--> + <actionGroup ref="StorefrontOpenCartPageActionGroup" stepKey="goToCartPage" /> + <actionGroup ref="AssertPagerTextIsNotVisibleActionGroup" stepKey="VerifyMissingPagerText" > + <argument name="text" value="Items 1 to 20"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontNotApplicableShippingMethodInReviewAndPaymentStepTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontNotApplicableShippingMethodInReviewAndPaymentStepTest.xml new file mode 100644 index 0000000000000..1a427bbe77166 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontNotApplicableShippingMethodInReviewAndPaymentStepTest.xml @@ -0,0 +1,209 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontNotApplicableShippingMethodInReviewAndPaymentStepTest"> + <annotations> + <title value="Not applicable Shipping Method In Review and Payment Step"/> + <stories value="Checkout Shipping Method Recalculation after Coupon Code Added"/> + <description value="User should not be able to place order when free shipping declined after applying coupon code"/> + <features value="Checkout"/> + <severity value="MAJOR"/> + <testCaseId value="MC-22625"/> + <useCaseId value="MC-21926"/> + <group value="Checkout"/> + <skip> + <issueId value="MC-29597"/> + </skip> + </annotations> + + <before> + <!-- Enable Free Shipping Method and set Minimum Order Amount to 100--> + <magentoCLI command="config:set {{AdminFreeshippingActiveConfigData.path}} {{AdminFreeshippingActiveConfigData.enabled}}" stepKey="enableFreeShippingMethod" /> + <magentoCLI command="config:set {{AdminFreeshippingMinimumOrderAmountConfigData.path}} {{AdminFreeshippingMinimumOrderAmountConfigData.hundred}}" stepKey="setFreeShippingMethodMinimumOrderAmountToBe100" /> + + <!--Set Fedex configs data--> + <magentoCLI command="config:set {{AdminFedexEnableForCheckoutConfigData.path}} {{AdminFedexEnableForCheckoutConfigData.value}}" stepKey="enableCheckout"/> + <magentoCLI command="config:set {{AdminFedexEnableSandboxModeConfigData.path}} {{AdminFedexEnableSandboxModeConfigData.value}}" stepKey="enableSandbox"/> + <magentoCLI command="config:set {{AdminFedexEnableDebugConfigData.path}} {{AdminFedexEnableDebugConfigData.value}}" stepKey="enableDebug"/> + <magentoCLI command="config:set {{AdminFedexEnableShowMethodConfigData.path}} {{AdminFedexEnableShowMethodConfigData.value}}" stepKey="enableShowMethod"/> + + <!--Set StoreInformation configs data--> + <magentoCLI command="config:set {{AdminGeneralSetStoreNameConfigData.path}} '{{AdminGeneralSetStoreNameConfigData.value}}'" stepKey="setStoreInformationName"/> + <magentoCLI command="config:set {{AdminGeneralSetStorePhoneConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.telephone}}" stepKey="setStoreInformationPhone"/> + <magentoCLI command="config:set {{AdminGeneralSetCountryConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.country_id}}" stepKey="setStoreInformationCountry"/> + <magentoCLI command="config:set {{AdminGeneralSetCityConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.city}}" stepKey="setStoreInformationCity"/> + <magentoCLI command="config:set {{AdminGeneralSetPostcodeConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.postcode}}" stepKey="setStoreInformationPostcode"/> + <magentoCLI command="config:set {{AdminGeneralSetStreetAddressConfigData.path}} '{{DE_Address_Berlin_Not_Default_Address.street[0]}}'" stepKey="setStoreInformationStreetAddress"/> + <magentoCLI command="config:set {{AdminGeneralSetStreetAddress2ConfigData.path}} '{{US_Address_California.street[0]}}'" stepKey="setStoreInformationStreetAddress2"/> + <magentoCLI command="config:set {{AdminGeneralSetVatNumberConfigData.path}} {{AdminGeneralSetVatNumberConfigData.value}}" stepKey="setStoreInformationVatNumber"/> + + <!--Set Shipping settings origin data--> + <magentoCLI command="config:set {{AdminShippingSettingsOriginCountryConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.country_id}}" stepKey="setOriginCountry"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginCityConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.city}}" stepKey="setOriginCity"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginZipCodeConfigData.path}} {{DE_Address_Berlin_Not_Default_Address.postcode}}" stepKey="setOriginZipCode"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginStreetAddressConfigData.path}} '{{DE_Address_Berlin_Not_Default_Address.street[0]}}'" stepKey="setOriginStreetAddress"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginStreetAddress2ConfigData.path}} '{{US_Address_California.street[0]}}'" stepKey="setOriginStreetAddress2"/> + + <!-- Create Simple Product --> + <createData entity="defaultSimpleProduct" stepKey="createSimpleProduct"> + <field key="price">100</field> + </createData> + <!-- Create Cart Price Rule with 10% discount --> + <createData entity="ApiSalesRule" stepKey="createCartPriceRule"/> + <!-- Create Coupon code for the Cart Price Rule --> + <createData entity="ApiSalesRuleCoupon" stepKey="createCartPriceRuleCoupon"> + <requiredEntity createDataKey="createCartPriceRule"/> + </createData> + <!-- Create Customer with filled Shipping & Billing Address --> + <createData entity="CustomerEntityOne" stepKey="createCustomer"/> + + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + + <after> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutFromStorefront"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCartPriceRule" stepKey="deleteCartPriceRule"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <magentoCLI command="config:set {{AdminFreeshippingMinimumOrderAmountConfigData.path}} {{AdminFreeshippingMinimumOrderAmountConfigData.default}}" stepKey="setFreeShippingMethodMinimumOrderAmountAsDefault" /> + <magentoCLI command="config:set {{AdminFreeshippingActiveConfigData.path}} {{AdminFreeshippingActiveConfigData.disabled}}" stepKey="disableFreeShippingMethod" /> + <!--Reset configs--> + <magentoCLI command="config:set {{AdminFedexDisableForCheckoutConfigData.path}} {{AdminFedexDisableForCheckoutConfigData.value}}" stepKey="disableCheckout"/> + <magentoCLI command="config:set {{AdminFedexDisableSandboxModeConfigData.path}} {{AdminFedexDisableSandboxModeConfigData.value}}" stepKey="disableSandbox"/> + <magentoCLI command="config:set {{AdminFedexDisableDebugConfigData.path}} {{AdminFedexDisableDebugConfigData.value}}" stepKey="disableDebug"/> + <magentoCLI command="config:set {{AdminFedexDisableShowMethodConfigData.path}} {{AdminFedexDisableShowMethodConfigData.value}}" stepKey="disableShowMethod"/> + <magentoCLI command="config:set {{AdminGeneralSetStoreNameConfigData.path}} ''" stepKey="setStoreInformationName"/> + <magentoCLI command="config:set {{AdminGeneralSetStorePhoneConfigData.path}} ''" stepKey="setStoreInformationPhone"/> + <magentoCLI command="config:set {{AdminGeneralSetCityConfigData.path}} ''" stepKey="setStoreInformationCity"/> + <magentoCLI command="config:set {{AdminGeneralSetPostcodeConfigData.path}} ''" stepKey="setStoreInformationPostcode"/> + <magentoCLI command="config:set {{AdminGeneralSetStreetAddressConfigData.path}} ''" stepKey="setStoreInformationStreetAddress"/> + <magentoCLI command="config:set {{AdminGeneralSetStreetAddress2ConfigData.path}} ''" stepKey="setStoreInformationStreetAddress2"/> + <magentoCLI command="config:set {{AdminGeneralSetVatNumberConfigData.path}} ''" stepKey="setStoreInformationVatNumber"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginCityConfigData.path}} ''" stepKey="setOriginCity"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginZipCodeConfigData.path}} ''" stepKey="setOriginZipCode"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginStreetAddressConfigData.path}} ''" stepKey="setOriginStreetAddress"/> + <magentoCLI command="config:set {{AdminShippingSettingsOriginStreetAddress2ConfigData.path}} ''" stepKey="setOriginStreetAddress2"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </after> + + <!-- Guest Customer Test Scenario --> + <!-- Add Simple Product to Cart --> + <actionGroup ref="StorefrontAddSimpleProductToShoppingCartActionGroup" stepKey="addProductToCart"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + + <!-- Go to Checkout --> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckout"/> + + <!-- Fill all required fields --> + <actionGroup ref="GuestCheckoutFillNewShippingAddressActionGroup" stepKey="fillNewShippingAddress"> + <argument name="customer" value="Simple_Customer_Without_Address" /> + <argument name="address" value="US_Address_TX"/> + </actionGroup> + + <!-- Select Free Shipping --> + <actionGroup ref="StorefrontSetShippingMethodActionGroup" stepKey="setShippingMethodFreeShipping"> + <argument name="shippingMethodName" value="Free Shipping"/> + </actionGroup> + + <!-- Go to Order review --> + <actionGroup ref="StorefrontCheckoutForwardFromShippingStepActionGroup" stepKey="goToCheckoutReview"/> + + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> + <!-- Select payment solution --> + <checkOption selector="{{CheckoutPaymentSection.billingAddressNotSameCheckbox}}" stepKey="selectPaymentSolution" /> + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> + + <!-- Apply Discount Coupon to the Order --> + <actionGroup ref="StorefrontApplyDiscountCodeActionGroup" stepKey="applyDiscountCoupon"> + <argument name="discountCode" value="$createCartPriceRuleCoupon.code$"/> + </actionGroup> + + <!-- Assert Shipping total is not yet calculated --> + <actionGroup ref="AssertStorefrontNotCalculatedValueInShippingTotalInOrderSummaryActionGroup" stepKey="assertNotYetCalculated"/> + + <!-- Assert order cannot be placed and error message will shown. --> + <actionGroup ref="AssertStorefrontOrderCannotBePlacedActionGroup" stepKey="assertOrderCannotBePlaced"> + <argument name="error" value="The shipping method is missing. Select the shipping method and try again."/> + </actionGroup> + + <!-- Go to checkout page --> + <actionGroup ref="OpenStoreFrontCheckoutShippingPageActionGroup" stepKey="openCheckoutShippingPage"/> + + <!-- Chose flat rate --> + <actionGroup ref="StorefrontSetShippingMethodActionGroup" stepKey="setShippingMethodFlatRate"> + <argument name="shippingMethodName" value="Flat Rate"/> + </actionGroup> + + <!-- Go to Order review --> + <actionGroup ref="StorefrontCheckoutForwardFromShippingStepActionGroup" stepKey="goToCheckoutReview2"/> + + <!-- Place order assert succeed --> + <actionGroup ref="ClickPlaceOrderActionGroup" stepKey="checkoutPlaceOrder"/> + + <!-- Loged in Customer Test Scenario --> + <!-- Login with created Customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Add Simple Product to Cart --> + <actionGroup ref="StorefrontAddSimpleProductToShoppingCartActionGroup" stepKey="addProductToCart2"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + + <!-- Go to Checkout --> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckout2"/> + + <!-- Select Free Shipping --> + <actionGroup ref="StorefrontSetShippingMethodActionGroup" stepKey="setShippingMethodFreeShipping2"> + <argument name="shippingMethodName" value="Free Shipping"/> + </actionGroup> + + <!-- Go to Order review --> + <actionGroup ref="StorefrontCheckoutForwardFromShippingStepActionGroup" stepKey="goToCheckoutReview3"/> + + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> + + <!-- Select payment solution --> + <checkOption selector="{{CheckoutPaymentSection.billingAddressNotSameCheckbox}}" stepKey="selectPaymentSolution2" /> + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton2"/> + + <!-- Apply Discount Coupon to the Order --> + <actionGroup ref="StorefrontApplyDiscountCodeActionGroup" stepKey="applyDiscountCoupon2"> + <argument name="discountCode" value="$createCartPriceRuleCoupon.code$"/> + </actionGroup> + + <!-- Assert Shipping total is not yet calculated --> + <actionGroup ref="AssertStorefrontNotCalculatedValueInShippingTotalInOrderSummaryActionGroup" stepKey="assertNotYetCalculated2"/> + + <!-- Assert order cannot be placed and error message will shown. --> + <actionGroup ref="AssertStorefrontOrderCannotBePlacedActionGroup" stepKey="assertOrderCannotBePlaced2"> + <argument name="error" value="The shipping method is missing. Select the shipping method and try again."/> + </actionGroup> + + <!-- Go to checkout page --> + <actionGroup ref="OpenStoreFrontCheckoutShippingPageActionGroup" stepKey="openCheckoutShippingPage2"/> + + <!-- Chose flat rate --> + <actionGroup ref="StorefrontSetShippingMethodActionGroup" stepKey="setShippingMethodFlatRate2"> + <argument name="shippingMethodName" value="Flat Rate"/> + </actionGroup> + + <!-- Go to Order review --> + <actionGroup ref="StorefrontCheckoutForwardFromShippingStepActionGroup" stepKey="goToCheckoutReview4"/> + + <!-- Place order assert succeed --> + <actionGroup ref="ClickPlaceOrderActionGroup" stepKey="checkoutPlaceOrder2"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml index 913eb34b34d07..ae7b8d2446380 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml @@ -30,7 +30,7 @@ <!--Add product to cart and checkout--> <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="amOnSimpleProductPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> <argument name="productName" value="$createProduct.name$$"/> </actionGroup> <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutJsValidationTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutJsValidationTest.xml index 8ed8e590eb229..66a4f417aed9d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutJsValidationTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutJsValidationTest.xml @@ -30,7 +30,7 @@ <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> </after> - <actionGroup ref="AddSimpleProductToCart" stepKey="addToCartFromStorefrontProductPage"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addToCartFromStorefrontProductPage"> <argument name="product" value="$$createProduct$$"/> </actionGroup> <actionGroup ref="StorefrontOpenCheckoutPageActionGroup" stepKey="guestGoToCheckout"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontPersistentDataForGuestCustomerWithPhysicalQuoteTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontPersistentDataForGuestCustomerWithPhysicalQuoteTest.xml index 20ff67a076e1e..c106ec9c552ff 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontPersistentDataForGuestCustomerWithPhysicalQuoteTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontPersistentDataForGuestCustomerWithPhysicalQuoteTest.xml @@ -31,7 +31,7 @@ <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShipping"/> </after> <!-- 1. Add simple product to cart and go to checkout--> - <actionGroup ref="AddSimpleProductToCart" stepKey="addSimpleProductToCart"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addSimpleProductToCart"> <argument name="product" value="$$createProduct$$"/> </actionGroup> <!-- 2. Go to Shopping Cart --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml index 5e7e76ae4f02a..b678cb835f503 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductNameMinicartOnCheckoutPageDifferentStoreViewsTest.xml @@ -45,7 +45,7 @@ <waitForPageLoad stepKey="waitForProductPage"/> <!--Switch to second store view and change the product name--> - <actionGroup ref="SwitchToTheNewStoreView" stepKey="switchToCustomStoreView"> + <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="switchToCustomStoreView"> <argument name="storeViewName" value="{{customStore.name}}"/> </actionGroup> <waitForPageLoad stepKey="waitForPageLoad"/> @@ -56,7 +56,7 @@ <!--Add product to cart--> <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="amOnSimpleProductPage"/> <waitForPageLoad stepKey="waitForProductPageLoad"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> <argument name="productName" value="$createProduct.name$$"/> </actionGroup> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductQuantityChangesInBackendAfterCustomerCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductQuantityChangesInBackendAfterCustomerCheckoutTest.xml index 285dc28cb520e..e87aa31576595 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductQuantityChangesInBackendAfterCustomerCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontProductQuantityChangesInBackendAfterCustomerCheckoutTest.xml @@ -32,7 +32,7 @@ </after> <!--Open Product page in StoreFront and assert product and price range --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPageAndVerifyProduct"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> @@ -42,7 +42,7 @@ </actionGroup> <!--Open View and edit --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="clickMiniCart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="clickMiniCart"/> <!-- Fill the Estimate Shipping and Tax section --> <actionGroup ref="CheckoutFillEstimateShippingAndTaxActionGroup" stepKey="fillEstimateShippingAndTaxFields"/> @@ -71,7 +71,7 @@ <waitForPageLoad stepKey="waitForPageLoad5"/> <!-- Filter Order using orderId and assert order--> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$orderId"/> </actionGroup> <click selector="{{AdminOrdersGridSection.viewLink('$orderId')}}" stepKey="clickOnViewLink"/> @@ -88,7 +88,7 @@ <actionGroup ref="AdminAssertOrderAvailableButtonsActionGroup" stepKey="assertOrderButtons"/> <!-- Assert Product Quantity in backend reduced after order processed --> - <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheProduct"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="filterAndSelectTheProduct"> <argument name="productSku" value="$$simpleProduct.sku$$"/> </actionGroup> <waitForElementVisible selector="{{AdminProductFormSection.productName}}" stepKey="waitForProductDetailsToLoad"/> @@ -97,9 +97,9 @@ <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="Out of Stock" stepKey="seeProductStockStatus"/> <!-- Assert Product is Out of Stock in frontend --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="assertProductInStorefront"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="assertProductInStorefront"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> <see selector="{{StorefrontProductInfoMainSection.productStockStatus}}" userInput="Out Of Stock" stepKey="seeProductDisplayedAsOutOfStock"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontRefreshPageDuringGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontRefreshPageDuringGuestCheckoutTest.xml index 1db460de44996..bb74726330b68 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontRefreshPageDuringGuestCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontRefreshPageDuringGuestCheckoutTest.xml @@ -41,7 +41,7 @@ </actionGroup> <!-- Go to Checkout page --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <click selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="clickProceedToCheckout"/> <waitForPageLoad stepKey="waitForProceedToCheckout"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartCheckCustomerDefaultShippingAddressForVirtualQuoteTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartCheckCustomerDefaultShippingAddressForVirtualQuoteTest.xml index b0e1dead1fff9..89e974892427d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartCheckCustomerDefaultShippingAddressForVirtualQuoteTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartCheckCustomerDefaultShippingAddressForVirtualQuoteTest.xml @@ -39,7 +39,7 @@ <argument name="productCount" value="1"/> </actionGroup> <!-- Step 3: Go to Shopping Cart --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingcart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingcart"/> <!-- Step 4: Open Estimate Tax section --> <click selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" stepKey="openEstimateTaxSection"/> <seeOptionIsSelected selector="{{CheckoutCartSummarySection.country}}" userInput="{{US_Address_CA.country}}" stepKey="checkCountry"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml new file mode 100644 index 0000000000000..2691dc2b9fd06 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontShoppingCartPagerForOneItemPerPageAnd2ProductsTest"> + <annotations> + <features value="Checkout"/> + <stories value="Check if the cart pager is visible with 2 cart items and one item per page"/> + <title value="Test if the cart pager is visible with 2 cart items and one item per page."/> + <description value="Test if the cart pager is visible with 2 cart items and one item per page."/> + <severity value="MAJOR"/> + <testCaseId value="MC-14701"/> + <group value="shoppingCart"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Changing the number of items to display in cart--> + <magentoCLI stepKey="allowSpecificValue" command="config:set checkout/cart/number_items_to_display_pager 1" /> + <createData entity="SimpleTwo" stepKey="createSimpleProduct1"/> + <createData entity="SimpleTwo" stepKey="createSimpleProduct2"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addToCartFromStorefrontProductPage1"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addToCartFromStorefrontProductPage2"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + </before> + <after> + <!--Set back the default number of items on cart which is 20--> + <magentoCLI stepKey="allowSpecificValue" command="config:set checkout/cart/number_items_to_display_pager 20" /> + <deleteData createDataKey="createSimpleProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="createSimpleProduct2" stepKey="deleteProduct2"/> + </after> + <actionGroup ref="StorefrontOpenCartPageActionGroup" stepKey="goToCartPage" /> + <actionGroup ref="AssertToolbarTextIsVisibleInCartActionGroup" stepKey="VerifyPagerTextWithChangedConfiguration"> + <argument name="text" value="Items 1 to 1 of 2 total"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml index 482e2fb6233a6..b53954709b2de 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKCustomerCheckoutWithCouponTest.xml @@ -57,17 +57,17 @@ </actionGroup> <!--Open Product page in StoreFront and assert product and price range --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openVirtualProductPageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openVirtualProductPageAndVerifyProduct"> <argument name="product" value="$$virtualProduct$$"/> </actionGroup> <!-- Add Product to the cart --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProduct1ToTheCart"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProduct1ToTheCart"> <argument name="productName" value="$$virtualProduct.name$$"/> </actionGroup> <!--Open View and edit --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="clickMiniCart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="clickMiniCart"/> <!-- Apply Coupon --> <actionGroup ref="StorefrontApplyCouponActionGroup" stepKey="applyDiscount"> @@ -116,7 +116,7 @@ <waitForPageLoad stepKey="waitForPageLoad5"/> <!-- Filter Order using orderId and assert order--> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$orderId"/> </actionGroup> <see selector="{{AdminOrdersGridSection.firstRow}}" userInput="$$createCustomer.fullname$$" stepKey="seeCustomerNameInGrid"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKGuestCheckoutWithConditionProductQuantityEqualsToOrderedQuantityTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKGuestCheckoutWithConditionProductQuantityEqualsToOrderedQuantityTest.xml index deab32aede324..ba096d4a59615 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKGuestCheckoutWithConditionProductQuantityEqualsToOrderedQuantityTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUKGuestCheckoutWithConditionProductQuantityEqualsToOrderedQuantityTest.xml @@ -29,7 +29,7 @@ </after> <!--Open Product page in StoreFront and assert product and price range --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPageAndVerifyProduct"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> @@ -39,7 +39,7 @@ </actionGroup> <!--Open View and edit --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="clickMiniCart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="clickMiniCart"/> <!-- Fill the Estimate Shipping and Tax section --> <actionGroup ref="CheckoutFillEstimateShippingAndTaxActionGroup" stepKey="fillEstimateShippingAndTaxFields"/> @@ -66,7 +66,7 @@ <waitForPageLoad stepKey="waitForPageLoad5"/> <!-- Filter Order using orderId and assert order--> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$orderId"/> </actionGroup> <click selector="{{AdminOrdersGridSection.viewLink('$orderId')}}" stepKey="clickOnViewLink"/> @@ -83,7 +83,7 @@ <actionGroup ref="AdminAssertOrderAvailableButtonsActionGroup" stepKey="assertOrderButtons"/> <!-- Assert Product Quantity in backend reduced after order processed --> - <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheProduct"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="filterAndSelectTheProduct"> <argument name="productSku" value="$$simpleProduct.sku$$"/> </actionGroup> <waitForElementVisible selector="{{AdminProductFormSection.productName}}" stepKey="waitForProductDetailsToLoad"/> @@ -92,9 +92,9 @@ <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="Out of Stock" stepKey="seeProductStockStatus"/> <!-- Assert Product is Out of Stock in frontend --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="assertProductInStorefront"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="assertProductInStorefront"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> <see selector="{{StorefrontProductInfoMainSection.productStockStatus}}" userInput="Out Of Stock" stepKey="seeProductDisplayedAsOutOfStock"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUSCustomerCheckoutWithCouponAndBankTransferPaymentMethodTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUSCustomerCheckoutWithCouponAndBankTransferPaymentMethodTest.xml index 6d5f79f3aadf4..e67fd938c0a74 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUSCustomerCheckoutWithCouponAndBankTransferPaymentMethodTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUSCustomerCheckoutWithCouponAndBankTransferPaymentMethodTest.xml @@ -36,7 +36,7 @@ </after> <!-- Open Product page in StoreFront and assert product and price range --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPageAndVerifyProduct"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> @@ -46,7 +46,7 @@ </actionGroup> <!-- Open View and edit --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="clickMiniCart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="clickMiniCart"/> <!-- Fill the Estimate Shipping and Tax section --> <actionGroup ref="CheckoutFillEstimateShippingAndTaxActionGroup" stepKey="fillEstimateShippingAndTaxFields"/> @@ -78,7 +78,7 @@ <waitForPageLoad stepKey="waitForPageLoad"/> <!-- Filter Order using orderId and assert order--> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$orderId"/> </actionGroup> <click selector="{{AdminOrdersGridSection.viewLink('$orderId')}}" stepKey="clickOnViewLink"/> @@ -94,4 +94,4 @@ <!-- Assert order buttons --> <actionGroup ref="AdminAssertOrderAvailableButtonsActionGroup" stepKey="assertOrderButtons"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml index b4747a6bf7273..4bff22950174f 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml @@ -37,7 +37,7 @@ <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToSimpleProductPage"/> <!--Add Product to Shopping Cart--> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> <argument name="productName" value="$$createSimpleProduct.name$$"/> </actionGroup> @@ -54,7 +54,7 @@ <openNewTab stepKey="openNewTab"/> <amOnPage url="{{AdminProductEditPage.url($$createSimpleProduct.id$$)}}" stepKey="goToProductEditPage"/> <fillField userInput="120" selector="{{AdminProductFormSection.productPrice}}" stepKey="setNewPrice"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <closeTab stepKey="closeTab"/> <!--Check price--> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml index 72f6cf95a6fbe..d0d75317531b7 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleProductQtyTest.xml @@ -26,7 +26,7 @@ </createData> <!-- Add the newly created product to the shopping cart --> - <actionGroup ref="AddSimpleProductToCart" stepKey="addToCartFromStorefrontProductPage"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addToCartFromStorefrontProductPage"> <argument name="product" value="$$createProduct$$"/> </actionGroup> </before> @@ -56,7 +56,7 @@ <see userInput="{{quoteQty3Price123.currency}}{{quoteQty3Price123.subtotal}}" selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="assertCartSubtotal"/> <!-- Minicart product price and subtotal should be updated --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="openMinicart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="openMinicart"/> <grabValueFrom selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" stepKey="grabProductQtyInMinicart"/> <assertEquals stepKey="assertProductQtyInMinicart"> <actualResult type="variable">grabProductQtyInMinicart</actualResult> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml index 7a653f13c4ee5..0b52b08980ded 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdateShoppingCartSimpleWithCustomOptionsProductQtyTest.xml @@ -61,7 +61,7 @@ <see userInput="{{quoteQty11Subtotal1320.currency}}{{quoteQty11Subtotal1320.subtotal}}" selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="assertSubtotal"/> <!-- Minicart product price and subtotal should be updated --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="openMinicart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="openMinicart"/> <grabValueFrom selector="{{StorefrontMinicartSection.itemQuantity($$createProduct.name$$)}}" stepKey="grabProductQtyInMinicart"/> <assertEquals stepKey="assertProductQtyInMinicart"> <expectedResult type="string">{{quoteQty11Subtotal1320.qty}}</expectedResult> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/UpdateProductFromMiniShoppingCartEntityTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/UpdateProductFromMiniShoppingCartEntityTest.xml index 7318f865a0dc1..6e484c30fa81e 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/UpdateProductFromMiniShoppingCartEntityTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/UpdateProductFromMiniShoppingCartEntityTest.xml @@ -24,7 +24,7 @@ <createData entity="simpleProductWithoutCategory" stepKey="createProduct"/> <!--Add product to cart--> - <actionGroup ref="AddSimpleProductToCart" stepKey="addToCartFromStorefrontProductPage"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addToCartFromStorefrontProductPage"> <argument name="product" value="$$createProduct$$"/> </actionGroup> </before> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml index 4b3e18fb31877..0f0b98912de30 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml @@ -49,7 +49,7 @@ <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"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="chooseNotLoggedInCustomerGroup"/> <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> <selectOption selector="{{AdminCartPriceRulesFormSection.coupon}}" userInput="Specific Coupon" stepKey="selectCouponType"/> @@ -63,7 +63,7 @@ <!--Proceed to store front and place an order with free shipping using created coupon--> <!--Add product to card--> - <actionGroup ref="AddSimpleProductToCart" stepKey="AddProductToCard"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="AddProductToCard"> <argument name="product" value="$$simpleproduct$$"/> </actionGroup> @@ -90,7 +90,7 @@ <waitForPageLoad stepKey="waitForSalesOrderPageLoaded"/> <!-- Open Order --> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> diff --git a/app/code/Magento/Checkout/Test/Unit/Block/Cart/ShippingTest.php b/app/code/Magento/Checkout/Test/Unit/Block/Cart/ShippingTest.php index 302188224b97a..5ab4615c52828 100644 --- a/app/code/Magento/Checkout/Test/Unit/Block/Cart/ShippingTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Block/Cart/ShippingTest.php @@ -3,44 +3,79 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Checkout\Test\Unit\Block\Cart; -class ShippingTest extends \PHPUnit\Framework\TestCase +use Magento\Checkout\Block\Cart\Shipping; +use Magento\Checkout\Model\CompositeConfigProvider; +use Magento\Checkout\Block\Checkout\LayoutProcessorInterface; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Serialize\Serializer\JsonHexTag; +use Magento\Framework\View\Element\Template\Context; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Store\Model\Store; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit Test for Magento\Checkout\Block\Cart\Shipping + */ +class ShippingTest extends TestCase { + /** - * @var \Magento\Checkout\Block\Cart\Shipping + * Stub Preinitialized Componets */ - protected $model; + private const STUB_PREINITIALIZED_COMPONENTS = [ + 'components' => [ + 'firstComponent' => ['param' => 'value'] + ] + ]; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * Stub Base URL */ - protected $context; + private const STUB_BASE_URL = 'baseurl'; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Shipping */ - protected $customerSession; + protected $block; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Context|MockObject */ - protected $checkoutSession; + protected $contextMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CustomerSession|MockObject */ - protected $configProvider; + protected $customerSessionMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CheckoutSession|MockObject */ - protected $layoutProcessor; + protected $checkoutSessionMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CompositeConfigProvider|MockObject */ - protected $storeManager; + protected $configProviderMock; + + /** + * @var LayoutProcessorInterface|MockObject + */ + protected $layoutProcessorMock; + + /** + * @var StoreManagerInterface|MockObject + */ + protected $storeManagerInterfaceMock; /** * @var array @@ -48,77 +83,147 @@ class ShippingTest extends \PHPUnit\Framework\TestCase protected $layout; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Json|MockObject */ - private $serializer; + private $serializerMock; - protected function setUp() + /** + * @var JsonHexTag|MockObject + */ + private $jsonHexTagSerializerMock; + + /** + * @inheritDoc + */ + protected function setUp(): void { - $this->context = $this->createMock(\Magento\Framework\View\Element\Template\Context::class); - $this->customerSession = $this->createMock(\Magento\Customer\Model\Session::class); - $this->checkoutSession = $this->createMock(\Magento\Checkout\Model\Session::class); - $this->configProvider = $this->createMock(\Magento\Checkout\Model\CompositeConfigProvider::class); - $this->layoutProcessor = $this->createMock(\Magento\Checkout\Block\Checkout\LayoutProcessorInterface::class); - $this->layout = [ - 'components' => [ - 'firstComponent' => ['param' => 'value'], - 'secondComponent' => ['param' => 'value'], + $this->contextMock = $this->createMock(Context::class); + $this->customerSessionMock = $this->createMock(CustomerSession::class); + $this->checkoutSessionMock = $this->createMock(CheckoutSession::class); + $this->configProviderMock = $this->createMock(CompositeConfigProvider::class); + $this->layoutProcessorMock = $this->createMock(LayoutProcessorInterface::class); + $this->serializerMock = $this->createMock(JsonHexTag::class); + $this->jsonHexTagSerializerMock = $this->createMock(JsonHexTag::class); + $this->storeManagerInterfaceMock = $this->createMock(StoreManagerInterface::class); + $this->layout = self::STUB_PREINITIALIZED_COMPONENTS; + + $objectManager = new ObjectManager($this); + $this->block = $objectManager->getObject( + Shipping::class, + [ + 'configProvider' => $this->configProviderMock, + 'layoutProcessors' => [$this->layoutProcessorMock], + 'jsLayout' => $this->layout, + 'serializer' => $this->serializerMock, + 'jsonHexTagSerializer' => $this->jsonHexTagSerializerMock, + 'storeManager' => $this->storeManagerInterfaceMock ] - ]; - - $this->storeManager = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); - $this->context->expects($this->once())->method('getStoreManager')->willReturn($this->storeManager); - $this->serializer = $this->createMock(\Magento\Framework\Serialize\Serializer\Json::class); - - $this->model = new \Magento\Checkout\Block\Cart\Shipping( - $this->context, - $this->customerSession, - $this->checkoutSession, - $this->configProvider, - [$this->layoutProcessor], - ['jsLayout' => $this->layout], - $this->serializer ); } - public function testGetCheckoutConfig() + /** + * Test for getCheckoutConfig + * + * @return void + */ + public function testGetCheckoutConfig(): void { $config = ['param' => 'value']; - $this->configProvider->expects($this->once())->method('getConfig')->willReturn($config); - $this->assertEquals($config, $this->model->getCheckoutConfig()); + $this->configProviderMock->expects($this->once()) + ->method('getConfig') + ->willReturn($config); + + $this->assertEquals($config, $this->block->getCheckoutConfig()); } - public function testGetJsLayout() + /** + * Test for getJsLayout() + * + * @return void + * @dataProvider getJsLayoutDataProvider + */ + public function testGetJsLayout(array $layoutProcessed, string $jsonLayoutProcessed): void { - $layoutProcessed = $this->layout; - $layoutProcessed['components']['thirdComponent'] = ['param' => 'value']; - $jsonLayoutProcessed = json_encode($layoutProcessed); - - $this->layoutProcessor->expects($this->once()) + $this->layoutProcessorMock->expects($this->once()) ->method('process') ->with($this->layout) ->willReturn($layoutProcessed); - $this->assertEquals( - $jsonLayoutProcessed, - $this->model->getJsLayout() - ); + $this->jsonHexTagSerializerMock->expects($this->once()) + ->method('serialize') + ->willReturn($jsonLayoutProcessed); + + $this->assertEquals($jsonLayoutProcessed, $this->block->getJsLayout()); } - public function testGetBaseUrl() + /** + * Data for getJsLayout() + * + * @return array + */ + public function getJsLayoutDataProvider(): array { - $baseUrl = 'baseUrl'; - $storeMock = $this->createPartialMock(\Magento\Store\Model\Store::class, ['getBaseUrl']); - $storeMock->expects($this->once())->method('getBaseUrl')->willReturn($baseUrl); - $this->storeManager->expects($this->once())->method('getStore')->willReturn($storeMock); - $this->assertEquals($baseUrl, $this->model->getBaseUrl()); + $layoutProcessed = $this->layout; + $layoutProcessed['components']['secondComponent'] = ['param' => 'value']; + return [ + [ + $layoutProcessed, + '{"components":{"firstComponent":{"param":"value"},"secondComponent":{"param":"value"}}}' + ] + ]; } - public function testGetSerializedCheckoutConfig() + /** + * Test for getBaseUrl() + * + * @return void + */ + public function testGetBaseUrl(): void { - $checkoutConfig = ['checkout', 'config']; - $this->configProvider->expects($this->once())->method('getConfig')->willReturn($checkoutConfig); + $baseUrl = self::STUB_BASE_URL; + $storeMock = $this->createPartialMock(Store::class, ['getBaseUrl']); + $storeMock->expects($this->once()) + ->method('getBaseUrl') + ->willReturn($baseUrl); + + $this->storeManagerInterfaceMock->expects($this->once()) + ->method('getStore') + ->willReturn($storeMock); + + $this->assertEquals($baseUrl, $this->block->getBaseUrl()); + } - $this->assertEquals(json_encode($checkoutConfig), $this->model->getSerializedCheckoutConfig()); + /** + * Test for getSerializedCheckoutConfig() + * + * @return void + * @dataProvider jsonEncodeDataProvider + */ + public function testGetSerializedCheckoutConfig(array $checkoutConfig, string $expectedJson): void + { + $this->configProviderMock->expects($this->once()) + ->method('getConfig') + ->willReturn($checkoutConfig); + + $this->jsonHexTagSerializerMock->expects($this->once()) + ->method('serialize') + ->willReturn($expectedJson); + + $this->assertEquals($expectedJson, $this->block->getSerializedCheckoutConfig()); + } + + /** + * Data for getSerializedCheckoutConfig() + * + * @return array + */ + public function jsonEncodeDataProvider(): array + { + return [ + [ + ['checkout', 'config'], + '["checkout","config"]' + ] + ]; } } diff --git a/app/code/Magento/Checkout/Test/Unit/CustomerData/DirectoryDataTest.php b/app/code/Magento/Checkout/Test/Unit/CustomerData/DirectoryDataTest.php new file mode 100644 index 0000000000000..3c0bae31c9c0d --- /dev/null +++ b/app/code/Magento/Checkout/Test/Unit/CustomerData/DirectoryDataTest.php @@ -0,0 +1,96 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Checkout\Test\Unit\CustomerData; + +use Magento\Checkout\CustomerData\DirectoryData; +use Magento\Directory\Helper\Data as HelperData; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Directory\Model\Country; +use PHPUnit\Framework\TestCase; + +class DirectoryDataTest extends TestCase +{ + /** + * @var DirectoryData + */ + private $model; + + /** + * @var HelperData|\PHPUnit_Framework_MockObject_MockObject + */ + private $directoryHelperMock; + + /** + * @var ObjectManagerHelper + */ + private $objectManager; + + /** + * Setup environment for testing + */ + protected function setUp() + { + $this->objectManager = new ObjectManagerHelper($this); + $this->directoryHelperMock = $this->createMock(HelperData::class); + + $this->model = $this->objectManager->getObject( + DirectoryData::class, + [ + 'directoryHelper' => $this->directoryHelperMock + ] + ); + } + + /** + * Test getSectionData() function + */ + public function testGetSectionData() + { + $regions = [ + 'US' => [ + 'TX' => [ + 'code' => 'TX', + 'name' => 'Texas' + ] + ] + ]; + + $testCountryInfo = $this->objectManager->getObject(Country::class); + $testCountryInfo->setData('country_id', 'US'); + $testCountryInfo->setData('iso2_code', 'US'); + $testCountryInfo->setData('iso3_code', 'USA'); + $testCountryInfo->setData('name_default', 'United States of America'); + $testCountryInfo->setData('name_en_US', 'United States of America'); + $countries = ['US' => $testCountryInfo]; + + $this->directoryHelperMock->expects($this->any()) + ->method('getRegionData') + ->willReturn($regions); + + $this->directoryHelperMock->expects($this->any()) + ->method('getCountryCollection') + ->willReturn($countries); + + /* Assert result */ + $this->assertEquals( + [ + 'US' => [ + 'name' => 'United States of America', + 'regions' => [ + 'TX' => [ + 'code' => 'TX', + 'name' => 'Texas' + ] + ] + ] + ], + $this->model->getSectionData() + ); + } +} diff --git a/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php b/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php index 1de0ebce10f51..e3843991a181f 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/GuestPaymentInformationManagementTest.php @@ -7,8 +7,6 @@ namespace Magento\Checkout\Test\Unit\Model; -use Magento\Framework\App\ResourceConnection; -use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Quote\Model\Quote; use Magento\Quote\Model\Quote\Address; use Magento\Quote\Model\QuoteIdMask; @@ -53,11 +51,6 @@ class GuestPaymentInformationManagementTest extends \PHPUnit\Framework\TestCase */ private $loggerMock; - /** - * @var ResourceConnection|\PHPUnit_Framework_MockObject_MockObject - */ - private $resourceConnectionMock; - protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -75,10 +68,6 @@ protected function setUp() ['create'] ); $this->loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); - $this->resourceConnectionMock = $this->getMockBuilder(ResourceConnection::class) - ->disableOriginalConstructor() - ->getMock(); - $this->model = $objectManager->getObject( \Magento\Checkout\Model\GuestPaymentInformationManagement::class, [ @@ -86,8 +75,7 @@ protected function setUp() 'paymentMethodManagement' => $this->paymentMethodManagementMock, 'cartManagement' => $this->cartManagementMock, 'cartRepository' => $this->cartRepositoryMock, - 'quoteIdMaskFactory' => $this->quoteIdMaskFactoryMock, - 'connectionPool' => $this->resourceConnectionMock, + 'quoteIdMaskFactory' => $this->quoteIdMaskFactoryMock ] ); $objectManager->setBackwardCompatibleProperty($this->model, 'logger', $this->loggerMock); @@ -104,26 +92,6 @@ public function testSavePaymentInformationAndPlaceOrder() $billingAddressMock->expects($this->once())->method('setEmail')->with($email)->willReturnSelf(); - $adapterMockForSales = $this->getMockBuilder(AdapterInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $adapterMockForCheckout = $this->getMockBuilder(AdapterInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - - $this->resourceConnectionMock->expects($this->at(0)) - ->method('getConnection') - ->with('sales') - ->willReturn($adapterMockForSales); - $adapterMockForSales->expects($this->once())->method('beginTransaction'); - $adapterMockForSales->expects($this->once())->method('commit'); - - $this->resourceConnectionMock->expects($this->at(1)) - ->method('getConnection') - ->with('checkout') - ->willReturn($adapterMockForCheckout); - $adapterMockForCheckout->expects($this->once())->method('beginTransaction'); - $adapterMockForCheckout->expects($this->once())->method('commit'); $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); $this->cartManagementMock->expects($this->once())->method('placeOrder')->with($cartId)->willReturn($orderId); @@ -146,27 +114,6 @@ public function testSavePaymentInformationAndPlaceOrderException() $this->getMockForAssignBillingAddress($cartId, $billingAddressMock); $billingAddressMock->expects($this->once())->method('setEmail')->with($email)->willReturnSelf(); - $adapterMockForSales = $this->getMockBuilder(AdapterInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $adapterMockForCheckout = $this->getMockBuilder(AdapterInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - - $this->resourceConnectionMock->expects($this->at(0)) - ->method('getConnection') - ->with('sales') - ->willReturn($adapterMockForSales); - $adapterMockForSales->expects($this->once())->method('beginTransaction'); - $adapterMockForSales->expects($this->once())->method('rollback'); - - $this->resourceConnectionMock->expects($this->at(1)) - ->method('getConnection') - ->with('checkout') - ->willReturn($adapterMockForCheckout); - $adapterMockForCheckout->expects($this->once())->method('beginTransaction'); - $adapterMockForCheckout->expects($this->once())->method('rollback'); - $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); $exception = new \Magento\Framework\Exception\CouldNotSaveException(__('DB exception')); $this->cartManagementMock->expects($this->once())->method('placeOrder')->willThrowException($exception); @@ -236,31 +183,9 @@ public function testSavePaymentInformationAndPlaceOrderWithLocalizedException() $billingAddressMock->expects($this->once())->method('setEmail')->with($email)->willReturnSelf(); - $adapterMockForSales = $this->getMockBuilder(AdapterInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $adapterMockForCheckout = $this->getMockBuilder(AdapterInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - - $this->resourceConnectionMock->expects($this->at(0)) - ->method('getConnection') - ->with('sales') - ->willReturn($adapterMockForSales); - $adapterMockForSales->expects($this->once())->method('beginTransaction'); - $adapterMockForSales->expects($this->once())->method('rollback'); - - $this->resourceConnectionMock->expects($this->at(1)) - ->method('getConnection') - ->with('checkout') - ->willReturn($adapterMockForCheckout); - $adapterMockForCheckout->expects($this->once())->method('beginTransaction'); - $adapterMockForCheckout->expects($this->once())->method('rollback'); - $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); $phrase = new \Magento\Framework\Phrase(__('DB exception')); $exception = new \Magento\Framework\Exception\LocalizedException($phrase); - $this->loggerMock->expects($this->never())->method('critical'); $this->cartManagementMock->expects($this->once())->method('placeOrder')->willThrowException($exception); $this->model->savePaymentInformationAndPlaceOrder($cartId, $email, $paymentMock, $billingAddressMock); diff --git a/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php b/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php index df5c255398ebd..ece395e3131f9 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php @@ -157,7 +157,6 @@ public function testSavePaymentInformationAndPlaceOrderWithLocolizedException() $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); $phrase = new \Magento\Framework\Phrase(__('DB exception')); $exception = new \Magento\Framework\Exception\LocalizedException($phrase); - $this->loggerMock->expects($this->never())->method('critical'); $this->cartManagementMock->expects($this->once())->method('placeOrder')->willThrowException($exception); $this->model->savePaymentInformationAndPlaceOrder($cartId, $paymentMock, $billingAddressMock); diff --git a/app/code/Magento/Checkout/Test/Unit/Model/SessionTest.php b/app/code/Magento/Checkout/Test/Unit/Model/SessionTest.php index 26234992e6136..969631901adff 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/SessionTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/SessionTest.php @@ -9,7 +9,8 @@ */ namespace Magento\Checkout\Test\Unit\Model; -use \Magento\Checkout\Model\Session; +use Magento\Checkout\Model\Session; +use Magento\Framework\Exception\NoSuchEntityException; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -374,6 +375,68 @@ public function testGetStepData() $this->assertEquals($stepData['complex']['key'], $session->getStepData('complex', 'key')); } + /** + * Ensure that if quote not exist for customer quote will be null + * + * @return void + */ + public function testGetQuote(): void + { + $storeManager = $this->getMockForAbstractClass(\Magento\Store\Model\StoreManagerInterface::class); + $customerSession = $this->createMock(\Magento\Customer\Model\Session::class); + $quoteRepository = $this->createMock(\Magento\Quote\Api\CartRepositoryInterface::class); + $quoteFactory = $this->createMock(\Magento\Quote\Model\QuoteFactory::class); + $quote = $this->createMock(\Magento\Quote\Model\Quote::class); + $logger = $this->createMock(\Psr\Log\LoggerInterface::class); + $loggerMethods = get_class_methods(\Psr\Log\LoggerInterface::class); + + $quoteFactory->expects($this->once()) + ->method('create') + ->willReturn($quote); + $customerSession->expects($this->exactly(3)) + ->method('isLoggedIn') + ->willReturn(true); + $store = $this->getMockBuilder(\Magento\Store\Model\Store::class) + ->disableOriginalConstructor() + ->setMethods(['getWebsiteId', '__wakeup']) + ->getMock(); + $storeManager->expects($this->any()) + ->method('getStore') + ->will($this->returnValue($store)); + $storage = $this->getMockBuilder(\Magento\Framework\Session\Storage::class) + ->disableOriginalConstructor() + ->setMethods(['setData', 'getData']) + ->getMock(); + $storage->expects($this->at(0)) + ->method('getData') + ->willReturn(1); + $quoteRepository->expects($this->once()) + ->method('getActiveForCustomer') + ->willThrowException(new NoSuchEntityException()); + + foreach ($loggerMethods as $method) { + $logger->expects($this->never())->method($method); + } + + $quote->expects($this->once()) + ->method('setCustomer') + ->with(null); + + $constructArguments = $this->_helper->getConstructArguments( + \Magento\Checkout\Model\Session::class, + [ + 'storeManager' => $storeManager, + 'quoteRepository' => $quoteRepository, + 'customerSession' => $customerSession, + 'storage' => $storage, + 'quoteFactory' => $quoteFactory, + 'logger' => $logger + ] + ); + $this->_session = $this->_helper->getObject(\Magento\Checkout\Model\Session::class, $constructArguments); + $this->_session->getQuote(); + } + public function testSetStepData() { $stepData = [ diff --git a/app/code/Magento/Checkout/Test/Unit/Plugin/ResetQuoteAddressesTest.php b/app/code/Magento/Checkout/Test/Unit/Plugin/ResetQuoteAddressesTest.php new file mode 100644 index 0000000000000..d5d3e3e8b0469 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Unit/Plugin/ResetQuoteAddressesTest.php @@ -0,0 +1,240 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Checkout\Test\Unit\Plugin; + +use Magento\Checkout\Plugin\Model\Quote\ResetQuoteAddresses; +use Magento\Quote\Api\Data\CartExtensionInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Address; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Class ResetQuoteAddressesTest + * + * Test of clearing quote addresses after all items were removed. + */ +class ResetQuoteAddressesTest extends TestCase +{ + /** + * @var int + */ + private const STUB_ADDRESS_ID = 1; + + /** + * @var int + */ + private const STUB_ITEM_ID = 1; + + /** + * @var int + */ + private const STUB_SHIPPING_ASSIGNMENTS = 1; + + /** + * @var array + */ + private const STUB_QUOTE_ITEMS = [1, 2]; + + /** + * @var ResetQuoteAddresses + */ + private $plugin; + + /** + * @var Quote|MockObject + */ + private $quoteMock; + + /** + * @var CartExtensionInterface|MockObject + */ + private $extensionAttributesMock; + + /** + * Set Up + */ + protected function setUp() + { + $this->quoteMock = $this->createPartialMock(Quote::class, [ + 'getAllAddresses', + 'getAllVisibleItems', + 'removeAddress', + 'getExtensionAttributes', + 'isVirtual', + ]); + $this->extensionAttributesMock = $this->getMockBuilder(CartExtensionInterface::class) + ->setMethods( + [ + 'getShippingAssignments', + 'setShippingAssignments' + ] + ) + ->getMockForAbstractClass(); + + $this->plugin = new ResetQuoteAddresses(); + } + + /** + * Test removing the addresses from a non empty quote + */ + public function testRemovingTheAddressesFromNonEmptyQuote() + { + $this->quoteMock->expects($this->any()) + ->method('getAllVisibleItems') + ->will($this->returnValue(static::STUB_QUOTE_ITEMS)); + $this->quoteMock->expects($this->never()) + ->method('getAllAddresses') + ->willReturnSelf(); + + $this->plugin->afterRemoveItem($this->quoteMock, $this->quoteMock, 1); + } + + /** + * Test clearing the addresses from an empty quote with addresses + * + * @dataProvider quoteAddressesDataProvider + * + * @param bool $isVirtualQuote + * @param array $extensionAttributes + */ + public function testClearingAddressesSuccessfullyFromEmptyQuoteWithAddress( + bool $isVirtualQuote, + array $extensionAttributes + ) { + $this->quoteMock->expects($this->any()) + ->method('getAllVisibleItems') + ->will($this->returnValue([])); + + $address = $this->createPartialMock(Address::class, ['getId']); + + $address->expects($this->any()) + ->method('getId') + ->willReturn(static::STUB_ADDRESS_ID); + + $addresses = [$address]; + + $this->quoteMock->expects($this->any()) + ->method('getAllAddresses') + ->will($this->returnValue($addresses)); + + $this->quoteMock->expects($this->exactly(count($addresses))) + ->method('removeAddress') + ->willReturnSelf(); + + $this->quoteMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->extensionAttributesMock); + + $this->quoteMock->expects($this->once()) + ->method('isVirtual') + ->willReturn($isVirtualQuote); + + if (!$isVirtualQuote && $extensionAttributes) { + $this->extensionAttributesMock->expects($this->any()) + ->method('getShippingAssignments') + ->willReturn([static::STUB_SHIPPING_ASSIGNMENTS]); + + $this->extensionAttributesMock->expects($this->once()) + ->method('setShippingAssignments') + ->willReturnSelf(); + } + + $this->plugin->afterRemoveItem($this->quoteMock, $this->quoteMock, static::STUB_ITEM_ID); + } + + /** + * Test clearing the addresses from an empty quote + * + * @dataProvider quoteNoAddressesDataProvider + * + * @param bool $isVirtualQuote + * @param array $extensionAttributes + */ + public function testClearingTheAddressesFromEmptyQuote( + bool $isVirtualQuote, + array $extensionAttributes + ) { + $quoteVisibleItems = []; + $addresses = []; + + $this->quoteMock->expects($this->any()) + ->method('getAllVisibleItems') + ->will($this->returnValue($quoteVisibleItems)); + + $this->quoteMock->expects($this->any()) + ->method('getAllAddresses') + ->willReturn($addresses); + + $this->quoteMock->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->extensionAttributesMock); + + $this->quoteMock->expects($this->once()) + ->method('isVirtual') + ->willReturn($isVirtualQuote); + + if (!$isVirtualQuote && $extensionAttributes) { + $this->extensionAttributesMock->expects($this->any()) + ->method('getShippingAssignments') + ->willReturn($extensionAttributes); + + $this->extensionAttributesMock->expects($this->once()) + ->method('setShippingAssignments') + ->willReturnSelf(); + } + + $this->plugin->afterRemoveItem($this->quoteMock, $this->quoteMock, static::STUB_ITEM_ID); + } + + /** + * Quote without address data provider + * + * @return array + */ + public function quoteNoAddressesDataProvider(): array + { + return [ + 'Test case with virtual quote' => [ + true, + [] + ], + 'Test case with a non virtual quote without extension attributes' => [ + false, + [] + ], + 'Test case with a non virtual quote with shipping assignments' => [ + false, + [1] + ] + ]; + } + + /** + * Quote with address information data provider + * + * @return array + */ + public function quoteAddressesDataProvider(): array + { + return [ + 'Test case with a virtual quote and no shipping assignments' => [ + true, + [] + ], + 'Test case with a virtual quote and with shipping assignments' => [ + true, + [1] + ], + 'Test case with none virtual quote and with shipping assignments' => [ + false, + [1] + ] + ]; + } +} diff --git a/app/code/Magento/Checkout/registration.php b/app/code/Magento/Checkout/registration.php index 741146232ee25..ca98e2eef761f 100644 --- a/app/code/Magento/Checkout/registration.php +++ b/app/code/Magento/Checkout/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Checkout', __DIR__); diff --git a/app/code/Magento/Checkout/view/adminhtml/email/failed_payment.html b/app/code/Magento/Checkout/view/adminhtml/email/failed_payment.html index 03ad7d9e8d848..6d2b27fd0e293 100644 --- a/app/code/Magento/Checkout/view/adminhtml/email/failed_payment.html +++ b/app/code/Magento/Checkout/view/adminhtml/email/failed_payment.html @@ -6,13 +6,13 @@ --> <!--@subject {{trans "Payment Transaction Failed Reminder"}} @--> <!--@vars { -"var billingAddress.format('html')|raw":"Billing Address", +"var billingAddressHtml|raw":"Billing Address", "var checkoutType":"Checkout Type", "var customerEmail":"Customer Email", "var customer":"Customer Name", "var dateAndTime":"Date and Time of Transaction", "var paymentMethod":"Payment Method", -"var shippingAddress.format('html')|raw":"Shipping Address", +"var shippingAddressHtml|raw":"Shipping Address", "var shippingMethod":"Shipping Method", "var items|raw":"Shopping Cart Items", "var total":"Total", @@ -44,11 +44,11 @@ <h1>{{trans "Payment Transaction Failed"}}</h1> </li> <li> <strong>{{trans "Billing Address:"}}</strong><br /> - {{var billingAddress.format('html')|raw}} + {{var billingAddressHtml|raw}} </li> <li> <strong>{{trans "Shipping Address:"}}</strong><br /> - {{var shippingAddress.format('html')|raw}} + {{var shippingAddressHtml|raw}} </li> <li> <strong>{{trans "Shipping Method:"}}</strong><br /> diff --git a/app/code/Magento/Checkout/view/frontend/web/js/action/recollect-shipping-rates.js b/app/code/Magento/Checkout/view/frontend/web/js/action/recollect-shipping-rates.js new file mode 100644 index 0000000000000..7cce025c4eafc --- /dev/null +++ b/app/code/Magento/Checkout/view/frontend/web/js/action/recollect-shipping-rates.js @@ -0,0 +1,26 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** + * @api + */ +define([ + 'Magento_Checkout/js/model/quote', + 'Magento_Checkout/js/action/select-shipping-address', + 'Magento_Checkout/js/model/shipping-rate-registry' +], function (quote, selectShippingAddress, rateRegistry) { + 'use strict'; + + return function () { + var shippingAddress = null; + + if (!quote.isVirtual()) { + shippingAddress = quote.shippingAddress(); + + rateRegistry.set(shippingAddress.getCacheKey(), null); + selectShippingAddress(shippingAddress); + } + }; +}); diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js index 16f84da0aceda..66539ad211859 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/checkout-data-resolver.js @@ -148,7 +148,7 @@ define([ var selectedShippingRate = checkoutData.getSelectedShippingRate(), availableRate = false; - if (ratesData.length === 1) { + if (ratesData.length === 1 && !quote.shippingMethod()) { //set shipping rate if we have only one available shipping rate selectShippingMethodAction(ratesData[0]); @@ -169,7 +169,12 @@ define([ } if (!availableRate && window.checkoutConfig.selectedShippingMethod) { - availableRate = window.checkoutConfig.selectedShippingMethod; + availableRate = _.find(ratesData, function (rate) { + var selectedShippingMethod = window.checkoutConfig.selectedShippingMethod; + + return rate['carrier_code'] == selectedShippingMethod['carrier_code'] && //eslint-disable-line + rate['method_code'] == selectedShippingMethod['method_code']; //eslint-disable-line eqeqeq + }); } //Unset selected shipping method if not available diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/form/element/email.js b/app/code/Magento/Checkout/view/frontend/web/js/view/form/element/email.js index c0de643d3a223..c570bda51a80e 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/form/element/email.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/form/element/email.js @@ -145,12 +145,19 @@ define([ var loginFormSelector = 'form[data-role=email-with-possible-login]', usernameSelector = loginFormSelector + ' input[name=username]', loginForm = $(loginFormSelector), - validator; + validator, + valid; loginForm.validation(); if (focused === false && !!this.email()) { - return !!$(usernameSelector).valid(); + valid = !!$(usernameSelector).valid(); + + if (valid) { + $(usernameSelector).removeAttr('aria-invalid aria-describedby'); + } + + return valid; } validator = loginForm.validate(); @@ -185,6 +192,10 @@ define([ * @returns {Boolean} - initial visibility state. */ resolveInitialPasswordVisibility: function () { + if (checkoutData.getInputFieldEmailValue() !== '' && checkoutData.getCheckedEmailValue() === '') { + return true; + } + if (checkoutData.getInputFieldEmailValue() !== '') { return checkoutData.getInputFieldEmailValue() === checkoutData.getCheckedEmailValue(); } diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-information.js b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-information.js index 2158873842687..73c9c53147c70 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-information.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping-information.js @@ -31,13 +31,17 @@ define([ var shippingMethod = quote.shippingMethod(), shippingMethodTitle = ''; + if (!shippingMethod) { + return ''; + } + + shippingMethodTitle = shippingMethod['carrier_title']; + if (typeof shippingMethod['method_title'] !== 'undefined') { - shippingMethodTitle = ' - ' + shippingMethod['method_title']; + shippingMethodTitle += ' - ' + shippingMethod['method_title']; } - return shippingMethod ? - shippingMethod['carrier_title'] + shippingMethodTitle : - shippingMethod['carrier_title']; + return shippingMethodTitle; }, /** diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js index 1c3f38a37c7f9..fe8d7782e5eae 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js @@ -60,7 +60,10 @@ define([ template: 'Magento_Checkout/shipping', shippingFormTemplate: 'Magento_Checkout/shipping-address/form', shippingMethodListTemplate: 'Magento_Checkout/shipping-address/shipping-method-list', - shippingMethodItemTemplate: 'Magento_Checkout/shipping-address/shipping-method-item' + shippingMethodItemTemplate: 'Magento_Checkout/shipping-address/shipping-method-item', + imports: { + countryOptions: '${ $.parentName }.shippingAddress.shipping-address-fieldset.country_id:indexedOptions' + } }, visible: ko.observable(!quote.isVirtual()), errorValidationMessage: ko.observable(false), @@ -276,9 +279,7 @@ define([ loginFormSelector = 'form[data-role=email-with-possible-login]', emailValidationResult = customer.isLoggedIn(), field, - country = registry.get(this.parentName + '.shippingAddress.shipping-address-fieldset.country_id'), - countryIndexedOptions = country.indexedOptions, - option = countryIndexedOptions[quote.shippingAddress().countryId], + option = _.isObject(this.countryOptions) && this.countryOptions[quote.shippingAddress().countryId], messageContainer = registry.get('checkout.errors').messageContainer; if (!quote.shippingMethod()) { diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/summary/shipping.js b/app/code/Magento/Checkout/view/frontend/web/js/view/summary/shipping.js index 10d49265e3bb9..a0bbc9dd55bff 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/summary/shipping.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/summary/shipping.js @@ -5,9 +5,11 @@ define([ 'jquery', + 'underscore', 'Magento_Checkout/js/view/summary/abstract-total', - 'Magento_Checkout/js/model/quote' -], function ($, Component, quote) { + 'Magento_Checkout/js/model/quote', + 'Magento_SalesRule/js/view/summary/discount' +], function ($, _, Component, quote, discountView) { 'use strict'; return Component.extend({ @@ -21,7 +23,7 @@ define([ * @return {*} */ getShippingMethodTitle: function () { - var shippingMethod = '', + var shippingMethod, shippingMethodTitle = ''; if (!this.isCalculated()) { @@ -29,11 +31,15 @@ define([ } shippingMethod = quote.shippingMethod(); + if (!_.isArray(shippingMethod) && !_.isObject(shippingMethod)) { + return ''; + } + if (typeof shippingMethod['method_title'] !== 'undefined') { shippingMethodTitle = ' - ' + shippingMethod['method_title']; } - return shippingMethod ? + return shippingMethodTitle ? shippingMethod['carrier_title'] + shippingMethodTitle : shippingMethod['carrier_title']; }, @@ -57,6 +63,34 @@ define([ price = this.totals()['shipping_amount']; return this.getFormattedPrice(price); + }, + + /** + * If is set coupon code, but there wasn't displayed discount view. + * + * @return {Boolean} + */ + haveToShowCoupon: function () { + var couponCode = this.totals()['coupon_code']; + + if (typeof couponCode === 'undefined') { + couponCode = false; + } + + return couponCode && !discountView().isDisplayed(); + }, + + /** + * Returns coupon code description. + * + * @return {String} + */ + getCouponDescription: function () { + if (!this.haveToShowCoupon()) { + return ''; + } + + return '(' + this.totals()['coupon_code'] + ')'; } }); }); diff --git a/app/code/Magento/Checkout/view/frontend/web/template/cart/shipping-rates.html b/app/code/Magento/Checkout/view/frontend/web/template/cart/shipping-rates.html index 5d1889519a302..9f0d436056924 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/cart/shipping-rates.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/cart/shipping-rates.html @@ -24,7 +24,8 @@ checked: $parents[1].selectedShippingMethod, attr: { value: carrier_code + '_' + method_code, - id: 's_method_' + carrier_code + '_' + method_code + id: 's_method_' + carrier_code + '_' + method_code, + disabled: false } "/> <label class="label" data-bind="attr: {for: 's_method_' + carrier_code + '_' + method_code}"> diff --git a/app/code/Magento/CheckoutAgreements/Model/AgreementsConfigProvider.php b/app/code/Magento/CheckoutAgreements/Model/AgreementsConfigProvider.php index 1217270d780e1..ff77db60a64e6 100644 --- a/app/code/Magento/CheckoutAgreements/Model/AgreementsConfigProvider.php +++ b/app/code/Magento/CheckoutAgreements/Model/AgreementsConfigProvider.php @@ -102,7 +102,8 @@ protected function getAgreementsConfig() : nl2br($this->escaper->escapeHtml($agreement->getContent())), 'checkboxText' => $this->escaper->escapeHtml($agreement->getCheckboxText()), 'mode' => $agreement->getMode(), - 'agreementId' => $agreement->getAgreementId() + 'agreementId' => $agreement->getAgreementId(), + 'contentHeight' => $agreement->getContentHeight() ]; } diff --git a/app/code/Magento/CheckoutAgreements/Test/Unit/Model/AgreementsConfigProviderTest.php b/app/code/Magento/CheckoutAgreements/Test/Unit/Model/AgreementsConfigProviderTest.php index c8309bacb0a86..6b8477e0b4919 100644 --- a/app/code/Magento/CheckoutAgreements/Test/Unit/Model/AgreementsConfigProviderTest.php +++ b/app/code/Magento/CheckoutAgreements/Test/Unit/Model/AgreementsConfigProviderTest.php @@ -77,6 +77,7 @@ public function testGetConfigIfContentIsHtml() $escapedCheckboxText = 'escaped_checkbox_text'; $mode = \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_AUTO; $agreementId = 100; + $contentHeight = '100px'; $expectedResult = [ 'checkoutAgreements' => [ 'isEnabled' => 1, @@ -86,6 +87,7 @@ public function testGetConfigIfContentIsHtml() 'checkboxText' => $escapedCheckboxText, 'mode' => $mode, 'agreementId' => $agreementId, + 'contentHeight' => $contentHeight ], ], ], @@ -116,6 +118,7 @@ public function testGetConfigIfContentIsHtml() $agreement->expects($this->once())->method('getCheckboxText')->willReturn($checkboxText); $agreement->expects($this->once())->method('getMode')->willReturn($mode); $agreement->expects($this->once())->method('getAgreementId')->willReturn($agreementId); + $agreement->expects($this->once())->method('getContentHeight')->willReturn($contentHeight); $this->assertEquals($expectedResult, $this->model->getConfig()); } @@ -133,6 +136,7 @@ public function testGetConfigIfContentIsNotHtml() $escapedCheckboxText = 'escaped_checkbox_text'; $mode = \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_AUTO; $agreementId = 100; + $contentHeight = '100px'; $expectedResult = [ 'checkoutAgreements' => [ 'isEnabled' => 1, @@ -142,6 +146,7 @@ public function testGetConfigIfContentIsNotHtml() 'checkboxText' => $escapedCheckboxText, 'mode' => $mode, 'agreementId' => $agreementId, + 'contentHeight' => $contentHeight ], ], ], @@ -172,6 +177,7 @@ public function testGetConfigIfContentIsNotHtml() $agreement->expects($this->once())->method('getCheckboxText')->willReturn($checkboxText); $agreement->expects($this->once())->method('getMode')->willReturn($mode); $agreement->expects($this->once())->method('getAgreementId')->willReturn($agreementId); + $agreement->expects($this->once())->method('getContentHeight')->willReturn($contentHeight); $this->assertEquals($expectedResult, $this->model->getConfig()); } diff --git a/app/code/Magento/CheckoutAgreements/registration.php b/app/code/Magento/CheckoutAgreements/registration.php index b4d4ce25d2826..15562341a94ae 100644 --- a/app/code/Magento/CheckoutAgreements/registration.php +++ b/app/code/Magento/CheckoutAgreements/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CheckoutAgreements', __DIR__); diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/web/js/view/checkout-agreements.js b/app/code/Magento/CheckoutAgreements/view/frontend/web/js/view/checkout-agreements.js index 434676fc04116..a189c42918099 100644 --- a/app/code/Magento/CheckoutAgreements/view/frontend/web/js/view/checkout-agreements.js +++ b/app/code/Magento/CheckoutAgreements/view/frontend/web/js/view/checkout-agreements.js @@ -23,6 +23,7 @@ define([ agreements: agreementsConfig.agreements, modalTitle: ko.observable(null), modalContent: ko.observable(null), + contentHeight: ko.observable(null), modalWindow: null, /** @@ -42,6 +43,7 @@ define([ showContent: function (element) { this.modalTitle(element.checkboxText); this.modalContent(element.content); + this.contentHeight(element.contentHeight ? element.contentHeight : 'auto'); agreementsModal.showModal(); }, diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html b/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html index 4b1a68624e547..f1c807fab3d22 100644 --- a/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html +++ b/app/code/Magento/CheckoutAgreements/view/frontend/web/template/checkout/checkout-agreements.html @@ -35,7 +35,7 @@ <!-- /ko --> <!-- /ko --> <div id="checkout-agreements-modal" data-bind="afterRender: initModal" style="display: none"> - <div class="checkout-agreements-item-content" data-bind="html: modalContent"></div> + <div class="checkout-agreements-item-content" data-bind="html: modalContent, style: {height: contentHeight, overflow:'auto' }"></div> </div> </div> </div> diff --git a/app/code/Magento/Cms/Api/BlockRepositoryInterface.php b/app/code/Magento/Cms/Api/BlockRepositoryInterface.php index b713ca91ea852..4b180b5153295 100644 --- a/app/code/Magento/Cms/Api/BlockRepositoryInterface.php +++ b/app/code/Magento/Cms/Api/BlockRepositoryInterface.php @@ -24,7 +24,7 @@ public function save(Data\BlockInterface $block); /** * Retrieve block. * - * @param int $blockId + * @param string $blockId * @return \Magento\Cms\Api\Data\BlockInterface * @throws \Magento\Framework\Exception\LocalizedException */ @@ -51,7 +51,7 @@ public function delete(Data\BlockInterface $block); /** * Delete block by ID. * - * @param int $blockId + * @param string $blockId * @return bool true on success * @throws \Magento\Framework\Exception\NoSuchEntityException * @throws \Magento\Framework\Exception\LocalizedException diff --git a/app/code/Magento/Cms/Api/Data/PageInterface.php b/app/code/Magento/Cms/Api/Data/PageInterface.php index 032f4916a85e4..7a31ab1b9a94f 100644 --- a/app/code/Magento/Cms/Api/Data/PageInterface.php +++ b/app/code/Magento/Cms/Api/Data/PageInterface.php @@ -125,6 +125,7 @@ public function getSortOrder(); * Get layout update xml * * @return string|null + * @deprecated Existing updates are applied, new are not accepted. */ public function getLayoutUpdateXml(); @@ -145,6 +146,8 @@ public function getCustomRootTemplate(); /** * Get custom layout update xml * + * @deprecated Existing updates are applied, new are not accepted. + * @see \Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelectedInterface * @return string|null */ public function getCustomLayoutUpdateXml(); @@ -272,6 +275,7 @@ public function setSortOrder($sortOrder); * * @param string $layoutUpdateXml * @return \Magento\Cms\Api\Data\PageInterface + * @deprecated Existing updates are applied, new are not accepted. */ public function setLayoutUpdateXml($layoutUpdateXml); @@ -296,6 +300,8 @@ public function setCustomRootTemplate($customRootTemplate); * * @param string $customLayoutUpdateXml * @return \Magento\Cms\Api\Data\PageInterface + * @deprecated Existing updates are applied, new are not accepted. + * @see \Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelectedInterface */ public function setCustomLayoutUpdateXml($customLayoutUpdateXml); diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php index 46f68955531a3..f1862026f0e35 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/PostDataProcessor.php @@ -12,7 +12,7 @@ use Magento\Framework\Config\Dom\ValidationSchemaException; /** - * Processes form data + * Controller helper for user input. */ class PostDataProcessor { @@ -140,7 +140,7 @@ private function validateData($data, $layoutXmlValidator) if (!empty($data['layout_update_xml']) && !$layoutXmlValidator->isValid($data['layout_update_xml'])) { return false; } - + if (!empty($data['custom_layout_update_xml']) && !$layoutXmlValidator->isValid($data['custom_layout_update_xml']) ) { diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php index 569f6b256163f..449fdb4224a57 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php @@ -8,11 +8,14 @@ use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Backend\App\Action; use Magento\Cms\Model\Page; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\Request\DataPersistorInterface; use Magento\Framework\Exception\LocalizedException; /** * Save CMS page action. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Save extends \Magento\Backend\App\Action implements HttpPostActionInterface { @@ -59,11 +62,9 @@ public function __construct( ) { $this->dataProcessor = $dataProcessor; $this->dataPersistor = $dataPersistor; - $this->pageFactory = $pageFactory - ?: \Magento\Framework\App\ObjectManager::getInstance()->get(\Magento\Cms\Model\PageFactory::class); + $this->pageFactory = $pageFactory ?: ObjectManager::getInstance()->get(\Magento\Cms\Model\PageFactory::class); $this->pageRepository = $pageRepository - ?: \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Cms\Api\PageRepositoryInterface::class); + ?: ObjectManager::getInstance()->get(\Magento\Cms\Api\PageRepositoryInterface::class); parent::__construct($context); } @@ -100,20 +101,22 @@ public function execute() } } + $data['layout_update_xml'] = $model->getLayoutUpdateXml(); + $data['custom_layout_update_xml'] = $model->getCustomLayoutUpdateXml(); $model->setData($data); - $this->_eventManager->dispatch( - 'cms_page_prepare_save', - ['page' => $model, 'request' => $this->getRequest()] - ); - try { + $this->_eventManager->dispatch( + 'cms_page_prepare_save', + ['page' => $model, 'request' => $this->getRequest()] + ); + $this->pageRepository->save($model); $this->messageManager->addSuccessMessage(__('You saved the page.')); return $this->processResultRedirect($model, $resultRedirect, $data); } catch (LocalizedException $e) { $this->messageManager->addExceptionMessage($e->getPrevious() ?: $e); - } catch (\Exception $e) { + } catch (\Throwable $e) { $this->messageManager->addExceptionMessage($e, __('Something went wrong while saving the page.')); } diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Directive.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Directive.php index b21ea9fd7ef7b..97d0b35a2354f 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Directive.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Directive.php @@ -4,15 +4,28 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Cms\Controller\Adminhtml\Wysiwyg; use Magento\Backend\App\Action; use Magento\Cms\Model\Template\Filter; use Magento\Cms\Model\Wysiwyg\Config; use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\Image\Adapter\AdapterInterface; +use Magento\Framework\Image\AdapterFactory; +use Psr\Log\LoggerInterface; +use Magento\Framework\Url\DecoderInterface; +use Magento\Framework\Controller\Result\Raw; +use Magento\Framework\Controller\Result\RawFactory; +use Magento\Backend\App\Action\Context; +use Magento\Framework\App\ObjectManager; /** * Process template text for wysiwyg editor. + * + * Class Directive */ class Directive extends Action implements HttpGetActionInterface { @@ -25,34 +38,68 @@ class Directive extends Action implements HttpGetActionInterface const ADMIN_RESOURCE = 'Magento_Cms::media_gallery'; /** - * @var \Magento\Framework\Url\DecoderInterface + * @var DecoderInterface */ protected $urlDecoder; /** - * @var \Magento\Framework\Controller\Result\RawFactory + * @var RawFactory */ protected $resultRawFactory; /** - * @param Action\Context $context - * @param \Magento\Framework\Url\DecoderInterface $urlDecoder - * @param \Magento\Framework\Controller\Result\RawFactory $resultRawFactory + * @var LoggerInterface + */ + private $logger; + + /** + * @var AdapterFactory + */ + private $adapterFactory; + + /** + * @var Config + */ + private $config; + + /** + * @var Filter + */ + private $filter; + + /** + * Constructor + * + * @param Context $context + * @param DecoderInterface $urlDecoder + * @param RawFactory $resultRawFactory + * @param AdapterFactory|null $adapterFactory + * @param LoggerInterface|null $logger + * @param Config|null $config + * @param Filter|null $filter */ public function __construct( - Action\Context $context, - \Magento\Framework\Url\DecoderInterface $urlDecoder, - \Magento\Framework\Controller\Result\RawFactory $resultRawFactory + Context $context, + DecoderInterface $urlDecoder, + RawFactory $resultRawFactory, + AdapterFactory $adapterFactory = null, + LoggerInterface $logger = null, + Config $config = null, + Filter $filter = null ) { parent::__construct($context); $this->urlDecoder = $urlDecoder; $this->resultRawFactory = $resultRawFactory; + $this->adapterFactory = $adapterFactory ?: ObjectManager::getInstance()->get(AdapterFactory::class); + $this->logger = $logger ?: ObjectManager::getInstance()->get(LoggerInterface::class); + $this->config = $config ?: ObjectManager::getInstance()->get(Config::class); + $this->filter = $filter ?: ObjectManager::getInstance()->get(Filter::class); } /** * Template directives callback * - * @return \Magento\Framework\Controller\Result\Raw + * @return Raw */ public function execute() { @@ -60,23 +107,25 @@ public function execute() $directive = $this->urlDecoder->decode($directive); try { /** @var Filter $filter */ - $filter = $this->_objectManager->create(Filter::class); - $imagePath = $filter->filter($directive); - /** @var \Magento\Framework\Image\Adapter\AdapterInterface $image */ - $image = $this->_objectManager->get(\Magento\Framework\Image\AdapterFactory::class)->create(); - /** @var \Magento\Framework\Controller\Result\Raw $resultRaw */ + $imagePath = $this->filter->filter($directive); + /** @var AdapterInterface $image */ + $image = $this->adapterFactory->create(); + /** @var Raw $resultRaw */ $resultRaw = $this->resultRawFactory->create(); $image->open($imagePath); $resultRaw->setHeader('Content-Type', $image->getMimeType()); $resultRaw->setContents($image->getImage()); } catch (\Exception $e) { /** @var Config $config */ - $config = $this->_objectManager->get(Config::class); - $imagePath = $config->getSkinImagePlaceholderPath(); - $image->open($imagePath); - $resultRaw->setHeader('Content-Type', $image->getMimeType()); - $resultRaw->setContents($image->getImage()); - $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); + $imagePath = $this->config->getSkinImagePlaceholderPath(); + try { + $image->open($imagePath); + $resultRaw->setHeader('Content-Type', $image->getMimeType()); + $resultRaw->setContents($image->getImage()); + $this->logger->warning($e); + } catch (\Exception $e) { + $this->logger->warning($e); + } } return $resultRaw; } diff --git a/app/code/Magento/Cms/Helper/Page.php b/app/code/Magento/Cms/Helper/Page.php index 70e9437235ac3..39b292bf07239 100644 --- a/app/code/Magento/Cms/Helper/Page.php +++ b/app/code/Magento/Cms/Helper/Page.php @@ -5,7 +5,12 @@ */ namespace Magento\Cms\Helper; +use Magento\Cms\Model\Page\CustomLayoutManagerInterface; +use Magento\Cms\Model\Page\CustomLayoutRepositoryInterface; +use Magento\Cms\Model\Page\IdentityMap; use Magento\Framework\App\Action\Action; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\NoSuchEntityException; /** * CMS Page Helper @@ -76,6 +81,21 @@ class Page extends \Magento\Framework\App\Helper\AbstractHelper */ protected $resultPageFactory; + /** + * @var CustomLayoutManagerInterface + */ + private $customLayoutManager; + + /** + * @var CustomLayoutRepositoryInterface + */ + private $customLayoutRepo; + + /** + * @var IdentityMap + */ + private $identityMap; + /** * Constructor * @@ -88,6 +108,9 @@ class Page extends \Magento\Framework\App\Helper\AbstractHelper * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate * @param \Magento\Framework\Escaper $escaper * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory + * @param CustomLayoutManagerInterface|null $customLayoutManager + * @param CustomLayoutRepositoryInterface|null $customLayoutRepo + * @param IdentityMap|null $identityMap * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -99,7 +122,10 @@ public function __construct( \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, \Magento\Framework\Escaper $escaper, - \Magento\Framework\View\Result\PageFactory $resultPageFactory + \Magento\Framework\View\Result\PageFactory $resultPageFactory, + ?CustomLayoutManagerInterface $customLayoutManager = null, + ?CustomLayoutRepositoryInterface $customLayoutRepo = null, + ?IdentityMap $identityMap = null ) { $this->messageManager = $messageManager; $this->_page = $page; @@ -109,6 +135,11 @@ public function __construct( $this->_localeDate = $localeDate; $this->_escaper = $escaper; $this->resultPageFactory = $resultPageFactory; + $this->customLayoutManager = $customLayoutManager + ?? ObjectManager::getInstance()->get(CustomLayoutManagerInterface::class); + $this->customLayoutRepo = $customLayoutRepo + ?? ObjectManager::getInstance()->get(CustomLayoutRepositoryInterface::class); + $this->identityMap = $identityMap ?? ObjectManager::getInstance()->get(IdentityMap::class); parent::__construct($context); } @@ -136,6 +167,7 @@ public function prepareResultPage(Action $action, $pageId = null) if (!$this->_page->getId()) { return false; } + $this->identityMap->add($this->_page); $inRange = $this->_localeDate->isScopeDateInInterval( null, @@ -152,7 +184,19 @@ public function prepareResultPage(Action $action, $pageId = null) $resultPage = $this->resultPageFactory->create(); $this->setLayoutType($inRange, $resultPage); $resultPage->addHandle('cms_page_view'); - $resultPage->addPageLayoutHandles(['id' => str_replace('/', '_', $this->_page->getIdentifier())]); + $pageHandles = ['id' => str_replace('/', '_', $this->_page->getIdentifier())]; + //Selected custom updates. + try { + $this->customLayoutManager->applyUpdate( + $resultPage, + $this->customLayoutRepo->getFor($this->_page->getId()) + ); + // phpcs:disable Magento2.CodeAnalysis.EmptyBlock.DetectedCatch + } catch (NoSuchEntityException $exception) { + //No custom layout selected + } + + $resultPage->addPageLayoutHandles($pageHandles); $this->_eventManager->dispatch( 'cms_page_render', diff --git a/app/code/Magento/Cms/Model/Page.php b/app/code/Magento/Cms/Model/Page.php index 8eefe26236ba5..28d013f45f1fa 100644 --- a/app/code/Magento/Cms/Model/Page.php +++ b/app/code/Magento/Cms/Model/Page.php @@ -7,7 +7,9 @@ use Magento\Cms\Api\Data\PageInterface; use Magento\Cms\Helper\Page as PageHelper; +use Magento\Cms\Model\Page\CustomLayout\CustomLayoutRepository; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\DataObject\IdentityInterface; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Model\AbstractModel; @@ -57,6 +59,32 @@ class Page extends AbstractModel implements PageInterface, IdentityInterface */ private $scopeConfig; + /** + * @var CustomLayoutRepository + */ + private $customLayoutRepository; + + /** + * @param \Magento\Framework\Model\Context $context + * @param \Magento\Framework\Registry $registry + * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource + * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection + * @param array $data + * @param CustomLayoutRepository|null $customLayoutRepository + */ + public function __construct( + \Magento\Framework\Model\Context $context, + \Magento\Framework\Registry $registry, + \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, + \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, + array $data = [], + ?CustomLayoutRepository $customLayoutRepository = null + ) { + parent::__construct($context, $registry, $resource, $resourceCollection, $data); + $this->customLayoutRepository = $customLayoutRepository + ?? ObjectManager::getInstance()->get(CustomLayoutRepository::class); + } + /** * Initialize resource model * @@ -536,34 +564,56 @@ public function setIsActive($isActive) } /** - * @inheritdoc - * @since 101.0.0 + * Validate identifier before saving the entity. + * + * @return void + * @throws LocalizedException */ - public function beforeSave() + private function validateNewIdentifier(): void { $originalIdentifier = $this->getOrigData('identifier'); $currentIdentifier = $this->getIdentifier(); + if ($this->getId() && $originalIdentifier !== $currentIdentifier) { + switch ($originalIdentifier) { + case $this->getScopeConfig()->getValue(PageHelper::XML_PATH_NO_ROUTE_PAGE): + throw new LocalizedException( + __('This identifier is reserved for "CMS No Route Page" in configuration.') + ); + case $this->getScopeConfig()->getValue(PageHelper::XML_PATH_HOME_PAGE): + throw new LocalizedException( + __('This identifier is reserved for "CMS Home Page" in configuration.') + ); + case $this->getScopeConfig()->getValue(PageHelper::XML_PATH_NO_COOKIES_PAGE): + throw new LocalizedException( + __('This identifier is reserved for "CMS No Cookies Page" in configuration.') + ); + } + } + } + /** + * @inheritdoc + * @since 101.0.0 + */ + public function beforeSave() + { if ($this->hasDataChanges()) { $this->setUpdateTime(null); } - if (!$this->getId() || $originalIdentifier === $currentIdentifier) { - return parent::beforeSave(); - } + $this->validateNewIdentifier(); - switch ($originalIdentifier) { - case $this->getScopeConfig()->getValue(PageHelper::XML_PATH_NO_ROUTE_PAGE): - throw new LocalizedException( - __('This identifier is reserved for "CMS No Route Page" in configuration.') - ); - case $this->getScopeConfig()->getValue(PageHelper::XML_PATH_HOME_PAGE): - throw new LocalizedException(__('This identifier is reserved for "CMS Home Page" in configuration.')); - case $this->getScopeConfig()->getValue(PageHelper::XML_PATH_NO_COOKIES_PAGE): - throw new LocalizedException( - __('This identifier is reserved for "CMS No Cookies Page" in configuration.') - ); + //Removing deprecated custom layout update if a new value is provided + $layoutUpdate = $this->getData('layout_update_selected'); + if ($layoutUpdate === '_no_update_' || ($layoutUpdate && $layoutUpdate !== '_existing_')) { + $this->setCustomLayoutUpdateXml(null); + $this->setLayoutUpdateXml(null); + } + if ($layoutUpdate === '_no_update_' || $layoutUpdate === '_existing_') { + $layoutUpdate = null; } + $this->setData('layout_update_selected', $layoutUpdate); + $this->customLayoutRepository->validateLayoutSelectedFor($this); return parent::beforeSave(); } diff --git a/app/code/Magento/Cms/Model/Page/Authorization.php b/app/code/Magento/Cms/Model/Page/Authorization.php new file mode 100644 index 0000000000000..9075141ce15b5 --- /dev/null +++ b/app/code/Magento/Cms/Model/Page/Authorization.php @@ -0,0 +1,133 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Model\Page; + +use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Api\PageRepositoryInterface; +use Magento\Framework\AuthorizationInterface; +use Magento\Framework\Exception\AuthorizationException; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface; +use \Magento\Store\Model\StoreManagerInterface; + +/** + * Authorization for saving a page. + */ +class Authorization +{ + /** + * @var PageRepositoryInterface + */ + private $pageRepository; + + /** + * @var AuthorizationInterface + */ + private $authorization; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @param PageRepositoryInterface $pageRepository + * @param AuthorizationInterface $authorization + * @param ScopeConfigInterface $scopeConfig + * @param StoreManagerInterface $storeManager + */ + public function __construct( + PageRepositoryInterface $pageRepository, + AuthorizationInterface $authorization, + ScopeConfigInterface $scopeConfig, + StoreManagerInterface $storeManager + ) { + $this->pageRepository = $pageRepository; + $this->authorization = $authorization; + $this->scopeConfig = $scopeConfig; + $this->storeManager = $storeManager; + } + + /** + * Check whether the design fields have been changed. + * + * @param PageInterface $page + * @param PageInterface|null $oldPage + * @return bool + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + private function hasPageChanged(PageInterface $page, ?PageInterface $oldPage): bool + { + if (!$oldPage) { + $oldPageLayout = $this->scopeConfig->getValue( + 'web/default_layouts/default_cms_layout', + ScopeInterface::SCOPE_STORE, + $this->storeManager->getStore() + ); + if ($page->getPageLayout() && $page->getPageLayout() !== $oldPageLayout) { + //If page layout is set and it's not a default value - design attributes are changed. + return true; + } + //Otherwise page layout is empty and is OK to save. + $oldPageLayout = $page->getPageLayout(); + } else { + //Compare page layout to saved value. + $oldPageLayout = $oldPage->getPageLayout(); + } + //Compare new values to saved values or require them to be empty + $oldUpdateXml = $oldPage ? $oldPage->getLayoutUpdateXml() : null; + $oldCustomTheme = $oldPage ? $oldPage->getCustomTheme() : null; + $oldLayoutUpdate = $oldPage ? $oldPage->getCustomLayoutUpdateXml() : null; + $oldThemeFrom = $oldPage ? $oldPage->getCustomThemeFrom() : null; + $oldThemeTo = $oldPage ? $oldPage->getCustomThemeTo() : null; + + if ($page->getLayoutUpdateXml() != $oldUpdateXml + || $page->getPageLayout() != $oldPageLayout + || $page->getCustomTheme() != $oldCustomTheme + || $page->getCustomLayoutUpdateXml() != $oldLayoutUpdate + || $page->getCustomThemeFrom() != $oldThemeFrom + || $page->getCustomThemeTo() != $oldThemeTo + ) { + return true; + } + + return false; + } + + /** + * Authorize user before updating a page. + * + * @param PageInterface $page + * @return void + * @throws AuthorizationException + * @throws \Magento\Framework\Exception\LocalizedException When it is impossible to perform authorization. + */ + public function authorizeFor(PageInterface $page): void + { + //Validate design changes. + if (!$this->authorization->isAllowed('Magento_Cms::save_design')) { + $oldPage = null; + if ($page->getId()) { + $oldPage = $this->pageRepository->getById($page->getId()); + } + if ($this->hasPageChanged($page, $oldPage)) { + throw new AuthorizationException( + __('You are not allowed to change CMS pages design settings') + ); + } + } + } +} diff --git a/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php new file mode 100644 index 0000000000000..988bd5b4ac136 --- /dev/null +++ b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutManager.php @@ -0,0 +1,153 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Model\Page\CustomLayout; + +use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Api\PageRepositoryInterface; +use Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelectedInterface; +use Magento\Cms\Model\Page\CustomLayoutManagerInterface; +use Magento\Cms\Model\Page\IdentityMap; +use Magento\Framework\App\Area; +use Magento\Framework\View\Design\Theme\FlyweightFactory; +use Magento\Framework\View\DesignInterface; +use Magento\Framework\View\Result\Page as PageLayout; +use Magento\Framework\View\Model\Layout\Merge as LayoutProcessor; +use Magento\Framework\View\Model\Layout\MergeFactory as LayoutProcessorFactory; + +/** + * @inheritDoc + */ +class CustomLayoutManager implements CustomLayoutManagerInterface +{ + /** + * @var FlyweightFactory + */ + private $themeFactory; + + /** + * @var DesignInterface + */ + private $design; + + /** + * @var PageRepositoryInterface + */ + private $pageRepository; + + /** + * @var LayoutProcessorFactory + */ + private $layoutProcessorFactory; + + /** + * @var LayoutProcessor|null + */ + private $layoutProcessor; + + /** + * @var IdentityMap + */ + private $identityMap; + + /** + * @param FlyweightFactory $themeFactory + * @param DesignInterface $design + * @param PageRepositoryInterface $pageRepository + * @param LayoutProcessorFactory $layoutProcessorFactory + * @param IdentityMap $identityMap + */ + public function __construct( + FlyweightFactory $themeFactory, + DesignInterface $design, + PageRepositoryInterface $pageRepository, + LayoutProcessorFactory $layoutProcessorFactory, + IdentityMap $identityMap + ) { + $this->themeFactory = $themeFactory; + $this->design = $design; + $this->pageRepository = $pageRepository; + $this->layoutProcessorFactory = $layoutProcessorFactory; + $this->identityMap = $identityMap; + } + + /** + * Adopt page's identifier to be used as layout handle. + * + * @param PageInterface $page + * @return string + */ + private function sanitizeIdentifier(PageInterface $page): string + { + return str_replace('/', '_', $page->getIdentifier()); + } + + /** + * Get the processor instance. + * + * @return LayoutProcessor + */ + private function getLayoutProcessor(): LayoutProcessor + { + if (!$this->layoutProcessor) { + $this->layoutProcessor = $this->layoutProcessorFactory->create( + [ + 'theme' => $this->themeFactory->create( + $this->design->getConfigurationDesignTheme(Area::AREA_FRONTEND) + ) + ] + ); + $this->themeFactory = null; + $this->design = null; + } + + return $this->layoutProcessor; + } + + /** + * @inheritDoc + */ + public function fetchAvailableFiles(PageInterface $page): array + { + $identifier = $this->sanitizeIdentifier($page); + $handles = $this->getLayoutProcessor()->getAvailableHandles(); + + return array_filter( + array_map( + function (string $handle) use ($identifier) : ?string { + preg_match( + '/^cms\_page\_view\_selectable\_' .preg_quote($identifier) .'\_([a-z0-9]+)/i', + $handle, + $selectable + ); + if (!empty($selectable[1])) { + return $selectable[1]; + } + + return null; + }, + $handles + ) + ); + } + + /** + * @inheritDoc + */ + public function applyUpdate(PageLayout $layout, CustomLayoutSelectedInterface $layoutSelected): void + { + $page = $this->identityMap->get($layoutSelected->getPageId()); + if (!$page) { + $page = $this->pageRepository->getById($layoutSelected->getPageId()); + } + + $layout->addPageLayoutHandles( + ['selectable' => $this->sanitizeIdentifier($page) .'_' .$layoutSelected->getLayoutFileId()] + ); + } +} diff --git a/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutRepository.php b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutRepository.php new file mode 100644 index 0000000000000..cf0db6eb27cb8 --- /dev/null +++ b/app/code/Magento/Cms/Model/Page/CustomLayout/CustomLayoutRepository.php @@ -0,0 +1,166 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Model\Page\CustomLayout; + +use Magento\Cms\Model\Page as PageModel; +use Magento\Cms\Model\PageFactory as PageModelFactory; +use Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelectedInterface; +use Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelected; +use Magento\Cms\Model\Page\CustomLayoutRepositoryInterface; +use Magento\Cms\Model\Page\IdentityMap; +use Magento\Cms\Model\ResourceModel\Page; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Cms\Model\Page\CustomLayoutManagerInterface; + +/** + * @inheritDoc + */ +class CustomLayoutRepository implements CustomLayoutRepositoryInterface +{ + /** + * @var Page + */ + private $pageRepository; + + /** + * @var PageModelFactory; + */ + private $pageFactory; + + /** + * @var IdentityMap + */ + private $identityMap; + + /** + * @var CustomLayoutManagerInterface + */ + private $manager; + + /** + * @param Page $pageRepository + * @param PageModelFactory $factory + * @param IdentityMap $identityMap + * @param CustomLayoutManagerInterface $manager + */ + public function __construct( + Page $pageRepository, + PageModelFactory $factory, + IdentityMap $identityMap, + CustomLayoutManagerInterface $manager + ) { + $this->pageRepository = $pageRepository; + $this->pageFactory = $factory; + $this->identityMap = $identityMap; + $this->manager = $manager; + } + + /** + * Find page model by ID. + * + * @param int $id + * @return PageModel + * @throws NoSuchEntityException + */ + private function findPage(int $id): PageModel + { + if (!$page = $this->identityMap->get($id)) { + /** @var PageModel $page */ + $this->pageRepository->load($page = $this->pageFactory->create(), $id); + if (!$page->getIdentifier()) { + throw NoSuchEntityException::singleField('id', $id); + } + } + + return $page; + } + + /** + * Check whether the page can use this layout. + * + * @param PageModel $page + * @param string $layoutFile + * @return bool + */ + private function isLayoutValidFor(PageModel $page, string $layoutFile): bool + { + return in_array($layoutFile, $this->manager->fetchAvailableFiles($page), true); + } + + /** + * Save new custom layout file value for a page. + * + * @param int $pageId + * @param string|null $layoutFile + * @throws LocalizedException + * @throws \InvalidArgumentException When invalid file was selected. + * @throws NoSuchEntityException + */ + private function saveLayout(int $pageId, ?string $layoutFile): void + { + $page = $this->findPage($pageId); + if ($layoutFile !== null && !$this->isLayoutValidFor($page, $layoutFile)) { + throw new \InvalidArgumentException( + $layoutFile .' is not available for page #' .$pageId + ); + } + + if ($page->getData('layout_update_selected') != $layoutFile) { + $page->setData('layout_update_selected', $layoutFile); + $this->pageRepository->save($page); + } + } + + /** + * @inheritDoc + */ + public function save(CustomLayoutSelectedInterface $layout): void + { + $this->saveLayout($layout->getPageId(), $layout->getLayoutFileId()); + } + + /** + * Validate layout update of given page model. + * + * @param PageModel $page + * @return void + * @throws LocalizedException + */ + public function validateLayoutSelectedFor(PageModel $page): void + { + $layoutFile = $page->getData('layout_update_selected'); + if ($layoutFile && (!$page->getId() || !$this->isLayoutValidFor($page, $layoutFile))) { + throw new LocalizedException(__('Invalid Custom Layout Update selected')); + } + } + + /** + * @inheritDoc + */ + public function deleteFor(int $pageId): void + { + $this->saveLayout($pageId, null); + } + + /** + * @inheritDoc + */ + public function getFor(int $pageId): CustomLayoutSelectedInterface + { + $page = $this->findPage($pageId); + if (!$page['layout_update_selected']) { + throw new NoSuchEntityException( + __('Page "%id" doesn\'t have custom layout assigned', ['id' => $page->getIdentifier()]) + ); + } + + return new CustomLayoutSelected($pageId, $page['layout_update_selected']); + } +} diff --git a/app/code/Magento/Cms/Model/Page/CustomLayout/Data/CustomLayoutSelected.php b/app/code/Magento/Cms/Model/Page/CustomLayout/Data/CustomLayoutSelected.php new file mode 100644 index 0000000000000..fc29ebd72e802 --- /dev/null +++ b/app/code/Magento/Cms/Model/Page/CustomLayout/Data/CustomLayoutSelected.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Model\Page\CustomLayout\Data; + +/** + * @inheritDoc + */ +class CustomLayoutSelected implements CustomLayoutSelectedInterface +{ + /** + * @var int + */ + private $pageId; + + /** + * @var string + */ + private $layoutFile; + + /** + * @param int $pageId + * @param string $layoutFile + */ + public function __construct(int $pageId, string $layoutFile) + { + $this->pageId = $pageId; + $this->layoutFile = $layoutFile; + } + + /** + * @inheritDoc + */ + public function getPageId(): int + { + return $this->pageId; + } + + /** + * @inheritDoc + */ + public function getLayoutFileId(): string + { + return $this->layoutFile; + } +} diff --git a/app/code/Magento/Cms/Model/Page/CustomLayout/Data/CustomLayoutSelectedInterface.php b/app/code/Magento/Cms/Model/Page/CustomLayout/Data/CustomLayoutSelectedInterface.php new file mode 100644 index 0000000000000..68bac57e98d56 --- /dev/null +++ b/app/code/Magento/Cms/Model/Page/CustomLayout/Data/CustomLayoutSelectedInterface.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Model\Page\CustomLayout\Data; + +/** + * Custom layout update file to be used for the specific CMS page. + */ +interface CustomLayoutSelectedInterface +{ + /** + * CMS page ID. + * + * @return int + */ + public function getPageId(): int; + + /** + * Custom layout file ID (layout update handle value). + * + * @return string + */ + public function getLayoutFileId(): string; +} diff --git a/app/code/Magento/Cms/Model/Page/CustomLayoutManagerInterface.php b/app/code/Magento/Cms/Model/Page/CustomLayoutManagerInterface.php new file mode 100644 index 0000000000000..6f15fcef7f8f4 --- /dev/null +++ b/app/code/Magento/Cms/Model/Page/CustomLayoutManagerInterface.php @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Model\Page; + +use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelectedInterface; +use Magento\Framework\View\Result\Page as View; + +/** + * Manage custom layout files for CMS pages. + */ +interface CustomLayoutManagerInterface +{ + /** + * List of available custom files for the given page. + * + * @param PageInterface $page + * @return string[] + */ + public function fetchAvailableFiles(PageInterface $page): array; + + /** + * Apply the page's layout settings. + * + * @param View $layout + * @param CustomLayoutSelectedInterface $layoutSelected + * @return void + */ + public function applyUpdate(View $layout, CustomLayoutSelectedInterface $layoutSelected): void; +} diff --git a/app/code/Magento/Cms/Model/Page/CustomLayoutRepositoryInterface.php b/app/code/Magento/Cms/Model/Page/CustomLayoutRepositoryInterface.php new file mode 100644 index 0000000000000..80eb39b7ab20f --- /dev/null +++ b/app/code/Magento/Cms/Model/Page/CustomLayoutRepositoryInterface.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Model\Page; + +use Magento\Cms\Model\Page\CustomLayout\Data\CustomLayoutSelectedInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; + +/** + * Access to "custom layout" page property. + */ +interface CustomLayoutRepositoryInterface +{ + + /** + * Save layout file to be used when rendering given page. + * + * @throws LocalizedException When failed to save new value. + * @throws \InvalidArgumentException When invalid file was selected. + * @throws NoSuchEntityException When given page is not found. + * @param CustomLayoutSelectedInterface $layout + * @return void + */ + public function save(CustomLayoutSelectedInterface $layout): void; + + /** + * Do not use custom layout update when rendering the page. + * + * @throws NoSuchEntityException When given page is not found. + * @throws LocalizedException When failed to remove existing value. + * @param int $pageId + * @return void + */ + public function deleteFor(int $pageId): void; + + /** + * Find custom layout settings for a page. + * + * @param int $pageId + * @return CustomLayoutSelectedInterface + * @throws NoSuchEntityException When either the page or any settings are found. + */ + public function getFor(int $pageId): CustomLayoutSelectedInterface; +} diff --git a/app/code/Magento/Cms/Model/Page/DataProvider.php b/app/code/Magento/Cms/Model/Page/DataProvider.php index 64abaffd04e66..41010575a1f27 100644 --- a/app/code/Magento/Cms/Model/Page/DataProvider.php +++ b/app/code/Magento/Cms/Model/Page/DataProvider.php @@ -5,9 +5,11 @@ */ namespace Magento\Cms\Model\Page; +use Magento\Cms\Model\Page; use Magento\Cms\Model\ResourceModel\Page\CollectionFactory; use Magento\Framework\App\ObjectManager; use Magento\Framework\App\Request\DataPersistorInterface; +use Magento\Framework\App\RequestInterface; use Magento\Ui\DataProvider\Modifier\PoolInterface; use Magento\Framework\AuthorizationInterface; @@ -36,6 +38,21 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider */ private $auth; + /** + * @var RequestInterface + */ + private $request; + + /** + * @var CustomLayoutManagerInterface + */ + private $customLayoutManager; + + /** + * @var CollectionFactory + */ + private $collectionFactory; + /** * @param string $name * @param string $primaryFieldName @@ -46,6 +63,9 @@ class DataProvider extends \Magento\Ui\DataProvider\ModifierPoolDataProvider * @param array $data * @param PoolInterface|null $pool * @param AuthorizationInterface|null $auth + * @param RequestInterface|null $request + * @param CustomLayoutManagerInterface|null $customLayoutManager + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( $name, @@ -56,13 +76,35 @@ public function __construct( array $meta = [], array $data = [], PoolInterface $pool = null, - ?AuthorizationInterface $auth = null + ?AuthorizationInterface $auth = null, + ?RequestInterface $request = null, + ?CustomLayoutManagerInterface $customLayoutManager = null ) { $this->collection = $pageCollectionFactory->create(); + $this->collectionFactory = $pageCollectionFactory; $this->dataPersistor = $dataPersistor; parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data, $pool); $this->auth = $auth ?? ObjectManager::getInstance()->get(AuthorizationInterface::class); $this->meta = $this->prepareMeta($this->meta); + $this->request = $request ?? ObjectManager::getInstance()->get(RequestInterface::class); + $this->customLayoutManager = $customLayoutManager + ?? ObjectManager::getInstance()->get(CustomLayoutManagerInterface::class); + } + + /** + * Find requested page. + * + * @return Page|null + */ + private function findCurrentPage(): ?Page + { + if ($this->getRequestFieldName() && ($pageId = (int)$this->request->getParam($this->getRequestFieldName()))) { + //Loading data for the collection. + $this->getData(); + return $this->collection->getItemById($pageId); + } + + return null; } /** @@ -86,10 +128,15 @@ public function getData() if (isset($this->loadedData)) { return $this->loadedData; } + $this->collection = $this->collectionFactory->create(); $items = $this->collection->getItems(); /** @var $page \Magento\Cms\Model\Page */ foreach ($items as $page) { $this->loadedData[$page->getId()] = $page->getData(); + if ($page->getCustomLayoutUpdateXml() || $page->getLayoutUpdateXml()) { + //Deprecated layout update exists. + $this->loadedData[$page->getId()]['layout_update_selected'] = '_existing_'; + } } $data = $this->dataPersistor->get('cms_page'); @@ -97,6 +144,9 @@ public function getData() $page = $this->collection->getNewEmptyItem(); $page->setData($data); $this->loadedData[$page->getId()] = $page->getData(); + if ($page->getCustomLayoutUpdateXml() || $page->getLayoutUpdateXml()) { + $this->loadedData[$page->getId()]['layout_update_selected'] = '_existing_'; + } $this->dataPersistor->clear('cms_page'); } @@ -134,6 +184,31 @@ public function getMeta() $meta = array_merge_recursive($meta, $designMeta); } + //List of custom layout files available for current page. + $options = [['label' => 'No update', 'value' => '_no_update_']]; + if ($page = $this->findCurrentPage()) { + //We must have a specific page selected. + //If custom layout XML is set then displaying this special option. + if ($page->getCustomLayoutUpdateXml() || $page->getLayoutUpdateXml()) { + $options[] = ['label' => 'Use existing layout update XML', 'value' => '_existing_']; + } + foreach ($this->customLayoutManager->fetchAvailableFiles($page) as $layoutFile) { + $options[] = ['label' => $layoutFile, 'value' => $layoutFile]; + } + } + $customLayoutMeta = [ + 'design' => [ + 'children' => [ + 'custom_layout_update_select' => [ + 'arguments' => [ + 'data' => ['options' => $options] + ] + ] + ] + ] + ]; + $meta = array_merge_recursive($meta, $customLayoutMeta); + return $meta; } } diff --git a/app/code/Magento/Cms/Model/Page/IdentityMap.php b/app/code/Magento/Cms/Model/Page/IdentityMap.php new file mode 100644 index 0000000000000..249010fbf90ce --- /dev/null +++ b/app/code/Magento/Cms/Model/Page/IdentityMap.php @@ -0,0 +1,72 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Cms\Model\Page; + +use Magento\Cms\Model\Page; + +/** + * Identity map of loaded pages. + */ +class IdentityMap +{ + /** + * @var Page[] + */ + private $pages = []; + + /** + * Add a page to the list. + * + * @param Page $page + * @throws \InvalidArgumentException When page doesn't have an ID. + * @return void + */ + public function add(Page $page): void + { + if (!$page->getId()) { + throw new \InvalidArgumentException('Cannot add non-persisted page to identity map'); + } + $this->pages[$page->getId()] = $page; + } + + /** + * Find a loaded page by ID. + * + * @param int $id + * @return Page|null + */ + public function get(int $id): ?Page + { + if (array_key_exists($id, $this->pages)) { + return $this->pages[$id]; + } + + return null; + } + + /** + * Remove the page from the list. + * + * @param int $id + * @return void + */ + public function remove(int $id): void + { + unset($this->pages[$id]); + } + + /** + * Clear the list. + * + * @return void + */ + public function clear(): void + { + $this->pages = []; + } +} diff --git a/app/code/Magento/Cms/Model/PageRepository.php b/app/code/Magento/Cms/Model/PageRepository.php index e6777659d7d88..72f07771f59d4 100644 --- a/app/code/Magento/Cms/Model/PageRepository.php +++ b/app/code/Magento/Cms/Model/PageRepository.php @@ -8,6 +8,7 @@ use Magento\Cms\Api\Data; use Magento\Cms\Api\PageRepositoryInterface; +use Magento\Cms\Model\Page\IdentityMap; use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; use Magento\Framework\App\ObjectManager; @@ -18,8 +19,6 @@ use Magento\Cms\Model\ResourceModel\Page as ResourcePage; use Magento\Cms\Model\ResourceModel\Page\CollectionFactory as PageCollectionFactory; use Magento\Store\Model\StoreManagerInterface; -use Magento\Framework\AuthorizationInterface; -use Magento\Authorization\Model\UserContextInterface; /** * Class PageRepository @@ -73,14 +72,9 @@ class PageRepository implements PageRepositoryInterface private $collectionProcessor; /** - * @var UserContextInterface + * @var IdentityMap */ - private $userContext; - - /** - * @var AuthorizationInterface - */ - private $authorization; + private $identityMap; /** * @param ResourcePage $resource @@ -92,6 +86,7 @@ class PageRepository implements PageRepositoryInterface * @param DataObjectProcessor $dataObjectProcessor * @param StoreManagerInterface $storeManager * @param CollectionProcessorInterface $collectionProcessor + * @param IdentityMap|null $identityMap * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -103,7 +98,8 @@ public function __construct( DataObjectHelper $dataObjectHelper, DataObjectProcessor $dataObjectProcessor, StoreManagerInterface $storeManager, - CollectionProcessorInterface $collectionProcessor = null + CollectionProcessorInterface $collectionProcessor = null, + ?IdentityMap $identityMap = null ) { $this->resource = $resource; $this->pageFactory = $pageFactory; @@ -114,34 +110,31 @@ public function __construct( $this->dataObjectProcessor = $dataObjectProcessor; $this->storeManager = $storeManager; $this->collectionProcessor = $collectionProcessor ?: $this->getCollectionProcessor(); + $this->identityMap = $identityMap ?? ObjectManager::getInstance()->get(IdentityMap::class); } /** - * Get user context. + * Validate new layout update values. * - * @return UserContextInterface + * @param Data\PageInterface $page + * @return void + * @throws \InvalidArgumentException */ - private function getUserContext(): UserContextInterface + private function validateLayoutUpdate(Data\PageInterface $page): void { - if (!$this->userContext) { - $this->userContext = ObjectManager::getInstance()->get(UserContextInterface::class); + //Persisted data + $savedPage = $page->getId() ? $this->getById($page->getId()) : null; + //Custom layout update can be removed or kept as is. + if ($page->getCustomLayoutUpdateXml() + && (!$savedPage || $page->getCustomLayoutUpdateXml() !== $savedPage->getCustomLayoutUpdateXml()) + ) { + throw new \InvalidArgumentException('Custom layout updates must be selected from a file'); } - - return $this->userContext; - } - - /** - * Get authorization service. - * - * @return AuthorizationInterface - */ - private function getAuthorization(): AuthorizationInterface - { - if (!$this->authorization) { - $this->authorization = ObjectManager::getInstance()->get(AuthorizationInterface::class); + if ($page->getLayoutUpdateXml() + && (!$savedPage || $page->getLayoutUpdateXml() !== $savedPage->getLayoutUpdateXml()) + ) { + throw new \InvalidArgumentException('Custom layout updates must be selected from a file'); } - - return $this->authorization; } /** @@ -158,33 +151,9 @@ public function save(\Magento\Cms\Api\Data\PageInterface $page) $page->setStoreId($storeId); } try { - //Validate changing of design. - $userType = $this->getUserContext()->getUserType(); - if (( - $userType === UserContextInterface::USER_TYPE_ADMIN - || $userType === UserContextInterface::USER_TYPE_INTEGRATION - ) - && !$this->getAuthorization()->isAllowed('Magento_Cms::save_design') - ) { - if (!$page->getId()) { - $page->setLayoutUpdateXml(null); - $page->setPageLayout(null); - $page->setCustomTheme(null); - $page->setCustomLayoutUpdateXml(null); - $page->setCustomThemeTo(null); - $page->setCustomThemeFrom(null); - } else { - $savedPage = $this->getById($page->getId()); - $page->setLayoutUpdateXml($savedPage->getLayoutUpdateXml()); - $page->setPageLayout($savedPage->getPageLayout()); - $page->setCustomTheme($savedPage->getCustomTheme()); - $page->setCustomLayoutUpdateXml($savedPage->getCustomLayoutUpdateXml()); - $page->setCustomThemeTo($savedPage->getCustomThemeTo()); - $page->setCustomThemeFrom($savedPage->getCustomThemeFrom()); - } - } - + $this->validateLayoutUpdate($page); $this->resource->save($page); + $this->identityMap->add($page); } catch (\Exception $exception) { throw new CouldNotSaveException( __('Could not save the page: %1', $exception->getMessage()), @@ -208,6 +177,8 @@ public function getById($pageId) if (!$page->getId()) { throw new NoSuchEntityException(__('The CMS page with the "%1" ID doesn\'t exist.', $pageId)); } + $this->identityMap->add($page); + return $page; } @@ -245,6 +216,7 @@ public function delete(\Magento\Cms\Api\Data\PageInterface $page) { try { $this->resource->delete($page); + $this->identityMap->remove($page->getId()); } catch (\Exception $exception) { throw new CouldNotDeleteException( __('Could not delete the page: %1', $exception->getMessage()) @@ -276,7 +248,7 @@ private function getCollectionProcessor() { if (!$this->collectionProcessor) { $this->collectionProcessor = \Magento\Framework\App\ObjectManager::getInstance()->get( - 'Magento\Cms\Model\Api\SearchCriteria\PageCollectionProcessor' + \Magento\Cms\Model\Api\SearchCriteria\PageCollectionProcessor::class ); } return $this->collectionProcessor; diff --git a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php index e02d2b461a94e..21f3b232e783c 100644 --- a/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php +++ b/app/code/Magento/Cms/Model/Wysiwyg/Images/Storage.php @@ -416,7 +416,7 @@ public function createDirectory($name, $path) { if (!preg_match(self::DIRECTORY_NAME_REGEXP, $name)) { throw new \Magento\Framework\Exception\LocalizedException( - __('Please rename the folder using only letters, numbers, underscores and dashes.') + __('Please rename the folder using only Latin letters, numbers, underscores and dashes.') ); } diff --git a/app/code/Magento/Cms/Observer/PageAclPlugin.php b/app/code/Magento/Cms/Observer/PageAclPlugin.php new file mode 100644 index 0000000000000..c71fe0af396c0 --- /dev/null +++ b/app/code/Magento/Cms/Observer/PageAclPlugin.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Observer; + +use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Api\PageRepositoryInterface; +use Magento\Cms\Model\Page\Authorization; + +/** + * Perform additional authorization before saving a page. + */ +class PageAclPlugin +{ + /** + * @var Authorization + */ + private $authorization; + + /** + * @param Authorization $authorization + */ + public function __construct(Authorization $authorization) + { + $this->authorization = $authorization; + } + + /** + * Authorize saving before it is executed. + * + * @param PageRepositoryInterface $subject + * @param PageInterface $page + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeSave(PageRepositoryInterface $subject, PageInterface $page): array + { + $this->authorization->authorizeFor($page); + + return [$page]; + } +} diff --git a/app/code/Magento/Cms/Observer/PageValidatorObserver.php b/app/code/Magento/Cms/Observer/PageValidatorObserver.php new file mode 100644 index 0000000000000..b4e5d2bc0e0a7 --- /dev/null +++ b/app/code/Magento/Cms/Observer/PageValidatorObserver.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cms\Observer; + +use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Model\Page\Authorization; +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Exception\LocalizedException; + +/** + * Performing additional validation each time a user saves a CMS page. + */ +class PageValidatorObserver implements ObserverInterface +{ + /** + * @var Authorization + */ + private $authorization; + + /** + * @param Authorization $authorization + */ + public function __construct(Authorization $authorization) + { + $this->authorization = $authorization; + } + + /** + * @inheritDoc + * + * @throws LocalizedException + */ + public function execute(Observer $observer) + { + /** @var PageInterface $page */ + $page = $observer->getEvent()->getData('page'); + $this->authorization->authorizeFor($page); + } +} diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AddStoreViewToCmsPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AddStoreViewToCmsPageActionGroup.xml new file mode 100644 index 0000000000000..505f3bebbf70c --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AddStoreViewToCmsPageActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddStoreViewToCmsPageActionGroup" extends="NavigateToCreatedCMSPageActionGroup"> + <annotations> + <description>EXTENDS: navigateToCreatedCMSPage. Adds the provided Store View to a Page.</description> + </annotations> + <arguments> + <argument name="storeViewName" type="string"/> + </arguments> + + <remove keyForRemoval="clickExpandContentTabForPage"/> + <remove keyForRemoval="waitForLoadingMaskOfStagingSection"/> + <click selector="{{CmsNewPagePiwSection.header}}" stepKey="clickPageInWebsites" after="waitForPageLoad3"/> + <waitForElementVisible selector="{{CmsNewPagePiwSection.selectStoreView(storeViewName)}}" stepKey="waitForStoreGridReload"/> + <clickWithLeftButton selector="{{CmsNewPagePiwSection.selectStoreView(storeViewName)}}" stepKey="clickStoreView"/> + <click selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="expandButtonMenu"/> + <waitForElementVisible selector="{{CmsNewPagePageActionsSection.splitButtonMenu}}" stepKey="waitForSplitButtonMenuVisible"/> + <click selector="{{CmsNewPagePageActionsSection.savePage}}" stepKey="clickSavePage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <see userInput="You saved the page." stepKey="seeMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCmsPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCmsPageActionGroup.xml new file mode 100644 index 0000000000000..7e907b5b395a4 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCmsPageActionGroup.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOpenCmsPageActionGroup"> + <arguments> + <argument name="page_id" type="string"/> + </arguments> + <amOnPage url="{{AdminCmsPageEditPage.url(page_id)}}" stepKey="openEditCmsPage"/> + </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 336ed8ea495ec..7697931a24176 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageContentActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageContentActionGroup.xml @@ -8,11 +8,11 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertCMSPageContent"> + <actionGroup name="AssertCMSPageContentActionGroup"> <annotations> <description>Validates that the CMS Page details are present and correct. PLEASE NOTE: The CMS Page data is Hardcoded, using '_duplicatedCMSPage'.</description> </annotations> - + <grabValueFrom selector="{{CmsNewPagePageBasicFieldsSection.pageTitle}}" stepKey="grabTextFromTitle"/> <assertEquals stepKey="assertTitle" message="pass"> <expectedResult type="string">{{_duplicatedCMSPage.title}}</expectedResult> @@ -28,19 +28,4 @@ <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"> - <annotations> - <description>Validates that the provided CMS Page Title, Content and Heading are present and correct on a Storefront CMS Page.</description> - </annotations> - <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/ActionGroup/AssertStoreFrontCMSPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertStoreFrontCMSPageActionGroup.xml new file mode 100644 index 0000000000000..136fecbab5d8b --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertStoreFrontCMSPageActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStoreFrontCMSPageActionGroup"> + <annotations> + <description>Validates that the provided CMS Page Title, Content and Heading are present and correct on a Storefront CMS Page.</description> + </annotations> + <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/ActionGroup/AttachImageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AttachImageActionGroup.xml new file mode 100644 index 0000000000000..f4eb3ced2a203 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AttachImageActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AttachImageActionGroup"> + <annotations> + <description>Uploads the provided Image to Media Gallery. + If you use this action group, you MUST add steps to delete the image in the "after" steps.</description> + </annotations> + <arguments> + <argument name="Image"/> + </arguments> + + <attachFile selector="{{MediaGallerySection.BrowseUploadImage}}" userInput="{{Image.value}}" stepKey="uploadImage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> + <waitForElementVisible selector="{{MediaGallerySection.imageOrImageCopy(Image.fileName, Image.extension)}}" stepKey="waitForUploadImage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CMSActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CMSActionGroup.xml deleted file mode 100644 index a75090179460b..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CMSActionGroup.xml +++ /dev/null @@ -1,170 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="navigateToCreatedCMSPage"> - <annotations> - <description>Goes to the Admin CMS Pages page. </description> - </annotations> - <arguments> - <argument name="CMSPage" defaultValue=""/> - </arguments> - - <amOnPage url="{{CmsPagesPage.url}}" stepKey="navigateToCMSPagesGrid"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <conditionalClick selector="{{CmsPagesPageActionsSection.clearAllButton}}" dependentSelector="{{CmsPagesPageActionsSection.activeFilters}}" stepKey="clickToResetFilter" visible="true"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <conditionalClick selector="//div[contains(@data-role, 'grid-wrapper')]/table/thead/tr/th/span[contains(text(), 'ID')]" dependentSelector="//span[contains(text(), 'ID')]/parent::th[not(contains(@class, '_descend'))]/parent::tr/parent::thead/parent::table/parent::div[contains(@data-role, 'grid-wrapper')]" stepKey="clickToAttemptSortByIdDescending" visible="true"/> - <waitForLoadingMaskToDisappear stepKey="waitForFirstIdSortDescendingToFinish"/> - - <!-- Conditional Click again in case it goes from default state to ascending on first click --> - <conditionalClick selector="//div[contains(@data-role, 'grid-wrapper')]/table/thead/tr/th/span[contains(text(), 'ID')]" dependentSelector="//span[contains(text(), 'ID')]/parent::th[not(contains(@class, '_descend'))]/parent::tr/parent::thead/parent::table/parent::div[contains(@data-role, 'grid-wrapper')]" stepKey="secondClickToAttemptSortByIdDescending" visible="true"/> - <waitForLoadingMaskToDisappear stepKey="waitForSecondIdSortDescendingToFinish"/> - <click selector="{{CmsPagesPageActionsSection.select(CMSPage.identifier)}}" stepKey="clickSelectCreatedCMSPage"/> - <click selector="{{CmsPagesPageActionsSection.edit(CMSPage.identifier)}}" stepKey="navigateToCreatedCMSPage"/> - <waitForPageLoad stepKey="waitForPageLoad3"/> - <click selector="{{CmsNewPagePageContentSection.header}}" stepKey="clickExpandContentTabForPage"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskOfStagingSection"/> - </actionGroup> - - <actionGroup name="navigateToCreatedCMSBlockPage"> - <annotations> - <description>Goes to the Admin Blocks page. Clicks on 'Edit' for the provided Block.</description> - </annotations> - <arguments> - <argument name="CMSBlockPage" defaultValue=""/> - </arguments> - - <amOnPage url="{{CmsBlocksPage.url}}" stepKey="navigateToCMSBlocksGrid"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <conditionalClick selector="{{CmsPagesPageActionsSection.clearAllButton}}" dependentSelector="{{CmsPagesPageActionsSection.activeFilters}}" stepKey="clickToResetFilter" visible="true"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <conditionalClick selector="{{BlockPageActionsSection.idColumn}}" dependentSelector="//span[contains(text(), 'ID')]/parent::th[not(contains(@class, '_descend'))]/parent::tr/parent::thead/parent::table/parent::div[contains(@data-role, 'grid-wrapper')]" stepKey="clickToAttemptSortByIdDescending" visible="true"/> - <waitForLoadingMaskToDisappear stepKey="waitForFirstIdSortDescendingToFinish"/> - - <!-- Conditional Click again in case it goes from default state to ascending on first click --> - <conditionalClick selector="{{BlockPageActionsSection.idColumn}}" dependentSelector="//span[contains(text(), 'ID')]/parent::th[not(contains(@class, '_descend'))]/parent::tr/parent::thead/parent::table/parent::div[contains(@data-role, 'grid-wrapper')]" stepKey="secondClickToAttemptSortByIdDescending" visible="true"/> - <waitForLoadingMaskToDisappear stepKey="waitForSecondIdSortDescendingToFinish"/> - <click selector="{{BlockPageActionsSection.select(CMSBlockPage.identifier)}}" stepKey="clickSelectCreatedCMSBlock"/> - <click selector="{{BlockPageActionsSection.edit(CMSBlockPage.identifier)}}" stepKey="navigateToCreatedCMSBlock"/> - <waitForPageLoad stepKey="waitForPageLoad3"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskOfStagingSection"/> - </actionGroup> - - <actionGroup name="DeleteCMSBlockActionGroup"> - <annotations> - <description>Goes to the Admin CMS Blocks page. Deletes the '_defaultBlock' Block. Validates that the Success Message is present and correct. PLEASE NOTE: The Block that is deleted it Hardcoded.</description> - </annotations> - - <amOnPage url="{{CmsBlocksPage.url}}" stepKey="navigateToCMSPagesGrid"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <click selector="{{CmsPagesPageActionsSection.select(_defaultBlock.title)}}" stepKey="ClickOnSelect"/> - <click selector="{{CmsPagesPageActionsSection.delete(_defaultBlock.title)}}" stepKey="ClickOnEdit"/> - <waitForPageLoad stepKey="waitForPageLoad3"/> - <click selector="{{CmsPagesPageActionsSection.deleteConfirm}}" stepKey="ClickToConfirm"/> - <waitForPageLoad stepKey="waitForPageLoad4"/> - <see userInput="You deleted the block." stepKey="VerifyBlockIsDeleted"/> - </actionGroup> - - <actionGroup name="AddStoreViewToCmsPage" extends="navigateToCreatedCMSPage"> - <annotations> - <description>EXTENDS: navigateToCreatedCMSPage. Adds the provided Store View to a Page.</description> - </annotations> - <arguments> - <argument name="storeViewName" type="string"/> - </arguments> - - <remove keyForRemoval="clickExpandContentTabForPage"/> - <remove keyForRemoval="waitForLoadingMaskOfStagingSection"/> - <click selector="{{CmsNewPagePiwSection.header}}" stepKey="clickPageInWebsites" after="waitForPageLoad3"/> - <waitForElementVisible selector="{{CmsNewPagePiwSection.selectStoreView(storeViewName)}}" stepKey="waitForStoreGridReload"/> - <clickWithLeftButton selector="{{CmsNewPagePiwSection.selectStoreView(storeViewName)}}" stepKey="clickStoreView"/> - <click selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="expandButtonMenu"/> - <waitForElementVisible selector="{{CmsNewPagePageActionsSection.splitButtonMenu}}" stepKey="waitForSplitButtonMenuVisible"/> - <click selector="{{CmsNewPagePageActionsSection.savePage}}" stepKey="clickSavePage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <see userInput="You saved the page." stepKey="seeMessage"/> - </actionGroup> - - <actionGroup name="saveAndCloseCMSBlockWithSplitButton"> - <annotations> - <description>Clicks on the Save and Close button.</description> - </annotations> - - <waitForElementVisible selector="{{BlockNewPagePageActionsSection.expandSplitButton}}" stepKey="waitForExpandSplitButtonToBeVisible"/> - <click selector="{{BlockNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitButton"/> - <click selector="{{BlockNewPagePageActionsSection.saveAndClose}}" stepKey="clickSaveBlock"/> - <waitForPageLoad stepKey="waitForPageLoadAfterClickingSave"/> - <see userInput="You saved the block." stepKey="assertSaveBlockSuccessMessage"/> - </actionGroup> - - <actionGroup name="navigateToStorefrontForCreatedPage"> - <annotations> - <description>Goes to the provided Page on the Storefront.</description> - </annotations> - <arguments> - <argument name="page" type="string"/> - </arguments> - - <amOnPage url="{{page}}" stepKey="goToStorefront"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - </actionGroup> - - <actionGroup name="saveCMSBlock"> - <annotations> - <description>Clicks on the Save button. Validates that the Save message is present and correct. PLEASE NOTE: The message is Hardcoded.</description> - </annotations> - - <waitForElementVisible selector="{{CmsNewBlockBlockActionsSection.savePage}}" stepKey="waitForSaveButton"/> - <click selector="{{CmsNewBlockBlockActionsSection.savePage}}" stepKey="clickSaveButton"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <see userInput="You saved the block." stepKey="seeSuccessfulSaveMessage"/> - </actionGroup> - - <actionGroup name="saveAndContinueEditCmsPage"> - <annotations> - <description>Clicks on the Save and Continue button.</description> - </annotations> - - <waitForElementVisible time="10" selector="{{CmsNewPagePageActionsSection.saveAndContinueEdit}}" stepKey="waitForSaveAndContinueVisibility"/> - <click selector="{{CmsNewPagePageActionsSection.saveAndContinueEdit}}" stepKey="clickSaveAndContinueEditCmsPage"/> - <waitForPageLoad stepKey="waitForCmsPageLoad"/> - <waitForElementVisible time="1" selector="{{CmsNewPagePageActionsSection.cmsPageTitle}}" stepKey="waitForCmsPageSaveButton"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - </actionGroup> - - <actionGroup name="saveCmsPage"> - <annotations> - <description>Clicks on the Save button. Validates that the Success Message is present.</description> - </annotations> - - <waitForElementVisible selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="waitForSplitButton"/> - <click selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitButton"/> - <waitForElementVisible selector="{{CmsNewPagePageActionsSection.savePage}}" stepKey="waitForSaveCmsPage"/> - <click selector="{{CmsNewPagePageActionsSection.savePage}}" stepKey="clickSaveCmsPage"/> - <waitForElementVisible time="1" selector="{{CmsPagesPageActionsSection.addNewPageButton}}" stepKey="waitForCmsPageSaveButton"/> - <see userInput="You saved the page." selector="{{CmsPagesPageActionsSection.savePageSuccessMessage}}" stepKey="assertSavePageSuccessMessage"/> - </actionGroup> - - <actionGroup name="setLayout"> - <annotations> - <description>Sets the provided option for 'Layout', under 'Design' on the Page creation/edit page. </description> - </annotations> - <arguments> - <argument name="designSection"/> - <argument name="layoutOption"/> - </arguments> - - <waitForElementVisible selector="{{designSection.DesignTab}}" stepKey="waitForDesignTabVisible"/> - <conditionalClick selector="{{designSection.DesignTab}}" dependentSelector="{{designSection.LayoutDropdown}}" visible="false" stepKey="clickOnDesignTab"/> - <waitForPageLoad stepKey="waitForPageLoadDesignTab"/> - <waitForElementVisible selector="{{designSection.LayoutDropdown}}" stepKey="waitForLayoutDropDown"/> - <selectOption selector="{{designSection.LayoutDropdown}}" userInput="{{layoutOption}}" stepKey="selectLayout"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsForCMSHomePageContentWYSIWYGDisabledActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsForCMSHomePageContentWYSIWYGDisabledActionGroup.xml new file mode 100644 index 0000000000000..adaeb8c90ff0b --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsForCMSHomePageContentWYSIWYGDisabledActionGroup.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="ClearWidgetsForCMSHomePageContentWYSIWYGDisabledActionGroup"> + <amOnPage url="{{CmsPageEditPage.url('2')}}" stepKey="navigateToEditHomePagePage"/> + <waitForPageLoad stepKey="waitForCmsPageEditPage"/> + <conditionalClick selector="{{CmsNewPagePageActionsSection.contentSectionName}}" dependentSelector="{{CatalogWidgetSection.insertWidgetButton}}" visible="false" stepKey="clickShowHideEditorIfVisible"/> + <waitForElementVisible selector="{{CmsNewPagePageContentSection.content}}" stepKey="waitForContentField"/> + <fillField selector="{{CmsNewPagePageContentSection.content}}" userInput="CMS homepage content goes here." stepKey="resetCMSPageToDefaultContent"/> + <click selector="{{CmsNewPagePageActionsSection.saveAndContinueEdit}}" stepKey="clickSave"/> + <waitForPageLoad stepKey="waitForSettingsApply"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsFromCMSContentActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsFromCMSContentActionGroup.xml index b176de3748282..98de51574aa4b 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsFromCMSContentActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClearWidgetsFromCMSContentActionGroup.xml @@ -8,11 +8,11 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="ClearWidgetsFromCMSContent"> + <actionGroup name="ClearWidgetsFromCMSContentActionGroup"> <annotations> <description>Goes to the Admin CMS Page Edit page for the Page ID number 2. Clears the Widget and replaces it with Text. PLEASE NOTE: The Page ID/Text are Hardcoded.</description> </annotations> - + <amOnPage url="{{CmsPageEditPage.url('2')}}" stepKey="navigateToEditHomePagePage"/> <waitForPageLoad stepKey="waitEditHomePagePageToLoad"/> <click selector="{{CmsNewPagePageContentSection.header}}" stepKey="clickContentTab"/> @@ -26,13 +26,4 @@ <waitForPageLoad stepKey="waitSaveToBeApplied"/> <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="You saved the page." stepKey="seeSaveSuccess"/> </actionGroup> - <actionGroup name="ClearWidgetsForCMSHomePageContentWYSIWYGDisabled"> - <amOnPage url="{{CmsPageEditPage.url('2')}}" stepKey="navigateToEditHomePagePage"/> - <waitForPageLoad stepKey="waitForCmsPageEditPage"/> - <conditionalClick selector="{{CmsNewPagePageActionsSection.contentSectionName}}" dependentSelector="{{CatalogWidgetSection.insertWidgetButton}}" visible="false" stepKey="clickShowHideEditorIfVisible"/> - <waitForElementVisible selector="{{CmsNewPagePageContentSection.content}}" stepKey="waitForContentField"/> - <fillField selector="{{CmsNewPagePageContentSection.content}}" userInput="CMS homepage content goes here." stepKey="resetCMSPageToDefaultContent"/> - <click selector="{{CmsNewPagePageActionsSection.saveAndContinueEdit}}" stepKey="clickSave"/> - <waitForPageLoad stepKey="waitForSettingsApply"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClickBrowseBtnOnUploadPopupActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClickBrowseBtnOnUploadPopupActionGroup.xml new file mode 100644 index 0000000000000..b0c0278f0c1a5 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/ClickBrowseBtnOnUploadPopupActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ClickBrowseBtnOnUploadPopupActionGroup"> + <annotations> + <description>Clicks on the Browse button in the 'Insert/edit image' model.</description> + </annotations> + + <click selector="{{MediaGallerySection.Browse}}" stepKey="clickBrowse"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateImageFolderActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateImageFolderActionGroup.xml new file mode 100644 index 0000000000000..e39263debd127 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateImageFolderActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateImageFolderActionGroup"> + <annotations> + <description>Creates a folder in the Media Gallery based on the provided Folder.</description> + </annotations> + <arguments> + <argument name="ImageFolder" defaultValue="ImageFolder"/> + </arguments> + + <click selector="{{MediaGallerySection.CreateFolder}}" stepKey="createFolder"/> + <waitForElementVisible selector="{{MediaGallerySection.FolderName}}" stepKey="waitForPopUp"/> + <fillField selector="{{MediaGallerySection.FolderName}}" userInput="{{ImageFolder.name}}" stepKey="fillFolderName"/> + <click selector="{{MediaGallerySection.AcceptFolderName}}" stepKey="acceptFolderName"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading3"/> + <waitForPageLoad stepKey="waitForLoadingArrowToExpand" time="15"/> + <conditionalClick selector="{{MediaGallerySection.StorageRootArrow}}" dependentSelector="{{MediaGallerySection.checkIfArrowExpand}}" stepKey="clickArrowIfClosed" visible="true"/> + <conditionalClick selector="{{MediaGallerySection.WysiwygArrow}}" dependentSelector="{{MediaGallerySection.checkIfWysiwygArrowExpand}}" stepKey="clickWysiwygArrowIfClosed" visible="true"/> + <waitForText userInput="{{ImageFolder.name}}" stepKey="waitForNewFolder"/> + <click userInput="{{ImageFolder.name}}" stepKey="clickOnCreatedFolder"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading5"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml index fa5cccafccd54..fb8250ae00f27 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesActionGroup.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="CreateNewPageWithAllValues"> + <actionGroup name="CreateNewPageWithAllValuesActionGroup"> <annotations> <description>Goes to the Admin CMS creation page. Fills in the provided Page details (Title, Heading, URL, Store View and Hierarchy).</description> </annotations> @@ -33,10 +33,4 @@ <click selector="{{CmsNewPageHierarchySection.header}}" stepKey="clickHierarchy"/> <click selector="{{CmsNewPageHierarchySection.selectHierarchy(selectHierarchyOpt)}}" stepKey="clickPageCheckBoxes"/> </actionGroup> - <actionGroup name="CreateNewPageWithAllValuesAndContent" extends="CreateNewPageWithAllValues"> - <arguments> - <argument name="pageContent" type="string"/> - </arguments> - <fillField selector="{{CmsNewPagePageContentSection.content}}" userInput="{{pageContent}}" stepKey="fillContentField" after="fillFieldContentHeading"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesAndContentActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesAndContentActionGroup.xml new file mode 100644 index 0000000000000..f231cdbbbfe37 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithAllValuesAndContentActionGroup.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="CreateNewPageWithAllValuesAndContentActionGroup" extends="CreateNewPageWithAllValuesActionGroup"> + <arguments> + <argument name="pageContent" type="string"/> + </arguments> + <fillField selector="{{CmsNewPagePageContentSection.content}}" userInput="{{pageContent}}" stepKey="fillContentField" after="fillFieldContentHeading"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeleteCMSBlockActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeleteCMSBlockActionGroup.xml new file mode 100644 index 0000000000000..2f9b03f1e33b3 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeleteCMSBlockActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteCMSBlockActionGroup"> + <annotations> + <description>Goes to the Admin CMS Blocks page. Deletes the '_defaultBlock' Block. Validates that the Success Message is present and correct. PLEASE NOTE: The Block that is deleted it Hardcoded.</description> + </annotations> + + <amOnPage url="{{CmsBlocksPage.url}}" stepKey="navigateToCMSPagesGrid"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <click selector="{{CmsPagesPageActionsSection.select(_defaultBlock.title)}}" stepKey="ClickOnSelect"/> + <click selector="{{CmsPagesPageActionsSection.delete(_defaultBlock.title)}}" stepKey="ClickOnEdit"/> + <waitForPageLoad stepKey="waitForPageLoad3"/> + <click selector="{{CmsPagesPageActionsSection.deleteConfirm}}" stepKey="ClickToConfirm"/> + <waitForPageLoad stepKey="waitForPageLoad4"/> + <see userInput="You deleted the block." stepKey="VerifyBlockIsDeleted"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeleteImageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeleteImageActionGroup.xml new file mode 100644 index 0000000000000..3bf2c6795bcb6 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeleteImageActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteImageActionGroup"> + <annotations> + <description>Deletes the selected Image from the Media Gallery.</description> + </annotations> + + <see selector="{{MediaGallerySection.DeleteSelectedBtn}}" userInput="Delete Selected" stepKey="seeDeleteBtn"/> + <click selector="{{MediaGallerySection.DeleteSelectedBtn}}" stepKey="clickDeleteSelected"/> + <waitForText userInput="OK" stepKey="waitForConfirm"/> + <click selector="{{MediaGallerySection.confirmDelete}}" stepKey="confirmDelete"/> + <waitForElementNotVisible selector="{{MediaGallerySection.image(ImageUpload.file)}}" stepKey="waitForImageDeleted"/> + <dontSeeElement selector="{{MediaGallerySection.image(ImageUpload.file)}}" stepKey="dontSeeImage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeleteImageFromStorageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeleteImageFromStorageActionGroup.xml index dff2d60c9e8c3..52a5757ec7b9a 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeleteImageFromStorageActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/DeleteImageFromStorageActionGroup.xml @@ -18,7 +18,8 @@ <waitForElementVisible selector="{{MediaGallerySection.imageOrImageCopy(Image.fileName, Image.extension)}}" stepKey="waitForInitialImages"/> <grabMultiple selector="{{MediaGallerySection.imageOrImageCopy(Image.fileName, Image.extension)}}" stepKey="initialImages"/> - <click selector="{{MediaGallerySection.imageOrImageCopy(Image.fileName, Image.extension)}}" stepKey="selectImage"/> + <waitForElementVisible selector="{{MediaGallerySection.lastImageOrImageCopy(Image.fileName, Image.extension)}}" stepKey="waitForLastImage"/> + <click selector="{{MediaGallerySection.lastImageOrImageCopy(Image.fileName, Image.extension)}}" stepKey="selectImage"/> <waitForElementVisible selector="{{MediaGallerySection.DeleteSelectedBtn}}" stepKey="waitForDeleteBtn"/> <click selector="{{MediaGallerySection.DeleteSelectedBtn}}" stepKey="clickDeleteSelected"/> <waitForPageLoad stepKey="waitForPageLoad1"/> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/FillOutUploadImagePopupActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/FillOutUploadImagePopupActionGroup.xml new file mode 100644 index 0000000000000..dc4abe9a5115a --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/FillOutUploadImagePopupActionGroup.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="FillOutUploadImagePopupActionGroup"> + <annotations> + <description>Fills in the Image Description and Height fields. PLEASE NOTE: The values are Hardcoded using 'ImageUpload'.</description> + </annotations> + + <waitForElementVisible selector="{{MediaGallerySection.OkBtn}}" stepKey="waitForOkBtn"/> + <fillField selector="{{MediaGallerySection.ImageDescription}}" userInput="{{ImageUpload.content}}" stepKey="fillImageDescription"/> + <fillField selector="{{MediaGallerySection.Height}}" userInput="{{ImageUpload.height}}" stepKey="fillImageHeight"/> + <click selector="{{MediaGallerySection.OkBtn}}" stepKey="clickOkBtn"/> + <waitForPageLoad stepKey="wait3"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/NavigateToCreatedCMSBlockPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/NavigateToCreatedCMSBlockPageActionGroup.xml new file mode 100644 index 0000000000000..06bc3787579e4 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/NavigateToCreatedCMSBlockPageActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="NavigateToCreatedCMSBlockPageActionGroup"> + <annotations> + <description>Goes to the Admin Blocks page. Clicks on 'Edit' for the provided Block.</description> + </annotations> + <arguments> + <argument name="CMSBlockPage" defaultValue=""/> + </arguments> + + <amOnPage url="{{CmsBlocksPage.url}}" stepKey="navigateToCMSBlocksGrid"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <conditionalClick selector="{{CmsPagesPageActionsSection.clearAllButton}}" dependentSelector="{{CmsPagesPageActionsSection.activeFilters}}" stepKey="clickToResetFilter" visible="true"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <conditionalClick selector="{{BlockPageActionsSection.idColumn}}" dependentSelector="//span[contains(text(), 'ID')]/parent::th[not(contains(@class, '_descend'))]/parent::tr/parent::thead/parent::table/parent::div[contains(@data-role, 'grid-wrapper')]" stepKey="clickToAttemptSortByIdDescending" visible="true"/> + <waitForLoadingMaskToDisappear stepKey="waitForFirstIdSortDescendingToFinish"/> + + <!-- Conditional Click again in case it goes from default state to ascending on first click --> + <conditionalClick selector="{{BlockPageActionsSection.idColumn}}" dependentSelector="//span[contains(text(), 'ID')]/parent::th[not(contains(@class, '_descend'))]/parent::tr/parent::thead/parent::table/parent::div[contains(@data-role, 'grid-wrapper')]" stepKey="secondClickToAttemptSortByIdDescending" visible="true"/> + <waitForLoadingMaskToDisappear stepKey="waitForSecondIdSortDescendingToFinish"/> + <click selector="{{BlockPageActionsSection.select(CMSBlockPage.identifier)}}" stepKey="clickSelectCreatedCMSBlock"/> + <click selector="{{BlockPageActionsSection.edit(CMSBlockPage.identifier)}}" stepKey="navigateToCreatedCMSBlock"/> + <waitForPageLoad stepKey="waitForPageLoad3"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskOfStagingSection"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/NavigateToCreatedCMSPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/NavigateToCreatedCMSPageActionGroup.xml new file mode 100644 index 0000000000000..3b763d3f44125 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/NavigateToCreatedCMSPageActionGroup.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="NavigateToCreatedCMSPageActionGroup"> + <annotations> + <description>Goes to the Admin CMS Pages page. </description> + </annotations> + <arguments> + <argument name="CMSPage" defaultValue=""/> + </arguments> + + <amOnPage url="{{CmsPagesPage.url}}" stepKey="navigateToCMSPagesGrid"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <conditionalClick selector="{{CmsPagesPageActionsSection.clearAllButton}}" dependentSelector="{{CmsPagesPageActionsSection.activeFilters}}" stepKey="clickToResetFilter" visible="true"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <conditionalClick selector="//div[contains(@data-role, 'grid-wrapper')]/table/thead/tr/th/span[contains(text(), 'ID')]" dependentSelector="//span[contains(text(), 'ID')]/parent::th[not(contains(@class, '_descend'))]/parent::tr/parent::thead/parent::table/parent::div[contains(@data-role, 'grid-wrapper')]" stepKey="clickToAttemptSortByIdDescending" visible="true"/> + <waitForLoadingMaskToDisappear stepKey="waitForFirstIdSortDescendingToFinish"/> + + <!-- Conditional Click again in case it goes from default state to ascending on first click --> + <conditionalClick selector="//div[contains(@data-role, 'grid-wrapper')]/table/thead/tr/th/span[contains(text(), 'ID')]" dependentSelector="//span[contains(text(), 'ID')]/parent::th[not(contains(@class, '_descend'))]/parent::tr/parent::thead/parent::table/parent::div[contains(@data-role, 'grid-wrapper')]" stepKey="secondClickToAttemptSortByIdDescending" visible="true"/> + <waitForLoadingMaskToDisappear stepKey="waitForSecondIdSortDescendingToFinish"/> + <click selector="{{CmsPagesPageActionsSection.select(CMSPage.identifier)}}" stepKey="clickSelectCreatedCMSPage"/> + <click selector="{{CmsPagesPageActionsSection.edit(CMSPage.identifier)}}" stepKey="navigateToCreatedCMSPage"/> + <waitForPageLoad stepKey="waitForPageLoad3"/> + <click selector="{{CmsNewPagePageContentSection.header}}" stepKey="clickExpandContentTabForPage"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskOfStagingSection"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/NavigateToStorefrontForCreatedPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/NavigateToStorefrontForCreatedPageActionGroup.xml new file mode 100644 index 0000000000000..7576c61e2391c --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/NavigateToStorefrontForCreatedPageActionGroup.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="NavigateToStorefrontForCreatedPageActionGroup"> + <annotations> + <description>Goes to the provided Page on the Storefront.</description> + </annotations> + <arguments> + <argument name="page" type="string"/> + </arguments> + + <amOnPage url="{{page}}" stepKey="goToStorefront"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveAndCloseCMSBlockWithSplitButtonActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveAndCloseCMSBlockWithSplitButtonActionGroup.xml new file mode 100644 index 0000000000000..95ce3c499b6b8 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveAndCloseCMSBlockWithSplitButtonActionGroup.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="SaveAndCloseCMSBlockWithSplitButtonActionGroup"> + <annotations> + <description>Clicks on the Save and Close button.</description> + </annotations> + + <waitForElementVisible selector="{{BlockNewPagePageActionsSection.expandSplitButton}}" stepKey="waitForExpandSplitButtonToBeVisible"/> + <click selector="{{BlockNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitButton"/> + <click selector="{{BlockNewPagePageActionsSection.saveAndClose}}" stepKey="clickSaveBlock"/> + <waitForPageLoad stepKey="waitForPageLoadAfterClickingSave"/> + <see userInput="You saved the block." stepKey="assertSaveBlockSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveAndContinueEditCmsPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveAndContinueEditCmsPageActionGroup.xml new file mode 100644 index 0000000000000..a8dce19153a98 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveAndContinueEditCmsPageActionGroup.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="SaveAndContinueEditCmsPageActionGroup"> + <annotations> + <description>Clicks on the Save and Continue button.</description> + </annotations> + + <waitForElementVisible time="10" selector="{{CmsNewPagePageActionsSection.saveAndContinueEdit}}" stepKey="waitForSaveAndContinueVisibility"/> + <click selector="{{CmsNewPagePageActionsSection.saveAndContinueEdit}}" stepKey="clickSaveAndContinueEditCmsPage"/> + <waitForPageLoad stepKey="waitForCmsPageLoad"/> + <waitForElementVisible time="1" selector="{{CmsNewPagePageActionsSection.cmsPageTitle}}" stepKey="waitForCmsPageSaveButton"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveCMSBlockActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveCMSBlockActionGroup.xml new file mode 100644 index 0000000000000..2f86df70ae8c8 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveCMSBlockActionGroup.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="SaveCMSBlockActionGroup"> + <annotations> + <description>Clicks on the Save button. Validates that the Save message is present and correct. PLEASE NOTE: The message is Hardcoded.</description> + </annotations> + + <waitForElementVisible selector="{{CmsNewBlockBlockActionsSection.savePage}}" stepKey="waitForSaveButton"/> + <click selector="{{CmsNewBlockBlockActionsSection.savePage}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <see userInput="You saved the block." stepKey="seeSuccessfulSaveMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveCmsPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveCmsPageActionGroup.xml new file mode 100644 index 0000000000000..5b3b0460edbdb --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveCmsPageActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SaveCmsPageActionGroup"> + <annotations> + <description>Clicks on the Save button. Validates that the Success Message is present.</description> + </annotations> + + <waitForElementVisible selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="waitForSplitButton"/> + <click selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitButton"/> + <waitForElementVisible selector="{{CmsNewPagePageActionsSection.savePage}}" stepKey="waitForSaveCmsPage"/> + <click selector="{{CmsNewPagePageActionsSection.savePage}}" stepKey="clickSaveCmsPage"/> + <waitForElementVisible time="1" selector="{{CmsPagesPageActionsSection.addNewPageButton}}" stepKey="waitForCmsPageSaveButton"/> + <see userInput="You saved the page." selector="{{CmsPagesPageActionsSection.savePageSuccessMessage}}" stepKey="assertSavePageSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveImageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveImageActionGroup.xml new file mode 100644 index 0000000000000..612cc1c485b08 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveImageActionGroup.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="SaveImageActionGroup"> + <annotations> + <description>Clicks on Insert File in the Media Gallery.</description> + </annotations> + + <click selector="{{MediaGallerySection.InsertFile}}" stepKey="clickInsertBtn"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SelectImageFromMediaStorageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SelectImageFromMediaStorageActionGroup.xml deleted file mode 100644 index e2a3bbcbdc797..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SelectImageFromMediaStorageActionGroup.xml +++ /dev/null @@ -1,119 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="clickBrowseBtnOnUploadPopup"> - <annotations> - <description>Clicks on the Browse button in the 'Insert/edit image' model.</description> - </annotations> - - <click selector="{{MediaGallerySection.Browse}}" stepKey="clickBrowse"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - </actionGroup> - - <actionGroup name="VerifyMediaGalleryStorageActions"> - <annotations> - <description>Validates that the Media Gallery 'Cancel'/'Create Folder' buttons are present.</description> - </annotations> - - <waitForPageLoad stepKey="waitForPageLoad1"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoading1"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoading2"/> - <see selector="{{MediaGallerySection.CancelBtn}}" userInput="Cancel" stepKey="seeCancelBtn"/> - <see selector="{{MediaGallerySection.CreateFolder}}" userInput="Create Folder" stepKey="seeCreateFolderBtn"/> - </actionGroup> - - <actionGroup name="CreateImageFolder"> - <annotations> - <description>Creates a folder in the Media Gallery based on the provided Folder.</description> - </annotations> - <arguments> - <argument name="ImageFolder" defaultValue="ImageFolder"/> - </arguments> - - <click selector="{{MediaGallerySection.CreateFolder}}" stepKey="createFolder"/> - <waitForElementVisible selector="{{MediaGallerySection.FolderName}}" stepKey="waitForPopUp"/> - <fillField selector="{{MediaGallerySection.FolderName}}" userInput="{{ImageFolder.name}}" stepKey="fillFolderName"/> - <click selector="{{MediaGallerySection.AcceptFolderName}}" stepKey="acceptFolderName"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoading3"/> - <waitForPageLoad stepKey="waitForLoadingArrowToExpand" time="15"/> - <conditionalClick selector="{{MediaGallerySection.StorageRootArrow}}" dependentSelector="{{MediaGallerySection.checkIfArrowExpand}}" stepKey="clickArrowIfClosed" visible="true"/> - <conditionalClick selector="{{MediaGallerySection.WysiwygArrow}}" dependentSelector="{{MediaGallerySection.checkIfWysiwygArrowExpand}}" stepKey="clickWysiwygArrowIfClosed" visible="true"/> - <waitForText userInput="{{ImageFolder.name}}" stepKey="waitForNewFolder"/> - <click userInput="{{ImageFolder.name}}" stepKey="clickOnCreatedFolder"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoading5"/> - </actionGroup> - - <actionGroup name="attachImage"> - <annotations> - <description>Uploads the provided Image to Media Gallery.</description> - </annotations> - <arguments> - <argument name="Image"/> - </arguments> - - <attachFile selector="{{MediaGallerySection.BrowseUploadImage}}" userInput="{{Image.value}}" stepKey="uploadImage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> - <waitForElementVisible selector="{{MediaGallerySection.imageOrImageCopy(Image.fileName, Image.extension)}}" stepKey="waitForUploadImage"/> - </actionGroup> - - <actionGroup name="deleteImage"> - <annotations> - <description>Deletes the selected Image from the Media Gallery.</description> - </annotations> - - <see selector="{{MediaGallerySection.DeleteSelectedBtn}}" userInput="Delete Selected" stepKey="seeDeleteBtn"/> - <click selector="{{MediaGallerySection.DeleteSelectedBtn}}" stepKey="clickDeleteSelected"/> - <waitForText userInput="OK" stepKey="waitForConfirm"/> - <click selector="{{MediaGallerySection.confirmDelete}}" stepKey="confirmDelete"/> - <waitForElementNotVisible selector="{{MediaGallerySection.image(ImageUpload.file)}}" stepKey="waitForImageDeleted"/> - <dontSeeElement selector="{{MediaGallerySection.image(ImageUpload.file)}}" stepKey="dontSeeImage"/> - </actionGroup> - - <actionGroup name="saveImage"> - <annotations> - <description>Clicks on Insert File in the Media Gallery.</description> - </annotations> - - <click selector="{{MediaGallerySection.InsertFile}}" stepKey="clickInsertBtn"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> - </actionGroup> - - <actionGroup name="fillOutUploadImagePopup"> - <annotations> - <description>Fills in the Image Description and Height fields. PLEASE NOTE: The values are Hardcoded using 'ImageUpload'.</description> - </annotations> - - <waitForElementVisible selector="{{MediaGallerySection.OkBtn}}" stepKey="waitForOkBtn"/> - <fillField selector="{{MediaGallerySection.ImageDescription}}" userInput="{{ImageUpload.content}}" stepKey="fillImageDescription"/> - <fillField selector="{{MediaGallerySection.Height}}" userInput="{{ImageUpload.height}}" stepKey="fillImageHeight"/> - <click selector="{{MediaGallerySection.OkBtn}}" stepKey="clickOkBtn"/> - <waitForPageLoad stepKey="wait3"/> - </actionGroup> - - <actionGroup name="verifyOversizeValidationMsg"> - <annotations> - <description>Validates that the Oversize validation message is present and correct.</description> - </annotations> - - <see userInput="this is oversize" stepKey="seeValidationMsg"/> - <click selector="#OK" stepKey="clickOKBtn"/> - </actionGroup> - - <actionGroup name="verifyWrongExtensionValidationMsg"> - <annotations> - <description>Validates that the Wrong Extensions validation message is present and correct.</description> - </annotations> - - <see userInput="this is wrong extension" stepKey="seeValidationMsg"/> - <click selector="#OK" stepKey="clickOKBtn"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SetLayoutActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SetLayoutActionGroup.xml new file mode 100644 index 0000000000000..23f4cccd33118 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SetLayoutActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SetLayoutActionGroup"> + <annotations> + <description>Sets the provided option for 'Layout', under 'Design' on the Page creation/edit page. </description> + </annotations> + <arguments> + <argument name="designSection"/> + <argument name="layoutOption"/> + </arguments> + + <waitForElementVisible selector="{{designSection.DesignTab}}" stepKey="waitForDesignTabVisible"/> + <conditionalClick selector="{{designSection.DesignTab}}" dependentSelector="{{designSection.LayoutDropdown}}" visible="false" stepKey="clickOnDesignTab"/> + <waitForPageLoad stepKey="waitForPageLoadDesignTab"/> + <waitForElementVisible selector="{{designSection.LayoutDropdown}}" stepKey="waitForLayoutDropDown"/> + <selectOption selector="{{designSection.LayoutDropdown}}" userInput="{{layoutOption}}" stepKey="selectLayout"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontClickHeaderLinkActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontClickHeaderLinkActionGroup.xml new file mode 100644 index 0000000000000..48e7ec81ae1c1 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontClickHeaderLinkActionGroup.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="StorefrontClickHeaderLinkActionGroup"> + <annotations> + <description>Clicks a link in the storefront header.</description> + </annotations> + <arguments> + <argument name="linkName" type="string" defaultValue="Create an Account"/> + </arguments> + + <click stepKey="clickTheLink" selector="{{StorefrontHeaderSection.headerLinkByText(linkName)}}"/> + <waitForPageLoad stepKey="wait"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontSeeHeaderLinksActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontSeeHeaderLinksActionGroup.xml new file mode 100644 index 0000000000000..ec5c388ee1c6b --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontSeeHeaderLinksActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontSeeHeaderLinksActionGroup"> + <annotations> + <description>See a link by name in the storefront header.</description> + </annotations> + <arguments> + <argument name="linkName" type="string" defaultValue="Create an Account"/> + </arguments> + <see stepKey="seeElement" selector="{{StorefrontHeaderSection.headerlinks}}" userInput="{{linkName}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyMagentoEntityActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyMagentoEntityActionGroup.xml new file mode 100644 index 0000000000000..717c0dbb83dd2 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyMagentoEntityActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="VerifyMagentoEntityActionGroup"> + <annotations> + <description>Validates that the Insert Widget and Insert Variables buttons are present.</description> + </annotations> + + <seeElement selector="{{TinyMCESection.InsertWidgetIcon}}" stepKey="assertInfo15"/> + <seeElement selector="{{TinyMCESection.InsertVariableIcon}}" stepKey="assertInfo16"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyMediaGalleryStorageActionsActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyMediaGalleryStorageActionsActionGroup.xml new file mode 100644 index 0000000000000..4410be84485d0 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyMediaGalleryStorageActionsActionGroup.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="VerifyMediaGalleryStorageActionsActionGroup"> + <annotations> + <description>Validates that the Media Gallery 'Cancel'/'Create Folder' buttons are present.</description> + </annotations> + + <waitForPageLoad stepKey="waitForPageLoad1"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading1"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading2"/> + <see selector="{{MediaGallerySection.CancelBtn}}" userInput="Cancel" stepKey="seeCancelBtn"/> + <see selector="{{MediaGallerySection.CreateFolder}}" userInput="Create Folder" stepKey="seeCreateFolderBtn"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyOversizeValidationMsgActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyOversizeValidationMsgActionGroup.xml new file mode 100644 index 0000000000000..6a4d49fcd6eb7 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyOversizeValidationMsgActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="VerifyOversizeValidationMsgActionGroup"> + <annotations> + <description>Validates that the Oversize validation message is present and correct.</description> + </annotations> + + <see userInput="this is oversize" stepKey="seeValidationMsg"/> + <click selector="#OK" stepKey="clickOKBtn"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyTinyMCEActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyTinyMCEActionGroup.xml index c1682e32c28c6..7ba0fcdf0d605 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyTinyMCEActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyTinyMCEActionGroup.xml @@ -12,7 +12,7 @@ <annotations> <description>Goes to Admin CMS Page creation page. Validates that all of the Tiny MCE buttons are present.</description> </annotations> - + <seeElement selector="{{TinyMCESection.Style}}" stepKey="assertInfo2"/> <seeElement selector="{{TinyMCESection.Bold}}" stepKey="assertInfo3"/> <seeElement selector="{{TinyMCESection.Italic}}" stepKey="assertInfo4"/> @@ -27,13 +27,4 @@ <seeElement selector="{{TinyMCESection.InsertTable}}" stepKey="assertInfo13"/> <seeElement selector="{{TinyMCESection.SpecialCharacter}}" stepKey="assertInfo14"/> </actionGroup> - - <actionGroup name="VerifyMagentoEntityActionGroup"> - <annotations> - <description>Validates that the Insert Widget and Insert Variables buttons are present.</description> - </annotations> - - <seeElement selector="{{TinyMCESection.InsertWidgetIcon}}" stepKey="assertInfo15"/> - <seeElement selector="{{TinyMCESection.InsertVariableIcon}}" stepKey="assertInfo16"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyWrongExtensionValidationMsgActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyWrongExtensionValidationMsgActionGroup.xml new file mode 100644 index 0000000000000..bfd43bf0d9e1d --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyWrongExtensionValidationMsgActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="VerifyWrongExtensionValidationMsgActionGroup"> + <annotations> + <description>Validates that the Wrong Extensions validation message is present and correct.</description> + </annotations> + + <see userInput="this is wrong extension" stepKey="seeValidationMsg"/> + <click selector="#OK" stepKey="clickOKBtn"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsPageEditPage.xml b/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsPageEditPage.xml new file mode 100644 index 0000000000000..978b6d6a6d261 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsPageEditPage.xml @@ -0,0 +1,16 @@ +<?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="AdminCmsPageEditPage" area="admin" url="/cms/page/edit/page_id/{{id}}" parameterized="true" module="Magento_Cms"> + <section name="CmsNewPagePageActionsSection"/> + <section name="CmsNewPagePageBasicFieldsSection"/> + <section name="CmsNewPagePageContentSection"/> + <section name="CmsNewPagePageSeoSection"/> + </page> +</pages> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/StorefrontHeaderSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/StorefrontHeaderSection.xml new file mode 100644 index 0000000000000..7b47c6b49db7b --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Section/StorefrontHeaderSection.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="StorefrontHeaderSection"> + <element name="headerlinks" type="text" selector="ul.header.links"/> + <element name="headerLinkByText" type="text" selector="//ul[contains(@class, 'header') and contains(@class, 'links')]/li/a[contains(text(),'{{LinkName}}')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml index b1d0faa7507f0..b85c7554b58ae 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml @@ -40,6 +40,7 @@ <element name="BrowseUploadImage" type="file" selector=".fileupload" /> <element name="image" type="text" selector="//small[text()='{{var1}}']" parameterized="true"/> <element name="imageOrImageCopy" type="text" selector="//div[contains(@class,'media-gallery-modal')]//img[contains(@alt, '{{arg1}}.{{arg2}}')]|//img[contains(@alt,'{{arg1}}_') and contains(@alt,'.{{arg2}}')]" parameterized="true"/> + <element name="lastImageOrImageCopy" type="text" selector="(//div[contains(@class,'media-gallery-modal')]//img[contains(@alt, '{{arg1}}.{{arg2}}')]|//img[contains(@alt,'{{arg1}}_') and contains(@alt,'.{{arg2}}')])[last()]" parameterized="true"/> <element name="imageSelected" type="text" selector="//small[text()='{{var1}}']/parent::*[@class='filecnt selected']" parameterized="true"/> <element name="ImageSource" type="input" selector=".mce-combobox.mce-abs-layout-item.mce-last.mce-has-open" /> <element name="ImageDescription" type="input" selector=".mce-textbox.mce-abs-layout-item.mce-last" /> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToCMSPageTinyMCE3Test.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToCMSPageTinyMCE3Test.xml index 11bf03c1d5ee9..69f9d24699db0 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToCMSPageTinyMCE3Test.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToCMSPageTinyMCE3Test.xml @@ -47,13 +47,13 @@ <click selector="{{MediaGallerySection.browseForImage}}" stepKey="clickBrowse"/> <switchToIFrame stepKey="switchOutOfIFrame"/> <waitForPageLoad stepKey="waitForPageToLoad" /> - <actionGroup ref="CreateImageFolder" stepKey="CreateImageFolder"> + <actionGroup ref="CreateImageFolderActionGroup" stepKey="CreateImageFolder"> <argument name="ImageFolder" value="ImageFolder"/> </actionGroup> - <actionGroup ref="attachImage" stepKey="attachImage1"> + <actionGroup ref="AttachImageActionGroup" stepKey="attachImage1"> <argument name="Image" value="ImageUpload"/> </actionGroup> - <actionGroup ref="saveImage" stepKey="insertImage"/> + <actionGroup ref="SaveImageActionGroup" stepKey="insertImage"/> <!-- Switching back to the Edit/Insert Image iFrame--> <comment userInput="switching back to iFrame" stepKey="switchBackToIFrame"/> <executeJS function="document.querySelector('.clearlooks2 iframe').setAttribute('name', 'insert-image');" stepKey="makeIFrameInteractable2"/> @@ -67,4 +67,4 @@ <click selector="{{CmsNewPagePageActionsSection.savePage}}" stepKey="clickSavePage"/> <see userInput="You saved the page." stepKey="seeSuccessMessage"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml index 03edc69e6d625..64b775c6cbef9 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml @@ -21,34 +21,34 @@ <createData entity="_defaultCmsPage" stepKey="createCMSPage" /> <createData entity="_defaultBlock" stepKey="createPreReqBlock" /> <actionGroup ref="LoginActionGroup" stepKey="login"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <actionGroup ref="AssignBlockToCMSPage" stepKey="assignBlockToCMSPage"> <argument name="Block" value="$$createPreReqBlock$$"/> <argument name="CmsPage" value="$$createCMSPage$$"/> </actionGroup> - <actionGroup ref="navigateToCreatedCMSBlockPage" stepKey="navigateToCreatedCMSBlockPage1"> + <actionGroup ref="NavigateToCreatedCMSBlockPageActionGroup" stepKey="navigateToCreatedCMSBlockPage1"> <argument name="CMSBlockPage" value="$$createPreReqBlock$$"/> </actionGroup> <selectOption selector="{{BlockNewPageBasicFieldsSection.storeView}}" userInput="All Store View" stepKey="selectAllStoreView" /> <waitForElementVisible selector="{{TinyMCESection.TinyMCE4}}" stepKey="waitForTinyMCE" /> <click selector="{{TinyMCESection.InsertImageIcon}}" stepKey="clickInsertImageIcon" /> <waitForPageLoad stepKey="waitForPageLoad2" /> - <actionGroup ref="clickBrowseBtnOnUploadPopup" stepKey="clickBrowserBtn"/> - <actionGroup ref="VerifyMediaGalleryStorageActions" stepKey="VerifyMediaGalleryStorageBtn"/> - <actionGroup ref="CreateImageFolder" stepKey="CreateImageFolder"> + <actionGroup ref="ClickBrowseBtnOnUploadPopupActionGroup" stepKey="clickBrowserBtn"/> + <actionGroup ref="VerifyMediaGalleryStorageActionsActionGroup" stepKey="VerifyMediaGalleryStorageBtn"/> + <actionGroup ref="CreateImageFolderActionGroup" stepKey="CreateImageFolder"> <argument name="ImageFolder" value="ImageFolder"/> </actionGroup> - <actionGroup ref="attachImage" stepKey="attachImage1"> + <actionGroup ref="AttachImageActionGroup" stepKey="attachImage1"> <argument name="Image" value="ImageUpload"/> </actionGroup> - <actionGroup ref="deleteImage" stepKey="deleteImage"/> - <actionGroup ref="attachImage" stepKey="attachImage2"> + <actionGroup ref="DeleteImageActionGroup" stepKey="deleteImage"/> + <actionGroup ref="AttachImageActionGroup" stepKey="attachImage2"> <argument name="Image" value="ImageUpload"/> </actionGroup> - <actionGroup ref="saveImage" stepKey="insertImage"/> - <actionGroup ref="fillOutUploadImagePopup" stepKey="fillOutUploadImagePopup" /> + <actionGroup ref="SaveImageActionGroup" stepKey="insertImage"/> + <actionGroup ref="FillOutUploadImagePopupActionGroup" stepKey="fillOutUploadImagePopup" /> <click selector="{{BlockNewPagePageActionsSection.saveBlock}}" stepKey="clickSaveBlock"/> <amOnPage url="$$createCMSPage.identifier$$" stepKey="amOnPageTestPage"/> <waitForPageLoad stepKey="waitForPageLoad11" /> @@ -62,7 +62,7 @@ <waitForPageLoad stepKey="waitForGridReload"/> <deleteData createDataKey="createPreReqBlock" stepKey="deletePreReqBlock" /> <deleteData createDataKey="createCMSPage" stepKey="deletePreReqCMSPage" /> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml index 205850f888797..dd45c6768e67f 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml @@ -20,30 +20,30 @@ <before> <createData entity="_defaultCmsPage" stepKey="createCMSPage" /> <actionGroup ref="LoginActionGroup" stepKey="login"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> - <actionGroup ref="navigateToCreatedCMSPage" stepKey="navigateToCreatedCMSPage"> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage"> <argument name="CMSPage" value="$$createCMSPage$$"/> </actionGroup> <click selector="{{CmsNewPagePageContentSection.header}}" stepKey="clickExpandContent"/> <waitForElementVisible selector="{{TinyMCESection.TinyMCE4}}" stepKey="waitForTinyMCE4" /> <click selector="{{TinyMCESection.InsertImageIcon}}" stepKey="clickInsertImageIcon" /> <waitForPageLoad stepKey="waitForPageLoad" /> - <actionGroup ref="clickBrowseBtnOnUploadPopup" stepKey="clickBrowserBtn"/> - <actionGroup ref="VerifyMediaGalleryStorageActions" stepKey="VerifyMediaGalleryStorageBtn"/> - <actionGroup ref="CreateImageFolder" stepKey="CreateImageFolder"> + <actionGroup ref="ClickBrowseBtnOnUploadPopupActionGroup" stepKey="clickBrowserBtn"/> + <actionGroup ref="VerifyMediaGalleryStorageActionsActionGroup" stepKey="VerifyMediaGalleryStorageBtn"/> + <actionGroup ref="CreateImageFolderActionGroup" stepKey="CreateImageFolder"> <argument name="ImageFolder" value="ImageFolder"/> </actionGroup> - <actionGroup ref="attachImage" stepKey="attachImage1"> + <actionGroup ref="AttachImageActionGroup" stepKey="attachImage1"> <argument name="Image" value="ImageUpload3"/> </actionGroup> - <actionGroup ref="deleteImage" stepKey="deleteImage"/> - <actionGroup ref="attachImage" stepKey="attachImage2"> + <actionGroup ref="DeleteImageActionGroup" stepKey="deleteImage"/> + <actionGroup ref="AttachImageActionGroup" stepKey="attachImage2"> <argument name="Image" value="ImageUpload3"/> </actionGroup> - <actionGroup ref="saveImage" stepKey="insertImage"/> - <actionGroup ref="fillOutUploadImagePopup" stepKey="fillOutUploadImagePopup" /> + <actionGroup ref="SaveImageActionGroup" stepKey="insertImage"/> + <actionGroup ref="FillOutUploadImagePopupActionGroup" stepKey="fillOutUploadImagePopup" /> <click selector="{{CmsNewPagePageSeoSection.header}}" stepKey="clickExpandSearchEngineOptimisation"/> <fillField selector="{{CmsNewPagePageSeoSection.urlKey}}" userInput="$$createCMSPage.identifier$$" stepKey="fillFieldUrlKey"/> <click selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="expandButtonMenu"/> @@ -56,7 +56,7 @@ <seeElementInDOM selector="{{StorefrontCMSPageSection.imageSource(ImageUpload3.fileName)}}" stepKey="assertMediaSource"/> <after> <deleteData createDataKey="createCMSPage" stepKey="deletePreReqCMSPage" /> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml index ce34a8d09c302..cc7f59c7a9478 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml @@ -21,7 +21,7 @@ <createData entity="_defaultCmsPage" stepKey="createCMSPage" /> <createData entity="_defaultBlock" stepKey="createPreReqBlock" /> <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <!--Create Custom Variable--> @@ -38,7 +38,7 @@ <argument name="Block" value="$$createPreReqBlock$$"/> <argument name="CmsPage" value="$$createCMSPage$$"/> </actionGroup> - <actionGroup ref="navigateToCreatedCMSBlockPage" stepKey="navigateToCreatedCMSBlockPage1"> + <actionGroup ref="NavigateToCreatedCMSBlockPageActionGroup" stepKey="navigateToCreatedCMSBlockPage1"> <argument name="CMSBlockPage" value="$$createPreReqBlock$$"/> </actionGroup> <selectOption selector="{{BlockNewPageBasicFieldsSection.storeView}}" userInput="All Store View" stepKey="selectAllStoreView" /> @@ -142,7 +142,7 @@ <waitForPageLoad stepKey="waitForGridReload"/> <deleteData createDataKey="createPreReqBlock" stepKey="deletePreReqBlock" /> <deleteData createDataKey="createCMSPage" stepKey="deletePreReqCMSPage" /> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGCMSTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGCMSTest.xml index 3b501859e606e..92164f34f5a83 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGCMSTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGCMSTest.xml @@ -18,7 +18,7 @@ </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <!--Create Custom Variable--> @@ -94,7 +94,7 @@ <!--see custom variable blank--> <dontSee userInput="{{customVariable.html}}" stepKey="dontSeeCustomVariableName" /> <after> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGBlockTest.xml index ad5e769c61be4..24440a6164e84 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGBlockTest.xml @@ -21,10 +21,10 @@ <createData entity="_defaultCmsPage" stepKey="createCMSPage" /> <createData entity="_defaultBlock" stepKey="createPreReqBlock" /> <actionGroup ref="LoginActionGroup" stepKey="login"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> - <actionGroup ref="navigateToCreatedCMSBlockPage" stepKey="navigateToCreatedCMSBlockPage"> + <actionGroup ref="NavigateToCreatedCMSBlockPageActionGroup" stepKey="navigateToCreatedCMSBlockPage"> <argument name="CMSBlockPage" value="$$createPreReqBlock$$"/> </actionGroup> <selectOption selector="{{BlockNewPageBasicFieldsSection.storeView}}" userInput="All Store View" stepKey="selectAllStoreView" /> @@ -45,7 +45,7 @@ <waitForElementNotVisible selector="{{WidgetSection.InsertWidgetTitle}}" stepKey="waitForSlideOutCloses1" /> <click selector="{{BlockNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitButton"/> <click selector="{{BlockNewPagePageActionsSection.saveAndClose}}" stepKey="clickSaveBlock"/> - <actionGroup ref="navigateToCreatedCMSPage" stepKey="navigateToCreatedCMSPage"> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage"> <argument name="CMSPage" value="$$createCMSPage$$"/> </actionGroup> <waitForElementVisible selector="{{TinyMCESection.TinyMCE4}}" stepKey="waitForTinyMCE4"/> @@ -80,7 +80,7 @@ <waitForPageLoad stepKey="waitForGridReload"/> <deleteData createDataKey="createCMSPage" stepKey="deletePreReqCMSPage" /> <deleteData createDataKey="createPreReqBlock" stepKey="deletePreReqBlock" /> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml index 1adb781a67536..53ed63c89bfb6 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="login"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <!--Main test--> @@ -66,7 +66,7 @@ <!--see widget on Storefront--> <see userInput="Home page" stepKey="seeHomepageWidget" /> <after> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSStaticBlockTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSStaticBlockTypeTest.xml index f37038435e109..c199601231a13 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSStaticBlockTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSStaticBlockTypeTest.xml @@ -21,7 +21,7 @@ <before> <createData entity="_defaultBlock" stepKey="createPreReqBlock" /> <actionGroup ref="LoginActionGroup" stepKey="login"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <!--Main test--> @@ -68,7 +68,7 @@ <see userInput="$$createPreReqBlock.content$$" stepKey="seeBlockLink"/> <after> <deleteData createDataKey="createPreReqBlock" stepKey="deletePreReqBlock" /> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml index 552eae407391d..0237d3704f7c1 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml @@ -20,7 +20,7 @@ <before> <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> <actionGroup ref="LoginActionGroup" stepKey="login"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> <actionGroup ref="ConfigAdminAccountSharingActionGroup" stepKey="allowAdminShareAccount"/> </before> @@ -75,7 +75,7 @@ stepKey="seeProductLinkInCategory"/> <after> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCatalog" /> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> <actionGroup ref="logout" stepKey="logout"/> </after> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml index d75d422afa2a4..4e7ecd48a6af4 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml @@ -24,7 +24,7 @@ <requiredEntity createDataKey="createPreReqCategory"/> </createData> <actionGroup ref="LoginActionGroup" stepKey="login"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <!--Main test--> @@ -82,7 +82,7 @@ <after> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCatalog" /> <deleteData createDataKey="createPreReqProduct" stepKey="deletePreReqProduct" /> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> <actionGroup ref="logout" stepKey="logout"/> </after> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml index 394d79bda1ab3..2cd41eb9d9f7b 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml @@ -26,7 +26,7 @@ <requiredEntity createDataKey="createPreReqCategory"/> </createData> <actionGroup ref="LoginActionGroup" stepKey="login"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <!--Main test--> @@ -99,7 +99,7 @@ <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCatalog" /> <deleteData createDataKey="createPreReqProduct1" stepKey="deletePreReqProduct1" /> <deleteData createDataKey="createPreReqProduct2" stepKey="deletePreReqProduct2" /> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml index 862f51ea72fad..803a3b692dbe2 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml @@ -25,7 +25,7 @@ <requiredEntity createDataKey="createPreReqCategory"/> </createData> <actionGroup ref="LoginActionGroup" stepKey="login"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <amOnPage url="{{CmsNewPagePage.url}}" stepKey="navigateToPage"/> @@ -82,7 +82,7 @@ <after> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCatalog" /> <deleteData createDataKey="createPreReqProduct" stepKey="deletePreReqProduct" /> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest.xml index 298aed917fc18..2c3b060d3bc15 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest.xml @@ -24,7 +24,7 @@ <requiredEntity createDataKey="createPreReqCategory"/> </createData> <actionGroup ref="LoginActionGroup" stepKey="login"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <!--Main test--> @@ -72,7 +72,7 @@ <after> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCatalog" /> <deleteData createDataKey="createPreReqProduct" stepKey="deletePreReqProduct" /> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsBlockTest.xml index 7ab0d9209dde3..ef4a7575c35d3 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsBlockTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> </before> <after> <actionGroup ref="deleteBlock" stepKey="deleteCreatedBlock"> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsPageTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsPageTest.xml index b7c7e4a4212fe..db8b5606cd398 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsPageTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsPageTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> </before> <after> <actionGroup ref="logout" stepKey="adminLogout"/> @@ -90,7 +90,7 @@ </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> </before> <after> <actionGroup ref="logout" stepKey="logout"/> @@ -110,7 +110,7 @@ <see userInput="You duplicated the page." stepKey="seeDuplicatedPageMsg"/> <!--Verify duplicated CMS Page--> <seeElement selector="{{BlockNewPageBasicFieldsSection.isActive('0')}}" stepKey="seeBlockNotEnable" /> - <actionGroup ref="AssertCMSPageContent" stepKey="assertContent"/> + <actionGroup ref="AssertCMSPageContentActionGroup" stepKey="assertContent"/> <click selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtn3" /> <click selector="{{CmsNewPagePageActionsSection.saveAndClose}}" stepKey="clickSaveAndClose"/> <see userInput="You saved the page." stepKey="seeSavedCMSPageMsgOnGrid"/> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminMediaGalleryPopupUploadImagesWithoutErrorTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminMediaGalleryPopupUploadImagesWithoutErrorTest.xml index bc1688c9d692b..ff389a6ce2965 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminMediaGalleryPopupUploadImagesWithoutErrorTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminMediaGalleryPopupUploadImagesWithoutErrorTest.xml @@ -37,7 +37,7 @@ </after> <!--Open created block page and add image--> <comment userInput="Open create block page and add image" stepKey="commentOpenBlockPage"/> - <actionGroup ref="navigateToCreatedCMSBlockPage" stepKey="navigateToCreatedCMSBlockPage1"> + <actionGroup ref="NavigateToCreatedCMSBlockPageActionGroup" stepKey="navigateToCreatedCMSBlockPage1"> <argument name="CMSBlockPage" value="$$createBlock$$"/> </actionGroup> <actionGroup ref="AdminAddImageToCMSBlockContent" stepKey="addImage"> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/CheckOrderOfProdsInWidgetOnCMSPageTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/CheckOrderOfProdsInWidgetOnCMSPageTest.xml new file mode 100644 index 0000000000000..afcf5e3764ef7 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/CheckOrderOfProdsInWidgetOnCMSPageTest.xml @@ -0,0 +1,138 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CheckOrderOfProdsInWidgetOnCMSPageTest"> + <annotations> + <features value="Catalog"/> + <stories value="Widgets"/> + <title value="Checking order of products in a widget on a CMS page - SKU condition"/> + <description value="Checking order of products in a widget on a CMS page - SKU condition"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-13718"/> + <useCaseId value="MC-5906"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> + <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="enableTinyMCE4"/> + <waitForPageLoad stepKey="waitConfigToSave"/> + <createData entity="ApiCategory" stepKey="createFirstCategory"/> + <createData entity="ApiSimpleProduct" stepKey="product1"> + <requiredEntity createDataKey="createFirstCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="product2"> + <requiredEntity createDataKey="createFirstCategory"/> + </createData> + <createData entity="_defaultCmsPage" stepKey="createCMSPage"/> + </before> + <after> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> + <deleteData createDataKey="createFirstCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="product1" stepKey="deleteProduct1"/> + <deleteData createDataKey="product2" stepKey="deleteProduct2"/> + <deleteData createDataKey="createCMSPage" stepKey="deletePreReqCMSPage"/> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + </after> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage1"> + <argument name="CMSPage" value="$$createCMSPage$$"/> + </actionGroup> + <conditionalClick selector="{{CmsNewPagePageContentSection.header}}" + dependentSelector="{{CmsNewPagePageContentSection.header}}._show" visible="false" + stepKey="clickContentTab1"/> + <waitForPageLoad stepKey="waitForContentSectionLoad1"/> + <waitForElementNotVisible selector="{{CmsWYSIWYGSection.CheckIfTabExpand}}" stepKey="waitForTabExpand1"/> + <click selector="{{CmsNewPagePageActionsSection.showHideEditor}}" stepKey="showHiddenButtons"/> + <seeElement selector="{{TinyMCESection.InsertWidgetBtn}}" stepKey="seeWidgetButton"/> + <click selector="{{TinyMCESection.InsertWidgetBtn}}" stepKey="clickInsertWidgetButton"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <see userInput="Inserting a widget does not create a widget instance." stepKey="seeMessage"/> + <see selector="{{WidgetSection.InsertWidgetBtnDisabled}}" userInput="Insert Widget" + stepKey="seeInsertWidgetDisabled"/> + <see selector="{{WidgetSection.CancelBtnEnabled}}" userInput="Cancel" stepKey="seeCancelBtnEnabled"/> + <selectOption selector="{{WidgetSection.WidgetType}}" userInput="Catalog Products List" + stepKey="selectCatalogProductsList"/> + <waitForPageLoad stepKey="waitBeforeClickingOnAddParamBtn"/> + <click selector="{{WidgetSection.AddParam}}" stepKey="clickAddParamBtn"/> + <waitForElement selector="{{WidgetSection.ConditionsDropdown}}" stepKey="addingWaitForConditionsDropDown"/> + <waitForElementVisible selector="{{WidgetSection.ConditionsDropdown}}" stepKey="waitForDropdownVisible"/> + <selectOption selector="{{WidgetSection.ConditionsDropdown}}" userInput="SKU" + stepKey="selectCategoryCondition"/> + <waitForPageLoad stepKey="waitBeforeClickingOnRuleParam"/> + <click selector="{{WidgetSection.RuleParam1('3')}}" stepKey="clickOnRuleParam1"/> + <waitForElementVisible selector="{{WidgetSection.RuleParamSelect('1','1')}}" + stepKey="waitDropdownToAppear"/> + <selectOption selector="{{WidgetSection.RuleParamSelect('1','1')}}" userInput="is one of" + stepKey="selectOption"/> + <waitForElement selector="{{WidgetSection.RuleParam}}" stepKey="waitForRuleParam"/> + <click selector="{{WidgetSection.RuleParam}}" stepKey="clickOnRuleParam"/> + <waitForElementVisible selector="{{WidgetSection.Chooser}}" stepKey="waitForElement"/> + <click selector="{{WidgetSection.Chooser}}" stepKey="clickChooser"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <fillField selector="{{WidgetSection.ChooserName}}" userInput="$$product1.name$$" + stepKey="fillProduct1Name"/> + <waitForPageLoad stepKey="waitForPageToLoadBeforeClickingOnSearchFilter1"/> + <click selector="{{AdminNewWidgetSection.searchBlock}}" stepKey="searchFilter1"/> + <waitForPageLoad stepKey="waitForPageToLoadBeforeSelectingProduct"/> + <click selector="{{WidgetSection.PreCreateProduct('$$product1.name$$')}}" stepKey="selectProduct1"/> + <click selector="{{AdminWidgetsSection.resetFilter}}" stepKey="resetFilter1"/> + <waitForPageLoad stepKey="waitForPageToLoadBeforeFillingProductName"/> + <fillField selector="{{WidgetSection.ChooserName}}" userInput="$$product2.name$$" + stepKey="fillProduct2Name"/> + <click selector="{{AdminNewWidgetSection.searchBlock}}" stepKey="clickOnSearch"/> + <waitForPageLoad stepKey="waitForPageToLoadBeforeSelectingProduct2"/> + <click selector="{{WidgetSection.PreCreateProduct('$$product2.name$$')}}" stepKey="selectProduct2"/> + <click selector="{{AdminNewWidgetSection.applyParameter}}" stepKey="applyProducts"/> + <click selector="{{WidgetSection.InsertWidget}}" stepKey="clickOnInsertWidgetButton"/> + <waitForPageLoad stepKey="waitForPageToLoadBeforeClickingOnSaveWidget1"/> + <click selector="{{InsertWidgetSection.save}}" stepKey="saveWidget"/> + <waitForPageLoad stepKey="waitForSaveComplete"/> + <actionGroup ref="CompareTwoProductsOrder" stepKey="compareProductOrders1"> + <argument name="page" value="$$createCMSPage.identifier$$"/> + <argument name="product_1" value="$$product1$$"/> + <argument name="product_2" value="$$product2$$"/> + </actionGroup> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage2"> + <argument name="CMSPage" value="$$createCMSPage$$"/> + </actionGroup> + <conditionalClick selector="{{CmsNewPagePageContentSection.header}}" + dependentSelector="{{CmsNewPagePageContentSection.header}}._show" visible="false" + stepKey="clickContentTab2"/> + <waitForPageLoad stepKey="waitForContentSectionLoad2"/> + <waitForElementNotVisible selector="{{CmsWYSIWYGSection.CheckIfTabExpand}}" stepKey="waitForTabExpand2"/> + <executeJS function="jQuery('[id=\'cms_page_form_content_ifr\']').attr('name', 'preview-iframe')" + stepKey="setPreviewFrameName"/> + <switchToIFrame selector="preview-iframe" stepKey="switchToIframe"/> + <doubleClick selector="{{TinyMCESection.WidgetButton}}" stepKey="clickToEditWidget"/> + <switchToIFrame stepKey="switchOutFromIframe"/> + <waitForPageLoad stepKey="waitForPageToLoadBeforeOpeningProductsList"/> + <click selector="{{WidgetSection.RuleParam1('4')}}" stepKey="openProductsList"/> + <waitForElementVisible selector="{{WidgetSection.Chooser}}" stepKey="waitForElement2"/> + <click selector="{{WidgetSection.Chooser}}" stepKey="clickChooser2"/> + <waitForPageLoad stepKey="waitForPageToLoadBeforeFillingProduct1Name"/> + <fillField selector="{{WidgetSection.ChooserName}}" userInput="$$product1.name$$" stepKey="fillProduct1Name_2"/> + <waitForPageLoad stepKey="waitForPageToLoadBeforeClickingOnSearchFilter2"/> + <click selector="{{AdminNewWidgetSection.searchBlock}}" stepKey="searchFilter2"/> + <waitForPageLoad stepKey="waitForPageToLoadBeforeSelectingProduct1"/> + <click selector="{{WidgetSection.PreCreateProduct('$$product1.name$$')}}" stepKey="selectProduct1_1"/> + <click selector="{{WidgetSection.PreCreateProduct('$$product1.name$$')}}" stepKey="selectProduct2_2"/> + <click selector="{{AdminNewWidgetSection.applyParameter}}" stepKey="applyProducts1"/> + <click selector="{{WidgetSection.InsertWidget}}" stepKey="clickOnInsertWidgetButton1"/> + <waitForPageLoad stepKey="waitForPageToLoadBeforeClickingOnSaveWidget2"/> + <click selector="{{InsertWidgetSection.save}}" stepKey="saveWidget1"/> + <waitForPageLoad stepKey="waitForSaveComplete1"/> + + <actionGroup ref="CompareTwoProductsOrder" stepKey="compareProductOrders2"> + <argument name="page" value="$$createCMSPage.identifier$$"/> + <argument name="product_1" value="$$product2$$"/> + <argument name="product_2" value="$$product1$$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/StoreViewLanguageCorrectSwitchTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/StoreViewLanguageCorrectSwitchTest.xml index d4796b2ef7d43..6569c07f4580a 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/StoreViewLanguageCorrectSwitchTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/StoreViewLanguageCorrectSwitchTest.xml @@ -41,7 +41,7 @@ </actionGroup> <!-- Add StoreView To Cms Page--> - <actionGroup ref="AddStoreViewToCmsPage" stepKey="gotToCmsPage"> + <actionGroup ref="AddStoreViewToCmsPageActionGroup" stepKey="gotToCmsPage"> <argument name="CMSPage" value="$$createSecondCmsPage$$"/> <argument name="storeViewName" value="{{NewStoreViewData.name}}"/> </actionGroup> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnBlockTest.xml index 9ee2055aae650..944cd02d87b86 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnBlockTest.xml @@ -21,7 +21,7 @@ <before> <createData entity="_defaultCmsPage" stepKey="createPreReqCMSPage" /> <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <amOnPage url="{{CmsNewBlock.url}}" stepKey="amOnNewBlockPage"/> @@ -90,7 +90,7 @@ <conditionalClick selector="{{CmsPagesPageActionsSection.clearAllButton}}" dependentSelector="{{CmsPagesPageActionsSection.activeFilters}}" stepKey="clickToResetFilter" visible="true"/> <waitForPageLoad stepKey="waitForGridReload"/> <deleteData createDataKey="createPreReqCMSPage" stepKey="deletePreReqCMSPage" /> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCMSPageTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCMSPageTest.xml index caad1cabe78c5..24377e92fe083 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCMSPageTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnCMSPageTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <amOnPage url="{{CmsPagesPage.url}}" stepKey="amOnPagePagesGrid"/> @@ -50,7 +50,7 @@ <see userInput="{{_defaultCmsPage.content_heading}}" stepKey="seeContentHeading"/> <see userInput="Hello World!" stepKey="seeContent" /> <after> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/SaveTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/SaveTest.php index 0d44f66048ba3..c5bb26d04c734 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/SaveTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Block/SaveTest.php @@ -373,6 +373,58 @@ public function testSaveAndClose() $this->assertSame($this->resultRedirect, $this->saveController->execute()); } + public function testSaveActionWithMarginalSpace() + { + $postData = [ + 'title' => 'unique_title_123', + 'identifier' => ' unique_title_123', + 'stores' => ['0'], + 'is_active' => true, + 'content' => '', + 'back' => 'continue' + ]; + + $this->requestMock->expects($this->any())->method('getPostValue')->willReturn($postData); + $this->requestMock->expects($this->atLeastOnce()) + ->method('getParam') + ->willReturnMap( + [ + ['block_id', null, 1], + ['back', null, true], + ] + ); + + $this->blockFactory->expects($this->atLeastOnce()) + ->method('create') + ->willReturn($this->blockMock); + + $this->blockRepository->expects($this->once()) + ->method('getById') + ->with($this->blockId) + ->willReturn($this->blockMock); + + $this->blockMock->expects($this->once())->method('setData'); + $this->blockRepository->expects($this->once())->method('save') + ->with($this->blockMock) + ->willThrowException(new \Exception('No marginal white space please.')); + + $this->messageManagerMock->expects($this->never()) + ->method('addSuccessMessage'); + $this->messageManagerMock->expects($this->once()) + ->method('addExceptionMessage'); + + $this->dataPersistorMock->expects($this->any()) + ->method('set') + ->with('cms_block', array_merge($postData, ['block_id' => null])); + + $this->resultRedirect->expects($this->atLeastOnce()) + ->method('setPath') + ->with('*/*/edit', ['block_id' => $this->blockId]) + ->willReturnSelf(); + + $this->assertSame($this->resultRedirect, $this->saveController->execute()); + } + public function testSaveActionThrowsException() { $postData = [ diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php index 26b4055923107..91adcdd6db4c8 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php @@ -157,6 +157,7 @@ public function testSaveAction() $this->pageRepository->expects($this->once())->method('getById')->with($this->pageId)->willReturn($page); $page->expects($this->once())->method('setData'); + $page->method('getId')->willReturn($this->pageId); $this->pageRepository->expects($this->once())->method('save')->with($page); $this->dataPersistorMock->expects($this->any()) @@ -235,6 +236,7 @@ public function testSaveAndContinue() $page = $this->getMockBuilder(\Magento\Cms\Model\Page::class) ->disableOriginalConstructor() ->getMock(); + $page->method('getId')->willReturn(1); $this->pageFactory->expects($this->atLeastOnce()) ->method('create') ->willReturn($page); @@ -293,7 +295,14 @@ public function testSaveActionThrowsException() $this->dataPersistorMock->expects($this->any()) ->method('set') - ->with('cms_page', ['page_id' => $this->pageId]); + ->with( + 'cms_page', + [ + 'page_id' => $this->pageId, + 'layout_update_xml' => null, + 'custom_layout_update_xml' => null + ] + ); $this->resultRedirect->expects($this->atLeastOnce()) ->method('setPath') diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php index 16b218ebf6493..5fea276225622 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php @@ -151,7 +151,11 @@ protected function setUp() [ 'context' => $this->actionContextMock, 'urlDecoder' => $this->urlDecoderMock, - 'resultRawFactory' => $this->rawFactoryMock + 'resultRawFactory' => $this->rawFactoryMock, + 'adapterFactory' => $this->imageAdapterFactoryMock, + 'logger' => $this->loggerMock, + 'config' => $this->wysiwygConfigMock, + 'filter' => $this->templateFilterMock ] ); } @@ -228,7 +232,7 @@ public function testExecuteException() ->method('getImage') ->willReturn($imageBody); $this->loggerMock->expects($this->once()) - ->method('critical') + ->method('warning') ->with($exception); $this->rawFactoryMock->expects($this->any()) ->method('create') @@ -253,25 +257,46 @@ protected function prepareExecuteTest() ->method('decode') ->with($directiveParam) ->willReturn($directive); - $this->objectManagerMock->expects($this->once()) - ->method('create') - ->with(\Magento\Cms\Model\Template\Filter::class) - ->willReturn($this->templateFilterMock); + $this->templateFilterMock->expects($this->once()) ->method('filter') ->with($directive) ->willReturn(self::IMAGE_PATH); - $this->objectManagerMock->expects($this->any()) - ->method('get') - ->willReturnMap( - [ - [\Magento\Framework\Image\AdapterFactory::class, $this->imageAdapterFactoryMock], - [\Magento\Cms\Model\Wysiwyg\Config::class, $this->wysiwygConfigMock], - [\Psr\Log\LoggerInterface::class, $this->loggerMock] - ] - ); + $this->imageAdapterFactoryMock->expects($this->once()) ->method('create') ->willReturn($this->imageAdapterMock); } + + /** + * Test Execute With Deleted Image + * + * @covers \Magento\Cms\Controller\Adminhtml\Wysiwyg\Directive::execute + */ + public function testExecuteWithDeletedImage() + { + $exception = new \Exception('epic fail'); + $placeholderPath = 'pub/static/adminhtml/Magento/backend/en_US/Magento_Cms/images/wysiwyg_skin_image.png'; + $this->prepareExecuteTest(); + + $this->imageAdapterMock->expects($this->any()) + ->method('open') + ->with(self::IMAGE_PATH) + ->willThrowException($exception); + + $this->wysiwygConfigMock->expects($this->once()) + ->method('getSkinImagePlaceholderPath') + ->willReturn($placeholderPath); + + $this->imageAdapterMock->expects($this->any()) + ->method('open') + ->with($placeholderPath) + ->willThrowException($exception); + + $this->loggerMock->expects($this->once()) + ->method('warning') + ->with($exception); + + $this->wysiwygDirective->execute(); + } } diff --git a/app/code/Magento/Cms/Test/Unit/Model/PageRepositoryTest.php b/app/code/Magento/Cms/Test/Unit/Model/PageRepositoryTest.php deleted file mode 100644 index 61001794e2a0b..0000000000000 --- a/app/code/Magento/Cms/Test/Unit/Model/PageRepositoryTest.php +++ /dev/null @@ -1,268 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Cms\Test\Unit\Model; - -use Magento\Cms\Model\PageRepository; -use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; - -/** - * Test for Magento\Cms\Model\PageRepository - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ -class PageRepositoryTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var PageRepository - */ - protected $repository; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Cms\Model\ResourceModel\Page - */ - protected $pageResource; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Cms\Model\Page - */ - protected $page; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Cms\Api\Data\PageInterface - */ - protected $pageData; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Cms\Api\Data\PageSearchResultsInterface - */ - protected $pageSearchResult; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Api\DataObjectHelper - */ - protected $dataHelper; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Reflection\DataObjectProcessor - */ - protected $dataObjectProcessor; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Cms\Model\ResourceModel\Page\Collection - */ - protected $collection; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Store\Model\StoreManagerInterface - */ - private $storeManager; - - /** - * @var CollectionProcessorInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $collectionProcessor; - - /** - * Initialize repository - */ - protected function setUp() - { - $this->pageResource = $this->getMockBuilder(\Magento\Cms\Model\ResourceModel\Page::class) - ->disableOriginalConstructor() - ->getMock(); - $this->dataObjectProcessor = $this->getMockBuilder(\Magento\Framework\Reflection\DataObjectProcessor::class) - ->disableOriginalConstructor() - ->getMock(); - $pageFactory = $this->getMockBuilder(\Magento\Cms\Model\PageFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $pageDataFactory = $this->getMockBuilder(\Magento\Cms\Api\Data\PageInterfaceFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $pageSearchResultFactory = $this->getMockBuilder(\Magento\Cms\Api\Data\PageSearchResultsInterfaceFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $collectionFactory = $this->getMockBuilder(\Magento\Cms\Model\ResourceModel\Page\CollectionFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $store = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $store->expects($this->any())->method('getId')->willReturn(0); - $this->storeManager->expects($this->any())->method('getStore')->willReturn($store); - - $this->page = $this->getMockBuilder(\Magento\Cms\Model\Page::class)->disableOriginalConstructor()->getMock(); - $this->pageData = $this->getMockBuilder(\Magento\Cms\Api\Data\PageInterface::class) - ->getMock(); - $this->pageSearchResult = $this->getMockBuilder(\Magento\Cms\Api\Data\PageSearchResultsInterface::class) - ->getMock(); - $this->collection = $this->getMockBuilder(\Magento\Cms\Model\ResourceModel\Page\Collection::class) - ->disableOriginalConstructor() - ->setMethods(['getSize', 'setCurPage', 'setPageSize', 'load', 'addOrder']) - ->getMock(); - - $pageFactory->expects($this->any()) - ->method('create') - ->willReturn($this->page); - $pageDataFactory->expects($this->any()) - ->method('create') - ->willReturn($this->pageData); - $pageSearchResultFactory->expects($this->any()) - ->method('create') - ->willReturn($this->pageSearchResult); - $collectionFactory->expects($this->any()) - ->method('create') - ->willReturn($this->collection); - /** - * @var \Magento\Cms\Model\PageFactory $pageFactory - * @var \Magento\Cms\Api\Data\PageInterfaceFactory $pageDataFactory - * @var \Magento\Cms\Api\Data\PageSearchResultsInterfaceFactory $pageSearchResultFactory - * @var \Magento\Cms\Model\ResourceModel\Page\CollectionFactory $collectionFactory - */ - - $this->dataHelper = $this->getMockBuilder(\Magento\Framework\Api\DataObjectHelper::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->collectionProcessor = $this->getMockBuilder(CollectionProcessorInterface::class) - ->getMockForAbstractClass(); - - $this->repository = new PageRepository( - $this->pageResource, - $pageFactory, - $pageDataFactory, - $collectionFactory, - $pageSearchResultFactory, - $this->dataHelper, - $this->dataObjectProcessor, - $this->storeManager, - $this->collectionProcessor - ); - } - - /** - * @test - */ - public function testSave() - { - $this->pageResource->expects($this->once()) - ->method('save') - ->with($this->page) - ->willReturnSelf(); - $this->assertEquals($this->page, $this->repository->save($this->page)); - } - - /** - * @test - */ - public function testDeleteById() - { - $pageId = '123'; - - $this->page->expects($this->once()) - ->method('getId') - ->willReturn(true); - $this->page->expects($this->once()) - ->method('load') - ->with($pageId) - ->willReturnSelf(); - $this->pageResource->expects($this->once()) - ->method('delete') - ->with($this->page) - ->willReturnSelf(); - - $this->assertTrue($this->repository->deleteById($pageId)); - } - - /** - * @test - * - * @expectedException \Magento\Framework\Exception\CouldNotSaveException - */ - public function testSaveException() - { - $this->pageResource->expects($this->once()) - ->method('save') - ->with($this->page) - ->willThrowException(new \Exception()); - $this->repository->save($this->page); - } - - /** - * @test - * - * @expectedException \Magento\Framework\Exception\CouldNotDeleteException - */ - public function testDeleteException() - { - $this->pageResource->expects($this->once()) - ->method('delete') - ->with($this->page) - ->willThrowException(new \Exception()); - $this->repository->delete($this->page); - } - - /** - * @test - * - * @expectedException \Magento\Framework\Exception\NoSuchEntityException - */ - public function testGetByIdException() - { - $pageId = '123'; - - $this->page->expects($this->once()) - ->method('getId') - ->willReturn(false); - $this->page->expects($this->once()) - ->method('load') - ->with($pageId) - ->willReturnSelf(); - $this->repository->getById($pageId); - } - - /** - * @test - */ - public function testGetList() - { - $total = 10; - - /** @var \Magento\Framework\Api\SearchCriteriaInterface $criteria */ - $criteria = $this->getMockBuilder(\Magento\Framework\Api\SearchCriteriaInterface::class)->getMock(); - - $this->collection->addItem($this->page); - $this->collection->expects($this->once()) - ->method('getSize') - ->willReturn($total); - - $this->collectionProcessor->expects($this->once()) - ->method('process') - ->with($criteria, $this->collection) - ->willReturnSelf(); - - $this->pageSearchResult->expects($this->once()) - ->method('setSearchCriteria') - ->with($criteria) - ->willReturnSelf(); - $this->pageSearchResult->expects($this->once()) - ->method('setTotalCount') - ->with($total) - ->willReturnSelf(); - $this->pageSearchResult->expects($this->once()) - ->method('setItems') - ->with([$this->page]) - ->willReturnSelf(); - $this->assertEquals($this->pageSearchResult, $this->repository->getList($criteria)); - } -} diff --git a/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php b/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php index ed87c66b6e1c5..9a9c63195051c 100644 --- a/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php +++ b/app/code/Magento/Cms/Test/Unit/Model/Wysiwyg/Images/StorageTest.php @@ -7,6 +7,7 @@ use Magento\Cms\Model\Wysiwyg\Images\Storage\Collection as StorageCollection; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Exception\LocalizedException; /** * @SuppressWarnings(PHPMD.LongVariable) @@ -454,7 +455,7 @@ protected function generalTestGetDirsCollection($path, $collectionArray = [], $e $storageCollectionMock->expects($this->once()) ->method('getIterator') ->willReturn(new \ArrayIterator($collectionArray)); - $storageCollectionInvMock = $storageCollectionMock->expects($this->exactly(sizeof($expectedRemoveKeys))) + $storageCollectionInvMock = $storageCollectionMock->expects($this->exactly(count($expectedRemoveKeys))) ->method('removeItemByKey'); call_user_func_array([$storageCollectionInvMock, 'withConsecutive'], $expectedRemoveKeys); @@ -539,4 +540,18 @@ public function testUploadFile() $this->assertEquals($result, $this->imagesStorage->uploadFile($targetPath, $type)); } + + /** + * Test create directory with invalid name + */ + public function testCreateDirectoryWithInvalidName() + { + $name = 'папка'; + $path = '/tmp/path'; + $this->expectException(LocalizedException::class); + $this->expectExceptionMessage( + (string)__('Please rename the folder using only Latin letters, numbers, underscores and dashes.') + ); + $this->imagesStorage->createDirectory($name, $path); + } } diff --git a/app/code/Magento/Cms/Test/Unit/ViewModel/Page/Grid/UrlBuilderTest.php b/app/code/Magento/Cms/Test/Unit/ViewModel/Page/Grid/UrlBuilderTest.php new file mode 100644 index 0000000000000..fbb2fb1eb65c5 --- /dev/null +++ b/app/code/Magento/Cms/Test/Unit/ViewModel/Page/Grid/UrlBuilderTest.php @@ -0,0 +1,194 @@ +<?php +/*** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Cms\Test\Unit\ViewModel\Page\Grid; + +use Magento\Cms\ViewModel\Page\Grid\UrlBuilder; +use Magento\Framework\Url\EncoderInterface; +use Magento\Framework\UrlInterface; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Class UrlBuilderTest + * + * Testing the UrlBuilder + */ +class UrlBuilderTest extends TestCase +{ + /** + * @var UrlBuilder + */ + private $viewModel; + + /** + * @var UrlInterface|MockObject + */ + private $frontendUrlBuilderMock; + + /** + * @var EncoderInterface|MockObject + */ + private $urlEncoderMock; + + /** + * @var StoreManagerInterface|MockObject + */ + private $storeManagerMock; + + /** + * Set Up + */ + public function setUp() + { + $this->frontendUrlBuilderMock = $this->getMockBuilder(UrlInterface::class) + ->setMethods(['getUrl', 'setScope']) + ->getMockForAbstractClass(); + $this->urlEncoderMock = $this->createMock(EncoderInterface::class); + $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->viewModel = new UrlBuilder( + $this->frontendUrlBuilderMock, + $this->urlEncoderMock, + $this->storeManagerMock + ); + } + + /** + * Testing url builder with no scope provided + * + * @dataProvider nonScopedUrlsDataProvider + * + * @param array $url + * @param string $expected + * @param string $store + * @param null $scope + */ + public function testUrlBuilderWithNoScope(array $url, string $expected, string $store, $scope = null) + { + $this->frontendUrlBuilderMock->expects($this->any()) + ->method('getUrl') + ->with($url['path'], $url['params']) + ->willReturn($expected); + + $result = $this->viewModel->getUrl($url['path'], $scope, $store); + + $this->assertSame($expected, $result); + } + + /** + * Providing a non scoped urls + * + * @return array + */ + public function nonScopedUrlsDataProvider(): array + { + return [ + [ + [ + 'path' => 'test/view', + 'params' => [ + '_current' => false, + '_nosid' => true + ] + ], + 'http://domain.com/test/view/', + 'en' + ] + ]; + } + + /** + * Testing url builder with a scope provided + * + * @dataProvider scopedUrlsDataProvider + * + * @param string $storeCode + * @param string $defaultStoreCode + * @param array $urlParams + * @param string $scope + */ + public function testScopedUrlBuilder( + string $storeCode, + string $defaultStoreCode, + array $urlParams, + string $scope = 'store' + ) { + /** @var StoreInterface|MockObject $storeMock */ + $storeMock = $this->createMock(StoreInterface::class); + $storeMock->expects($this->any()) + ->method('getCode') + ->willReturn($defaultStoreCode); + $this->storeManagerMock->expects($this->once()) + ->method('getDefaultStoreView') + ->willReturn($storeMock); + + $this->frontendUrlBuilderMock->expects($this->any()) + ->method('getUrl') + ->withConsecutive( + [ + 'test/index', + [ + '_current' => false, + '_nosid' => true, + '_query' => [ + StoreManagerInterface::PARAM_NAME => $storeCode + ] + ] + ], + [ + 'stores/store/switch', + $urlParams + ] + ) + ->willReturnOnConsecutiveCalls( + 'http://domain.com/test', + 'http://domain.com/test/index' + ); + + $result = $this->viewModel->getUrl('test/index', $scope, $storeCode); + + $this->assertSame('http://domain.com/test/index', $result); + } + + /** + * Providing a scoped urls + * + * @return array + */ + public function scopedUrlsDataProvider(): array + { + $enStoreCode = 'en'; + $frStoreCode = 'fr'; + $scopedDefaultUrlParams = $defaultUrlParams = [ + '_current' => false, + '_nosid' => true, + '_query' => [ + '___store' => $enStoreCode, + 'uenc' => null, + ] + ]; + $scopedDefaultUrlParams['_query']['___from_store'] = $frStoreCode; + + return [ + [ + $enStoreCode, + $enStoreCode, + $defaultUrlParams, + ], + [ + $enStoreCode, + $frStoreCode, + $scopedDefaultUrlParams + ] + ]; + } +} diff --git a/app/code/Magento/Cms/composer.json b/app/code/Magento/Cms/composer.json index d04587bbdd728..91036d31fdc2b 100644 --- a/app/code/Magento/Cms/composer.json +++ b/app/code/Magento/Cms/composer.json @@ -15,8 +15,7 @@ "magento/module-theme": "*", "magento/module-ui": "*", "magento/module-variable": "*", - "magento/module-widget": "*", - "magento/module-authorization": "*" + "magento/module-widget": "*" }, "suggest": { "magento/module-cms-sample-data": "*" diff --git a/app/code/Magento/Cms/etc/db_schema.xml b/app/code/Magento/Cms/etc/db_schema.xml index 1e64c905badd8..9ff3153098482 100644 --- a/app/code/Magento/Cms/etc/db_schema.xml +++ b/app/code/Magento/Cms/etc/db_schema.xml @@ -68,6 +68,8 @@ comment="Page Custom Template"/> <column xsi:type="text" name="custom_layout_update_xml" nullable="true" comment="Page Custom Layout Update Content"/> + <column xsi:type="varchar" name="layout_update_selected" nullable="true" + length="128" comment="Page Custom Layout File"/> <column xsi:type="date" name="custom_theme_from" comment="Page Custom Theme Active From Date"/> <column xsi:type="date" name="custom_theme_to" comment="Page Custom Theme Active To Date"/> <column xsi:type="varchar" name="meta_title" nullable="true" length="255" comment="Page Meta Title"/> diff --git a/app/code/Magento/Cms/etc/db_schema_whitelist.json b/app/code/Magento/Cms/etc/db_schema_whitelist.json index 8da44baf71719..fe0e9c1f2e22e 100644 --- a/app/code/Magento/Cms/etc/db_schema_whitelist.json +++ b/app/code/Magento/Cms/etc/db_schema_whitelist.json @@ -50,7 +50,8 @@ "custom_layout_update_xml": true, "custom_theme_from": true, "custom_theme_to": true, - "meta_title": true + "meta_title": true, + "layout_update_selected": true }, "index": { "CMS_PAGE_IDENTIFIER": true, diff --git a/app/code/Magento/Cms/etc/di.xml b/app/code/Magento/Cms/etc/di.xml index f510a0a026f7a..35fb7d82651f0 100644 --- a/app/code/Magento/Cms/etc/di.xml +++ b/app/code/Magento/Cms/etc/di.xml @@ -243,4 +243,11 @@ </argument> </arguments> </type> + <preference for="Magento\Cms\Model\Page\CustomLayoutManagerInterface" type="Magento\Cms\Model\Page\CustomLayout\CustomLayoutManager" /> + <type name="Magento\Cms\Model\Page\CustomLayout\CustomLayoutManager"> + <arguments> + <argument name="themeFactory" xsi:type="object">Magento\Framework\View\Design\Theme\FlyweightFactory\Proxy</argument> + </arguments> + </type> + <preference for="Magento\Cms\Model\Page\CustomLayoutRepositoryInterface" type="Magento\Cms\Model\Page\CustomLayout\CustomLayoutRepository" /> </config> diff --git a/app/code/Magento/Cms/etc/events.xml b/app/code/Magento/Cms/etc/events.xml index d6b9ad4ee0248..1ad847e215ccc 100644 --- a/app/code/Magento/Cms/etc/events.xml +++ b/app/code/Magento/Cms/etc/events.xml @@ -36,4 +36,7 @@ <event name="magento_cms_api_data_pageinterface_load_after"> <observer name="legacy_model_cms_page_after_load" instance="Magento\Framework\EntityManager\Observer\AfterEntityLoad" /> </event> + <event name="cms_page_prepare_save"> + <observer name="validate_cms_page" instance="Magento\Cms\Observer\PageValidatorObserver" /> + </event> </config> diff --git a/app/code/Magento/Cms/etc/webapi_rest/di.xml b/app/code/Magento/Cms/etc/webapi_rest/di.xml new file mode 100644 index 0000000000000..686305f2ed300 --- /dev/null +++ b/app/code/Magento/Cms/etc/webapi_rest/di.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" ?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Cms\Api\PageRepositoryInterface"> + <plugin name="aclCheck" type="Magento\Cms\Observer\PageAclPlugin" sortOrder="100" /> + </type> +</config> diff --git a/app/code/Magento/Cms/etc/webapi_soap/di.xml b/app/code/Magento/Cms/etc/webapi_soap/di.xml new file mode 100644 index 0000000000000..686305f2ed300 --- /dev/null +++ b/app/code/Magento/Cms/etc/webapi_soap/di.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" ?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Cms\Api\PageRepositoryInterface"> + <plugin name="aclCheck" type="Magento\Cms\Observer\PageAclPlugin" sortOrder="100" /> + </type> +</config> diff --git a/app/code/Magento/Cms/i18n/en_US.csv b/app/code/Magento/Cms/i18n/en_US.csv index 2947c567d75ff..b860a8fb1041e 100644 --- a/app/code/Magento/Cms/i18n/en_US.csv +++ b/app/code/Magento/Cms/i18n/en_US.csv @@ -76,6 +76,7 @@ Pages,Pages "The page URL key contains capital letters or disallowed symbols.","The page URL key contains capital letters or disallowed symbols." "The page URL key cannot be made of only numbers.","The page URL key cannot be made of only numbers." "Please rename the folder using only letters, numbers, underscores and dashes.","Please rename the folder using only letters, numbers, underscores and dashes." +"Please rename the folder using only Latin letters, numbers, underscores and dashes.","Please rename the folder using only Latin letters, numbers, underscores and dashes." "We found a directory with the same name. Please try another folder name.","We found a directory with the same name. Please try another folder name." "We cannot create a new directory.","We cannot create a new directory." "We cannot delete directory %1.","We cannot delete directory %1." diff --git a/app/code/Magento/Cms/registration.php b/app/code/Magento/Cms/registration.php index d9feda25d6118..aa55b4932fdba 100644 --- a/app/code/Magento/Cms/registration.php +++ b/app/code/Magento/Cms/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Cms', __DIR__); diff --git a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_form.xml b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_form.xml index ad0f33df59d4e..a2ce0ec1b8740 100644 --- a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_form.xml +++ b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_block_form.xml @@ -105,6 +105,7 @@ <settings> <validation> <rule name="required-entry" xsi:type="boolean">true</rule> + <rule name="no-marginal-whitespace" xsi:type="boolean">true</rule> </validation> <dataType>text</dataType> <label translate="true">Identifier</label> diff --git a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml index dd58d17cbf577..396923a2b6f3b 100644 --- a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml +++ b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml @@ -249,6 +249,7 @@ </field> <field name="layout_update_xml" formElement="textarea"> <argument name="data" xsi:type="array"> + <item name="disabled" xsi:type="boolean">true</item> <item name="config" xsi:type="array"> <item name="source" xsi:type="string">page</item> </item> @@ -259,6 +260,26 @@ <dataScope>layout_update_xml</dataScope> </settings> </field> + <field name="custom_layout_update_select" formElement="select"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="source" xsi:type="string">page</item> + </item> + </argument> + <settings> + <dataType>text</dataType> + <label translate="true">Custom Layout Update</label> + <tooltip> + <description translate="true"> + List of layout files with an update handle "selectable" + matching *PageIdentifier*_*UpdateID*. + <br/> + See Magento documentation for more information + </description> + </tooltip> + <dataScope>layout_update_selected</dataScope> + </settings> + </field> </fieldset> <fieldset name="custom_design_update" sortOrder="60"> <settings> diff --git a/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Block.php b/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Block.php index fa4944381b858..21bdca732b606 100644 --- a/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Block.php +++ b/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Block.php @@ -56,7 +56,7 @@ public function getData(string $blockIdentifier): array ); } - $renderedContent = $this->widgetFilter->filter($block->getContent()); + $renderedContent = $this->widgetFilter->filterDirective($block->getContent()); $blockData = [ BlockInterface::BLOCK_ID => $block->getId(), diff --git a/app/code/Magento/CmsUrlRewrite/Test/Unit/Model/CmsPageUrlPathGeneratorTest.php b/app/code/Magento/CmsUrlRewrite/Test/Unit/Model/CmsPageUrlPathGeneratorTest.php new file mode 100644 index 0000000000000..6b57254dd0ec1 --- /dev/null +++ b/app/code/Magento/CmsUrlRewrite/Test/Unit/Model/CmsPageUrlPathGeneratorTest.php @@ -0,0 +1,156 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\CmsUrlRewrite\Test\Unit\Model; + +use Magento\CmsUrlRewrite\Model\CmsPageUrlPathGenerator; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\Filter\FilterManager; +use Magento\Cms\Api\Data\PageInterface; + +/** + * Class \Magento\CmsUrlRewrite\Test\Unit\Model\CmsPageUrlPathGeneratorTest + */ +class CmsPageUrlPathGeneratorTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManagerHelper + */ + private $objectManager; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|FilterManager + */ + private $filterManagerMock; + + /** + * @var CmsPageUrlPathGenerator + */ + private $model; + + /** + * Setup environment for test + */ + protected function setUp() + { + $this->objectManager = new ObjectManagerHelper($this); + $this->filterManagerMock = $this->getMockBuilder(FilterManager::class) + ->disableOriginalConstructor() + ->setMethods(['translitUrl']) + ->getMock(); + + $this->model = $this->objectManager->getObject( + CmsPageUrlPathGenerator::class, + [ + 'filterManager' => $this->filterManagerMock + ] + ); + } + + /** + * Test getUrlPath with page has identifier = cms-cookie + */ + public function testGetUrlPath() + { + /* @var PageInterface $cmsPageMock*/ + $cmsPageMock = $this->getMockBuilder(PageInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $cmsPageMock->expects($this->any()) + ->method('getIdentifier') + ->willReturn('cms-cookie'); + + $this->assertEquals('cms-cookie', $this->model->getUrlPath($cmsPageMock)); + } + + /** + * Test getCanonicalUrlPath() with page has id = 1 + */ + public function testGetCanonicalUrlPath() + { + /* @var PageInterface $cmsPageMock*/ + $cmsPageMock = $this->getMockBuilder(PageInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $cmsPageMock->expects($this->any()) + ->method('getId') + ->willReturn('1'); + + $this->assertEquals('cms/page/view/page_id/1', $this->model->getCanonicalUrlPath($cmsPageMock)); + } + + /** + * Test generateUrlKey() with page has no identifier + */ + public function testGenerateUrlKeyWithNullIdentifier() + { + /** + * Data set + */ + $page = [ + 'identifier' => null, + 'title' => 'CMS Cookie' + ]; + + /* @var PageInterface $cmsPageMock*/ + $cmsPageMock = $this->getMockBuilder(PageInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $cmsPageMock->expects($this->any()) + ->method('getIdentifier') + ->willReturn($page['identifier']); + + $cmsPageMock->expects($this->any()) + ->method('getTitle') + ->willReturn($page['title']); + + $this->filterManagerMock->expects($this->any()) + ->method('translitUrl') + ->with($page['title']) + ->willReturn('cms-cookie'); + + $this->assertEquals('cms-cookie', $this->model->generateUrlKey($cmsPageMock)); + } + + /** + * Test generateUrlKey() with page has identifier + */ + public function testGenerateUrlKeyWithIdentifier() + { + /** + * Data set + */ + $page = [ + 'identifier' => 'home', + 'title' => 'Home Page' + ]; + + /* @var PageInterface $cmsPageMock*/ + $cmsPageMock = $this->getMockBuilder(PageInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $cmsPageMock->expects($this->any()) + ->method('getIdentifier') + ->willReturn($page['identifier']); + + $cmsPageMock->expects($this->any()) + ->method('getTitle') + ->willReturn($page['title']); + + $this->filterManagerMock->expects($this->any()) + ->method('translitUrl') + ->with($page['identifier']) + ->willReturn('home'); + + $this->assertEquals('home', $this->model->generateUrlKey($cmsPageMock)); + } +} diff --git a/app/code/Magento/CmsUrlRewrite/registration.php b/app/code/Magento/CmsUrlRewrite/registration.php index aad46cb41827c..6acd495235110 100644 --- a/app/code/Magento/CmsUrlRewrite/registration.php +++ b/app/code/Magento/CmsUrlRewrite/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CmsUrlRewrite', __DIR__); diff --git a/app/code/Magento/Config/Block/System/Config/Form/Field.php b/app/code/Magento/Config/Block/System/Config/Form/Field.php index e4b582a1f0504..ac4a85b7d3bc6 100644 --- a/app/code/Magento/Config/Block/System/Config/Form/Field.php +++ b/app/code/Magento/Config/Block/System/Config/Form/Field.php @@ -43,6 +43,10 @@ public function render(\Magento\Framework\Data\Form\Element\AbstractElement $ele $element->setDisabled(true); } + if ($element->getIsDisableInheritance()) { + $element->setReadonly(true); + } + $html = '<td class="label"><label for="' . $element->getHtmlId() . '"><span' . $this->_renderScopeLabel($element) . '>' . @@ -94,7 +98,7 @@ protected function _renderInheritCheckbox(\Magento\Framework\Data\Form\Element\A $htmlId = $element->getHtmlId(); $namePrefix = preg_replace('#\[value\](\[\])?$#', '', $element->getName()); $checkedHtml = $element->getInherit() == 1 ? 'checked="checked"' : ''; - $disabled = $element->getIsDisableInheritance() == true ? ' disabled="disabled"' : ''; + $disabled = $element->getIsDisableInheritance() == true ? ' disabled="disabled" readonly="1"' : ''; $html = '<td class="use-default">'; $html .= '<input id="' . diff --git a/app/code/Magento/Config/Model/Config/Backend/Encrypted.php b/app/code/Magento/Config/Model/Config/Backend/Encrypted.php index 80ce061a0a17e..62d6531978d8a 100644 --- a/app/code/Magento/Config/Model/Config/Backend/Encrypted.php +++ b/app/code/Magento/Config/Model/Config/Backend/Encrypted.php @@ -48,9 +48,6 @@ public function __construct( * Magic method called during class serialization * * @return string[] - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __sleep() { @@ -62,9 +59,6 @@ public function __sleep() * Magic method called during class un-serialization * * @return void - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __wakeup() { diff --git a/app/code/Magento/Config/Model/Config/Structure.php b/app/code/Magento/Config/Model/Config/Structure.php index a380dc82a7c5e..a16920f0dc527 100644 --- a/app/code/Magento/Config/Model/Config/Structure.php +++ b/app/code/Magento/Config/Model/Config/Structure.php @@ -292,6 +292,7 @@ public function getFieldPathsByAttribute($attributeName, $attributeValue) foreach ($section['children'] as $group) { if (isset($group['children'])) { $path = $section['id'] . '/' . $group['id']; + // phpcs:ignore Magento2.Performance.ForeachArrayMerge.ForeachArrayMerge $result = array_merge( $result, $this->_getGroupFieldPathsByAttribute( @@ -398,7 +399,7 @@ private function getFieldsRecursively(array $elements = []) $this->getFieldsRecursively($element['children']) ); } else { - if ($element['_elementType'] === 'field' && isset($element['label'])) { + if ($element['_elementType'] === 'field') { $structurePath = (isset($element['path']) ? $element['path'] . '/' : '') . $element['id']; $configPath = isset($element['config_path']) ? $element['config_path'] : $structurePath; diff --git a/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php b/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php index 8f4d82eed51c5..15f23c7737294 100644 --- a/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php +++ b/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php @@ -9,6 +9,12 @@ * @api * @since 100.0.2 */ + +/** + * Class Field + * + * Fields are used to describe possible values for a type/interface. + */ class Field { /** @@ -41,7 +47,7 @@ public function __construct(array $fieldData = [], $fieldPrefix = "") if (isset($fieldData['separator'])) { $this->_values = explode($fieldData['separator'], $fieldData['value']); } else { - $this->_values = [$fieldData['value']]; + $this->_values = [isset($fieldData['value']) ? $fieldData['value'] : '']; } $fieldId = $fieldPrefix . (isset( $fieldData['dependPath'] diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminExpandConfigSectionActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminExpandConfigSectionActionGroup.xml new file mode 100644 index 0000000000000..03a3a45ef0947 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminExpandConfigSectionActionGroup.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="AdminExpandConfigSectionActionGroup"> + <annotations> + <description>Expands configuration section passed via argument as Section Name.</description> + </annotations> + <arguments> + <argument name="sectionName" type="string"/> + </arguments> + + <conditionalClick selector="{{AdminConfigSection.collapsibleSectionByTitle(sectionName)}}" dependentSelector="{{AdminConfigSection.expandedSectionByTitle(sectionName)}}" visible="false" stepKey="expandSection" /> + <waitForElement selector="{{AdminConfigSection.expandedSectionByTitle(sectionName)}}" stepKey="waitOpenedSection"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminExpandSecurityTabActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminExpandSecurityTabActionGroup.xml new file mode 100644 index 0000000000000..f07d4df9d86b2 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminExpandSecurityTabActionGroup.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="AdminExpandSecurityTabActionGroup"> + <conditionalClick selector="{{AdminSection.SecurityTab}}" dependentSelector="{{AdminSection.CheckIfTabExpand}}" visible="true" stepKey="openSecurityTab"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenConfigAdminPageActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenConfigAdminPageActionGroup.xml new file mode 100644 index 0000000000000..6d4fba179ecf4 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminOpenConfigAdminPageActionGroup.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="AdminOpenConfigAdminPageActionGroup"> + <amOnPage url="{{AdminConfigAdminPage.url}}" stepKey="goToConfigAdminSectionPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminSetMaximumLoginFailuresToLockoutAccountActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminSetMaximumLoginFailuresToLockoutAccountActionGroup.xml new file mode 100644 index 0000000000000..ada58e9f4225e --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/AdminSetMaximumLoginFailuresToLockoutAccountActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSetMaximumLoginFailuresToLockoutAccountActionGroup"> + <arguments> + <argument name="qty" type="string" defaultValue="3"/> + </arguments> + <uncheckOption selector="{{AdminSection.systemValueForMaximumLoginFailures}}" stepKey="uncheckUseSystemValue"/> + <fillField selector="{{AdminSection.MaximumLoginFailures}}" userInput="{{qty}}" stepKey="setMaximumLoginFailures"/> + <seeInField selector="{{AdminSection.MaximumLoginFailures}}" userInput="{{qty}}" stepKey="seeNewValueInField"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ChooseElasticSearchAsSearchEngineActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ChooseElasticSearchAsSearchEngineActionGroup.xml new file mode 100644 index 0000000000000..ad1cf897b4601 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/ChooseElasticSearchAsSearchEngineActionGroup.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="ChooseElasticSearchAsSearchEngineActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page for 'Catalog'. Sets the 'Search Engine' to 'elasticsearch5'. Clicks on the Save button. PLEASE NOTE: The value is Hardcoded.</description> + </annotations> + + <amOnPage url="{{AdminCatalogSearchConfigurationPage.url}}" stepKey="configureSearchEngine"/> + <waitForPageLoad stepKey="waitForConfigPage"/> + <scrollTo selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" stepKey="scrollToCatalogSearchTab"/> + <conditionalClick selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" dependentSelector="{{AdminCatalogSearchConfigurationSection.checkIfCatalogSearchTabExpand}}" visible="true" stepKey="expandCatalogSearchTab"/> + <waitForElementVisible selector="{{AdminCatalogSearchConfigurationSection.searchEngine}}" stepKey="waitForDropdownToBeVisible"/> + <uncheckOption selector="{{AdminCatalogSearchConfigurationSection.searchEngineDefaultSystemValue}}" stepKey="uncheckUseSystemValue"/> + <selectOption selector="{{AdminCatalogSearchConfigurationSection.searchEngine}}" userInput="elasticsearch5" stepKey="chooseES5"/> + <!--<scrollTo selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" stepKey="scrollToCatalogSearchTab2"/>--> + <!--<click selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" stepKey="collapseCatalogSearchTab"/>--> + <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfiguration"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the configuration." stepKey="seeConfigurationSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAdminAccountSharingActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAdminAccountSharingActionGroup.xml index 01cfa30ce0e7f..976f64a457074 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAdminAccountSharingActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAdminAccountSharingActionGroup.xml @@ -13,7 +13,7 @@ <description>Goes to the 'Configuration' page for 'Admin'. Enables 'Admin Account Sharing'. Clicks on the Save button.</description> </annotations> - <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/admin/system_config/edit/section/admin/" stepKey="navigateToConfigurationPage"/> + <amOnPage url="{{AdminConfigAdvancedAdmin.url}}" stepKey="navigateToConfigurationPage"/> <waitForPageLoad stepKey="wait1"/> <conditionalClick stepKey="expandSecurityTab" selector="{{AdminSection.SecurityTab}}" dependentSelector="{{AdminSection.CheckIfTabExpand}}" visible="true"/> <waitForElementVisible selector="{{AdminSection.AdminAccountSharing}}" stepKey="waitForAdminAccountSharingDrpDown"/> @@ -22,20 +22,4 @@ <click selector="{{AdminSection.SecurityTab}}" stepKey="clollapseSecurityTab"/> <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig"/> </actionGroup> - - <actionGroup name="EnableAdminAccountSharingActionGroup"> - <annotations> - <description>Enabled 'Admin Account Sharing' via the API.</description> - </annotations> - - <createData stepKey="setConfig" entity="EnableAdminAccountSharing"/> - </actionGroup> - - <actionGroup name="DisableAdminAccountSharingActionGroup"> - <annotations> - <description>Disables 'Admin Account Sharing' via the API.</description> - </annotations> - - <createData stepKey="setConfig" entity="DisableAdminAccountSharing"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAdminCatalogSearchActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAdminCatalogSearchActionGroup.xml deleted file mode 100644 index d700896f08bc0..0000000000000 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigAdminCatalogSearchActionGroup.xml +++ /dev/null @@ -1,44 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="ChooseElasticSearchAsSearchEngine"> - <annotations> - <description>Goes to the 'Configuration' page for 'Catalog'. Sets the 'Search Engine' to 'elasticsearch5'. Clicks on the Save button. PLEASE NOTE: The value is Hardcoded.</description> - </annotations> - - <amOnPage url="{{AdminCatalogSearchConfigurationPage.url}}" stepKey="configureSearchEngine"/> - <waitForPageLoad stepKey="waitForConfigPage"/> - <scrollTo selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" stepKey="scrollToCatalogSearchTab"/> - <conditionalClick selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" dependentSelector="{{AdminCatalogSearchConfigurationSection.checkIfCatalogSearchTabExpand}}" visible="true" stepKey="expandCatalogSearchTab"/> - <waitForElementVisible selector="{{AdminCatalogSearchConfigurationSection.searchEngine}}" stepKey="waitForDropdownToBeVisible"/> - <uncheckOption selector="{{AdminCatalogSearchConfigurationSection.searchEngineDefaultSystemValue}}" stepKey="uncheckUseSystemValue"/> - <selectOption selector="{{AdminCatalogSearchConfigurationSection.searchEngine}}" userInput="elasticsearch5" stepKey="chooseES5"/> - <!--<scrollTo selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" stepKey="scrollToCatalogSearchTab2"/>--> - <!--<click selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" stepKey="collapseCatalogSearchTab"/>--> - <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfiguration"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You saved the configuration." stepKey="seeConfigurationSuccessMessage"/> - </actionGroup> - - <actionGroup name="ResetSearchEngineConfiguration"> - <annotations> - <description>Goes to the 'Configuration' page for 'Catalog'. Sets the 'Search Engine' to 'mysql'. Clicks on the Save button. PLEASE NOTE: The value is Hardcoded.</description> - </annotations> - - <amOnPage url="{{AdminCatalogSearchConfigurationPage.url}}" stepKey="resetSearchEngine"/> - <waitForPageLoad stepKey="waitForConfigPage2"/> - <scrollTo selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" stepKey="scrollToCatalogSearchTab2"/> - <conditionalClick selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" dependentSelector="{{AdminCatalogSearchConfigurationSection.checkIfCatalogSearchTabExpand}}" visible="true" stepKey="expandCatalogSearchTab2"/> - <waitForElementVisible selector="{{AdminCatalogSearchConfigurationSection.searchEngine}}" stepKey="waitForDropdownToBeVisible2"/> - <selectOption selector="{{AdminCatalogSearchConfigurationSection.searchEngine}}" userInput="mysql" stepKey="chooseMySQL"/> - <checkOption selector="{{AdminCatalogSearchConfigurationSection.searchEngineDefaultSystemValue}}" stepKey="checkUseSystemValue"/> - <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfiguration2"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You saved the configuration." stepKey="seeConfigurationSuccessMessage2"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml deleted file mode 100644 index bfa42636519a0..0000000000000 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml +++ /dev/null @@ -1,78 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="SetTaxClassForShipping"> - <annotations> - <description>Goes to the 'Configuration' page for 'Tax'. Sets 'Tax Class for Shipping' to 'Taxable Goods'. Clicks on the Save button. PLEASE NOTE: The value is Hardcoded.</description> - </annotations> - - <amOnPage url="{{AdminSalesTaxClassPage.url}}" stepKey="navigateToSalesTaxPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <conditionalClick selector="{{SalesConfigSection.TaxClassesTab}}" dependentSelector="{{SalesConfigSection.CheckIfTaxClassesTabExpand}}" visible="true" stepKey="expandTaxClassesTab"/> - <waitForElementVisible selector="{{SalesConfigSection.ShippingTaxClass}}" stepKey="seeShippingTaxClass"/> - <uncheckOption selector="{{SalesConfigSection.EnableTaxClassForShipping}}" stepKey="uncheckUseSystemValue"/> - <selectOption selector="{{SalesConfigSection.ShippingTaxClass}}" userInput="Taxable Goods" stepKey="setShippingTaxClass"/> - <click selector="{{SalesConfigSection.TaxClassesTab}}" stepKey="collapseTaxClassesTab"/> - <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig"/> - </actionGroup> - - <actionGroup name="ResetTaxClassForShipping"> - <annotations> - <description>Goes to the 'Configuration' page for 'Tax'. Sets 'Tax Class for Shipping' to 'None'. Clicks on the Save button. PLEASE NOTE: The value is Hardcoded.</description> - </annotations> - - <amOnPage url="{{AdminSalesTaxClassPage.url}}" stepKey="navigateToSalesTaxConfigPagetoReset"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <conditionalClick selector="{{SalesConfigSection.TaxClassesTab}}" dependentSelector="{{SalesConfigSection.CheckIfTaxClassesTabExpand}}" visible="true" stepKey="openTaxClassTab"/> - <waitForElementVisible selector="{{SalesConfigSection.ShippingTaxClass}}" stepKey="seeShippingTaxClass2"/> - <selectOption selector="{{SalesConfigSection.ShippingTaxClass}}" userInput="None" stepKey="resetShippingTaxClass"/> - <checkOption selector="{{SalesConfigSection.EnableTaxClassForShipping}}" stepKey="useSystemValue"/> - <click selector="{{SalesConfigSection.TaxClassesTab}}" stepKey="collapseTaxClassesTab"/> - <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfiguration"/> - </actionGroup> - - <actionGroup name="SetTaxApplyOnSetting"> - <annotations> - <description>Goes to the 'Configuration' page for 'Tax'. Sets 'Apply Tax On' to the provided value. Clicks on the Save button</description> - </annotations> - <arguments> - <argument name="userInput" type="string"/> - </arguments> - - <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationSettings}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationAlgorithm}}" visible="false" stepKey="openTaxCalcSettingsSection"/> - <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"/> - <click selector="{{AdminConfigureTaxSection.taxCalculationSettings}}" stepKey="collapseCalcSettingsTab"/> - <click selector="{{AdminConfigureTaxSection.save}}" stepKey="saveConfig"/> - <waitForPageLoad stepKey="waitForConfigSaved"/> - <see userInput="You saved the configuration." stepKey="seeSuccessMessage"/> - </actionGroup> - - <actionGroup name="DisableTaxApplyOnOriginalPrice"> - <annotations> - <description>Goes to the 'Configuration' page for 'Tax'. Sets 'Apply Tax On' to the provided value. Clicks on the Save button.</description> - </annotations> - <arguments> - <argument name="userInput" type="string"/> - </arguments> - - <amOnPage url="{{AdminSalesTaxClassPage.url}}" stepKey="navigateToSalesTaxPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationSettings}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationAlgorithm}}" visible="false" stepKey="openTaxCalcSettingsSection"/> - <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> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml deleted file mode 100644 index 342b98a64c828..0000000000000 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWYSIWYGActionGroup.xml +++ /dev/null @@ -1,76 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="EnabledWYSIWYG"> - <annotations> - <description>Enables the WYSIWYG Editor via the CLI.</description> - </annotations> - - <magentoCLI stepKey="enableWYSIWYG" command="config:set cms/wysiwyg/enabled enabled"/> - </actionGroup> - - <actionGroup name="SwitchToTinyMCE3"> - <annotations> - <description>Goes to the 'Configuration' page for 'Content Management'. Sets 'WYSIWYG Editor' to 'TinyMCE 3'. Clicks on the Save button. PLEASE NOTE: The value is Hardcoded.</description> - </annotations> - - <comment userInput="Choose TinyMCE3 as the default editor" stepKey="chooseTinyMCE3AsEditor"/> - <conditionalClick stepKey="expandWYSIWYGOptions1" selector="{{ContentManagementSection.WYSIWYGOptions}}" dependentSelector="{{ContentManagementSection.CheckIfTabExpand}}" visible="true"/> - <waitForElementVisible selector="{{ContentManagementSection.SwitcherSystemValue}}" stepKey="waitForCheckbox2"/> - <uncheckOption selector="{{ContentManagementSection.SwitcherSystemValue}}" stepKey="uncheckUseSystemValue2"/> - <waitForElementVisible selector="{{ContentManagementSection.Switcher}}" stepKey="waitForSwitcherDropdown2"/> - <selectOption selector="{{ContentManagementSection.Switcher}}" userInput="TinyMCE 3" stepKey="switchToVersion3"/> - <click selector="{{ContentManagementSection.WYSIWYGOptions}}" stepKey="collapseWYSIWYGOptions"/> - <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You saved the configuration." stepKey="seeConfigurationSuccessMessage"/> - </actionGroup> - - <actionGroup name="DisabledWYSIWYG"> - <annotations> - <description>Disables the WYSIWYG Editor via the CLI.</description> - </annotations> - - <magentoCLI stepKey="disableWYSIWYG" command="config:set cms/wysiwyg/enabled disabled"/> - </actionGroup> - - <actionGroup name="UseStaticURLForMediaContentInWYSIWYG"> - <annotations> - <description>Goes to the 'Configuration' page for 'Content Management'. Sets 'Use Static URLs for Media Content in WYSIWYG' to the provided value. Clicks on the Save button.</description> - </annotations> - <arguments> - <argument name="value" defaultValue="Yes" type="string"/> - </arguments> - - <amOnPage url="{{ConfigurationStoresPage.url}}" stepKey="navigateToWYSIWYGConfigPage1"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <conditionalClick stepKey="expandWYSIWYGOptions" selector="{{ContentManagementSection.WYSIWYGOptions}}" dependentSelector="{{ContentManagementSection.CheckIfTabExpand}}" visible="true"/> - <waitForElementVisible selector="{{ContentManagementSection.EnableWYSIWYG}}" stepKey="waitForEnableWYSIWYGDropdown1"/> - <selectOption selector="{{ContentManagementSection.StaticURL}}" userInput="{{value}}" stepKey="selectOption1"/> - <click selector="{{ContentManagementSection.WYSIWYGOptions}}" stepKey="collapseWYSIWYGOptions"/> - <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - </actionGroup> - - <actionGroup name="EnabledWYSIWYGEditor"> - <annotations> - <description>Goes to the 'Configuration' page for 'Content Management'. Sets 'Enable WYSIWYG Editor' to 'Enabled by Default'. Clicks on the Save button. PLEASE NOTE: The value is Hardcoded.</description> - </annotations> - - <amOnPage url="{{AdminContentManagementPage.url}}" stepKey="navigateToConfigurationPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <conditionalClick selector="{{ContentManagementSection.WYSIWYGOptions}}" dependentSelector="{{ContentManagementSection.EnableWYSIWYG}}" visible="false" stepKey="expandWYSIWYGOptionsTab"/> - <waitForElementVisible selector="{{ContentManagementSection.EnableWYSIWYG}}" stepKey="waitTabToExpand"/> - <uncheckOption selector="{{ContentManagementSection.EnableSystemValue}}" stepKey="enableEnableSystemValue"/> - <selectOption selector="{{ContentManagementSection.EnableWYSIWYG}}" userInput="Enabled by Default" stepKey="enableWYSIWYG"/> - <click selector="{{ContentManagementSection.WYSIWYGOptions}}" stepKey="collapseWYSIWYGOptionsTab"/> - <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig"/> - <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the configuration."/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWebUrlOptionsActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWebUrlOptionsActionGroup.xml deleted file mode 100644 index fe297cbf9ad7d..0000000000000 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigWebUrlOptionsActionGroup.xml +++ /dev/null @@ -1,41 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="EnableWebUrlOptions"> - <annotations> - <description>Goes to the 'Configuration' page for 'Url Options'. Enables 'Add Store Code to Urls'. Clicks on the Save button.</description> - </annotations> - - <amOnPage url="{{WebConfigurationPage.url}}" stepKey="navigateToWebConfigurationPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <conditionalClick selector="{{WebSection.UrlOptionsTab}}" dependentSelector="{{WebSection.CheckIfUrlOptionsTabExpand}}" visible="true" stepKey="expandUrlSectionTab"/> - <waitForElementVisible selector="{{UrlOptionsSection.addStoreCodeToUrl}}" stepKey="seeAddStoreCodeToUrl"/> - <uncheckOption selector="{{UrlOptionsSection.systemValueForStoreCode}}" stepKey="uncheckUseSystemValue"/> - <selectOption selector="{{UrlOptionsSection.addStoreCodeToUrl}}" userInput="Yes" stepKey="enableStoreCode"/> - <click selector="{{WebSection.UrlOptionsTab}}" stepKey="collapseUrlOptions"/> - <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig"/> - </actionGroup> - - <actionGroup name="ResetWebUrlOptions"> - <annotations> - <description>Goes to the 'Configuration' page for 'Url Options'. Disables 'Add Store Code to Urls'. Clicks on the Save button.</description> - </annotations> - - <amOnPage url="{{WebConfigurationPage.url}}" stepKey="navigateToWebConfigurationPagetoReset"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <conditionalClick selector="{{WebSection.UrlOptionsTab}}" dependentSelector="{{WebSection.CheckIfUrlOptionsTabExpand}}" visible="true" stepKey="closeUrlSectionTab"/> - <waitForElementVisible selector="{{UrlOptionsSection.addStoreCodeToUrl}}" stepKey="seeAddStoreCodeToUrl2"/> - <!--<uncheckOption selector="{{UrlOptionsSection.systemValueForStoreCode}}" stepKey="uncheckUseSystemValue"/>--> - <selectOption selector="{{UrlOptionsSection.addStoreCodeToUrl}}" userInput="No" stepKey="enableStoreCode"/> - <checkOption selector="{{UrlOptionsSection.systemValueForStoreCode}}" stepKey="checkUseSystemValue"/> - <click selector="{{WebSection.UrlOptionsTab}}" stepKey="collapseUrlOptions"/> - <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/DisableAdminAccountSharingActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/DisableAdminAccountSharingActionGroup.xml new file mode 100644 index 0000000000000..5bc9cca7d171f --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/DisableAdminAccountSharingActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DisableAdminAccountSharingActionGroup"> + <annotations> + <description>Disables 'Admin Account Sharing' via the API.</description> + </annotations> + + <createData stepKey="setConfig" entity="DisableAdminAccountSharing"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/DisableTaxApplyOnOriginalPriceActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/DisableTaxApplyOnOriginalPriceActionGroup.xml new file mode 100644 index 0000000000000..c27fc6a93ca5a --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/DisableTaxApplyOnOriginalPriceActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DisableTaxApplyOnOriginalPriceActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page for 'Tax'. Sets 'Apply Tax On' to the provided value. Clicks on the Save button.</description> + </annotations> + <arguments> + <argument name="userInput" type="string"/> + </arguments> + + <amOnPage url="{{AdminSalesTaxClassPage.url}}" stepKey="navigateToSalesTaxPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationSettings}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationAlgorithm}}" visible="false" stepKey="openTaxCalcSettingsSection"/> + <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> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/DisabledWYSIWYGActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/DisabledWYSIWYGActionGroup.xml new file mode 100644 index 0000000000000..f1b817711d6b1 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/DisabledWYSIWYGActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DisabledWYSIWYGActionGroup"> + <annotations> + <description>Disables the WYSIWYG Editor via the CLI.</description> + </annotations> + + <magentoCLI stepKey="disableWYSIWYG" command="config:set cms/wysiwyg/enabled disabled"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/EnableAdminAccountSharingActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/EnableAdminAccountSharingActionGroup.xml new file mode 100644 index 0000000000000..5541a1e48a0af --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/EnableAdminAccountSharingActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="EnableAdminAccountSharingActionGroup"> + <annotations> + <description>Enabled 'Admin Account Sharing' via the API.</description> + </annotations> + + <createData stepKey="setConfig" entity="EnableAdminAccountSharing"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/EnableWebUrlOptionsActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/EnableWebUrlOptionsActionGroup.xml new file mode 100644 index 0000000000000..2e4fdf0debd79 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/EnableWebUrlOptionsActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="EnableWebUrlOptionsActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page for 'Url Options'. Enables 'Add Store Code to Urls'. Clicks on the Save button.</description> + </annotations> + + <amOnPage url="{{WebConfigurationPage.url}}" stepKey="navigateToWebConfigurationPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <conditionalClick selector="{{WebSection.UrlOptionsTab}}" dependentSelector="{{WebSection.CheckIfUrlOptionsTabExpand}}" visible="true" stepKey="expandUrlSectionTab"/> + <waitForElementVisible selector="{{UrlOptionsSection.addStoreCodeToUrl}}" stepKey="seeAddStoreCodeToUrl"/> + <uncheckOption selector="{{UrlOptionsSection.systemValueForStoreCode}}" stepKey="uncheckUseSystemValue"/> + <selectOption selector="{{UrlOptionsSection.addStoreCodeToUrl}}" userInput="Yes" stepKey="enableStoreCode"/> + <click selector="{{WebSection.UrlOptionsTab}}" stepKey="collapseUrlOptions"/> + <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/EnabledWYSIWYGActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/EnabledWYSIWYGActionGroup.xml new file mode 100644 index 0000000000000..556b8bdcf9736 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/EnabledWYSIWYGActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="EnabledWYSIWYGActionGroup"> + <annotations> + <description>Enables the WYSIWYG Editor via the CLI.</description> + </annotations> + + <magentoCLI stepKey="enableWYSIWYG" command="config:set cms/wysiwyg/enabled enabled"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/EnabledWYSIWYGEditorActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/EnabledWYSIWYGEditorActionGroup.xml new file mode 100644 index 0000000000000..7d23a5d7581c5 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/EnabledWYSIWYGEditorActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="EnabledWYSIWYGEditorActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page for 'Content Management'. Sets 'Enable WYSIWYG Editor' to 'Enabled by Default'. Clicks on the Save button. PLEASE NOTE: The value is Hardcoded.</description> + </annotations> + + <amOnPage url="{{AdminContentManagementPage.url}}" stepKey="navigateToConfigurationPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <conditionalClick selector="{{ContentManagementSection.WYSIWYGOptions}}" dependentSelector="{{ContentManagementSection.EnableWYSIWYG}}" visible="false" stepKey="expandWYSIWYGOptionsTab"/> + <waitForElementVisible selector="{{ContentManagementSection.EnableWYSIWYG}}" stepKey="waitTabToExpand"/> + <uncheckOption selector="{{ContentManagementSection.EnableSystemValue}}" stepKey="enableEnableSystemValue"/> + <selectOption selector="{{ContentManagementSection.EnableWYSIWYG}}" userInput="Enabled by Default" stepKey="enableWYSIWYG"/> + <click selector="{{ContentManagementSection.WYSIWYGOptions}}" stepKey="collapseWYSIWYGOptionsTab"/> + <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig"/> + <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the configuration."/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/GeneralConfigurationActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/GeneralConfigurationActionGroup.xml deleted file mode 100644 index d65376828e2c4..0000000000000 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/GeneralConfigurationActionGroup.xml +++ /dev/null @@ -1,72 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="NavigateToDefaultLayoutsSetting"> - <annotations> - <description>Goes to the 'Configuration' page for 'Web'. Expands the 'Default Layouts' section.</description> - </annotations> - - <amOnPage url="{{WebConfigurationPage.url}}" stepKey="navigateToWebConfigurationPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <conditionalClick stepKey="expandDefaultLayouts" selector="{{WebSection.DefaultLayoutsTab}}" dependentSelector="{{WebSection.CheckIfTabExpand}}" visible="true"/> - <waitForElementVisible selector="{{DefaultLayoutsSection.categoryLayout}}" stepKey="waittForDefaultCategoryLayout"/> - </actionGroup> - - <actionGroup name="NavigateToConfigurationGeneralPage"> - <annotations> - <description>Goes to the 'Configuration' page for 'General'.</description> - </annotations> - - <amOnPage url="{{AdminConfigGeneralPage.url}}" stepKey="navigateToConfigGeneralPage"/> - <waitForPageLoad stepKey="waitForConfigPageLoad"/> - </actionGroup> - <actionGroup name="SelectTopDestinationsCountry"> - <annotations> - <description>Selects the provided Countries under 'Top destinations' on the 'General' section of the 'Configuration' page. Clicks on the Save button.</description> - </annotations> - <arguments> - <argument name="countries" type="entity"/> - </arguments> - - <selectOption selector="{{CountryOptionsSection.topDestinations}}" parameterArray="[{{countries.country}}]" stepKey="selectTopDestinationsCountry"/> - <click selector="#save" stepKey="saveConfig"/> - <waitForPageLoad stepKey="waitForSavingConfig"/> - </actionGroup> - - <actionGroup name="UnSelectTopDestinationsCountry"> - <annotations> - <description>Un-selects the provided Countries under 'Top destinations' on the 'General' section of the 'Configuration' page. Clicks on the Save button.</description> - </annotations> - <arguments> - <argument name="countries" type="entity"/> - </arguments> - - <unselectOption selector="{{CountryOptionsSection.topDestinations}}" parameterArray="[{{countries.country}}]" stepKey="unSelectTopDestinationsCountry"/> - <click selector="#save" stepKey="saveConfig"/> - <waitForPageLoad stepKey="waitForSavingConfig"/> - </actionGroup> - - <actionGroup name="SelectCountriesWithRequiredRegion"> - <annotations> - <description>Goes to the 'Configuration' page for 'General'. Selects the provided Countries under 'State is Required for'. Clicks on the Save button.</description> - </annotations> - <arguments> - <argument name="countries" type="entity"/> - </arguments> - - <amOnPage url="{{AdminConfigGeneralPage.url}}" stepKey="navigateToAdminConfigGeneralPage"/> - <conditionalClick selector="{{StateOptionsSection.stateOptions}}" dependentSelector="{{StateOptionsSection.countriesWithRequiredRegions}}" visible="false" stepKey="expandStateOptionsTab"/> - <waitForAjaxLoad stepKey="waitForAjax"/> - <scrollTo selector="{{StateOptionsSection.countriesWithRequiredRegions}}" stepKey="scrollToForm"/> - <selectOption selector="{{StateOptionsSection.countriesWithRequiredRegions}}" parameterArray="[{{countries.country}}]" stepKey="selectCountriesWithRequiredRegion"/> - <click selector="#save" stepKey="saveConfig"/> - <waitForPageLoad stepKey="waitForSavingConfig"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/NavigateToConfigurationGeneralPageActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/NavigateToConfigurationGeneralPageActionGroup.xml new file mode 100644 index 0000000000000..fb89b406d9f32 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/NavigateToConfigurationGeneralPageActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="NavigateToConfigurationGeneralPageActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page for 'General'.</description> + </annotations> + + <amOnPage url="{{AdminConfigGeneralPage.url}}" stepKey="navigateToConfigGeneralPage"/> + <waitForPageLoad stepKey="waitForConfigPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/NavigateToDefaultLayoutsSettingActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/NavigateToDefaultLayoutsSettingActionGroup.xml new file mode 100644 index 0000000000000..c6690632194b3 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/NavigateToDefaultLayoutsSettingActionGroup.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="NavigateToDefaultLayoutsSettingActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page for 'Web'. Expands the 'Default Layouts' section.</description> + </annotations> + + <amOnPage url="{{WebConfigurationPage.url}}" stepKey="navigateToWebConfigurationPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <conditionalClick stepKey="expandDefaultLayouts" selector="{{WebSection.DefaultLayoutsTab}}" dependentSelector="{{WebSection.CheckIfTabExpand}}" visible="true"/> + <waitForElementVisible selector="{{DefaultLayoutsSection.categoryLayout}}" stepKey="waittForDefaultCategoryLayout"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ResetSearchEngineConfigurationActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ResetSearchEngineConfigurationActionGroup.xml new file mode 100644 index 0000000000000..f6451a4171a59 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/ResetSearchEngineConfigurationActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ResetSearchEngineConfigurationActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page for 'Catalog'. Sets the 'Search Engine' to 'mysql'. Clicks on the Save button. PLEASE NOTE: The value is Hardcoded.</description> + </annotations> + + <amOnPage url="{{AdminCatalogSearchConfigurationPage.url}}" stepKey="resetSearchEngine"/> + <waitForPageLoad stepKey="waitForConfigPage2"/> + <scrollTo selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" stepKey="scrollToCatalogSearchTab2"/> + <conditionalClick selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" dependentSelector="{{AdminCatalogSearchConfigurationSection.checkIfCatalogSearchTabExpand}}" visible="true" stepKey="expandCatalogSearchTab2"/> + <waitForElementVisible selector="{{AdminCatalogSearchConfigurationSection.searchEngine}}" stepKey="waitForDropdownToBeVisible2"/> + <selectOption selector="{{AdminCatalogSearchConfigurationSection.searchEngine}}" userInput="mysql" stepKey="chooseMySQL"/> + <checkOption selector="{{AdminCatalogSearchConfigurationSection.searchEngineDefaultSystemValue}}" stepKey="checkUseSystemValue"/> + <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfiguration2"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the configuration." stepKey="seeConfigurationSuccessMessage2"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ResetTaxClassForShippingActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ResetTaxClassForShippingActionGroup.xml new file mode 100644 index 0000000000000..3f768bdac8055 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/ResetTaxClassForShippingActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ResetTaxClassForShippingActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page for 'Tax'. Sets 'Tax Class for Shipping' to 'None'. Clicks on the Save button. PLEASE NOTE: The value is Hardcoded.</description> + </annotations> + + <amOnPage url="{{AdminSalesTaxClassPage.url}}" stepKey="navigateToSalesTaxConfigPagetoReset"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <conditionalClick selector="{{SalesConfigSection.TaxClassesTab}}" dependentSelector="{{SalesConfigSection.CheckIfTaxClassesTabExpand}}" visible="true" stepKey="openTaxClassTab"/> + <waitForElementVisible selector="{{SalesConfigSection.ShippingTaxClass}}" stepKey="seeShippingTaxClass2"/> + <selectOption selector="{{SalesConfigSection.ShippingTaxClass}}" userInput="None" stepKey="resetShippingTaxClass"/> + <checkOption selector="{{SalesConfigSection.EnableTaxClassForShipping}}" stepKey="useSystemValue"/> + <click selector="{{SalesConfigSection.TaxClassesTab}}" stepKey="collapseTaxClassesTab"/> + <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfiguration"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ResetWebUrlOptionsActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ResetWebUrlOptionsActionGroup.xml new file mode 100644 index 0000000000000..8c403c95cacbc --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/ResetWebUrlOptionsActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ResetWebUrlOptionsActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page for 'Url Options'. Disables 'Add Store Code to Urls'. Clicks on the Save button.</description> + </annotations> + + <amOnPage url="{{WebConfigurationPage.url}}" stepKey="navigateToWebConfigurationPagetoReset"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <conditionalClick selector="{{WebSection.UrlOptionsTab}}" dependentSelector="{{WebSection.CheckIfUrlOptionsTabExpand}}" visible="true" stepKey="closeUrlSectionTab"/> + <waitForElementVisible selector="{{UrlOptionsSection.addStoreCodeToUrl}}" stepKey="seeAddStoreCodeToUrl2"/> + <!--<uncheckOption selector="{{UrlOptionsSection.systemValueForStoreCode}}" stepKey="uncheckUseSystemValue"/>--> + <selectOption selector="{{UrlOptionsSection.addStoreCodeToUrl}}" userInput="No" stepKey="enableStoreCode"/> + <checkOption selector="{{UrlOptionsSection.systemValueForStoreCode}}" stepKey="checkUseSystemValue"/> + <click selector="{{WebSection.UrlOptionsTab}}" stepKey="collapseUrlOptions"/> + <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/SelectCountriesWithRequiredRegionActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/SelectCountriesWithRequiredRegionActionGroup.xml new file mode 100644 index 0000000000000..c1e90c1c835b8 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/SelectCountriesWithRequiredRegionActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SelectCountriesWithRequiredRegionActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page for 'General'. Selects the provided Countries under 'State is Required for'. Clicks on the Save button.</description> + </annotations> + <arguments> + <argument name="countries" type="entity"/> + </arguments> + + <amOnPage url="{{AdminConfigGeneralPage.url}}" stepKey="navigateToAdminConfigGeneralPage"/> + <conditionalClick selector="{{StateOptionsSection.stateOptions}}" dependentSelector="{{StateOptionsSection.countriesWithRequiredRegions}}" visible="false" stepKey="expandStateOptionsTab"/> + <waitForAjaxLoad stepKey="waitForAjax"/> + <scrollTo selector="{{StateOptionsSection.countriesWithRequiredRegions}}" stepKey="scrollToForm"/> + <selectOption selector="{{StateOptionsSection.countriesWithRequiredRegions}}" parameterArray="[{{countries.country}}]" stepKey="selectCountriesWithRequiredRegion"/> + <click selector="#save" stepKey="saveConfig"/> + <waitForPageLoad stepKey="waitForSavingConfig"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/SelectTopDestinationsCountryActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/SelectTopDestinationsCountryActionGroup.xml new file mode 100644 index 0000000000000..8e44bbf912667 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/SelectTopDestinationsCountryActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SelectTopDestinationsCountryActionGroup"> + <annotations> + <description>Selects the provided Countries under 'Top destinations' on the 'General' section of the 'Configuration' page. Clicks on the Save button.</description> + </annotations> + <arguments> + <argument name="countries" type="entity"/> + </arguments> + + <selectOption selector="{{CountryOptionsSection.topDestinations}}" parameterArray="[{{countries.country}}]" stepKey="selectTopDestinationsCountry"/> + <click selector="#save" stepKey="saveConfig"/> + <waitForPageLoad stepKey="waitForSavingConfig"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/SetTaxApplyOnSettingActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/SetTaxApplyOnSettingActionGroup.xml new file mode 100644 index 0000000000000..f91575b052ac7 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/SetTaxApplyOnSettingActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SetTaxApplyOnSettingActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page for 'Tax'. Sets 'Apply Tax On' to the provided value. Clicks on the Save button</description> + </annotations> + <arguments> + <argument name="userInput" type="string"/> + </arguments> + + <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationSettings}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationAlgorithm}}" visible="false" stepKey="openTaxCalcSettingsSection"/> + <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"/> + <click selector="{{AdminConfigureTaxSection.taxCalculationSettings}}" stepKey="collapseCalcSettingsTab"/> + <click selector="{{AdminConfigureTaxSection.save}}" stepKey="saveConfig"/> + <waitForPageLoad stepKey="waitForConfigSaved"/> + <see userInput="You saved the configuration." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/SetTaxClassForShippingActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/SetTaxClassForShippingActionGroup.xml new file mode 100644 index 0000000000000..a0f05405ba0c9 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/SetTaxClassForShippingActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SetTaxClassForShippingActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page for 'Tax'. Sets 'Tax Class for Shipping' to 'Taxable Goods'. Clicks on the Save button. PLEASE NOTE: The value is Hardcoded.</description> + </annotations> + + <amOnPage url="{{AdminSalesTaxClassPage.url}}" stepKey="navigateToSalesTaxPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <conditionalClick selector="{{SalesConfigSection.TaxClassesTab}}" dependentSelector="{{SalesConfigSection.CheckIfTaxClassesTabExpand}}" visible="true" stepKey="expandTaxClassesTab"/> + <waitForElementVisible selector="{{SalesConfigSection.ShippingTaxClass}}" stepKey="seeShippingTaxClass"/> + <uncheckOption selector="{{SalesConfigSection.EnableTaxClassForShipping}}" stepKey="uncheckUseSystemValue"/> + <selectOption selector="{{SalesConfigSection.ShippingTaxClass}}" userInput="Taxable Goods" stepKey="setShippingTaxClass"/> + <click selector="{{SalesConfigSection.TaxClassesTab}}" stepKey="collapseTaxClassesTab"/> + <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/SwitchToTinyMCE3ActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/SwitchToTinyMCE3ActionGroup.xml new file mode 100644 index 0000000000000..b725610b7b2ee --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/SwitchToTinyMCE3ActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SwitchToTinyMCE3ActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page for 'Content Management'. Sets 'WYSIWYG Editor' to 'TinyMCE 3'. Clicks on the Save button. PLEASE NOTE: The value is Hardcoded.</description> + </annotations> + + <comment userInput="Choose TinyMCE3 as the default editor" stepKey="chooseTinyMCE3AsEditor"/> + <conditionalClick stepKey="expandWYSIWYGOptions1" selector="{{ContentManagementSection.WYSIWYGOptions}}" dependentSelector="{{ContentManagementSection.CheckIfTabExpand}}" visible="true"/> + <waitForElementVisible selector="{{ContentManagementSection.SwitcherSystemValue}}" stepKey="waitForCheckbox2"/> + <uncheckOption selector="{{ContentManagementSection.SwitcherSystemValue}}" stepKey="uncheckUseSystemValue2"/> + <waitForElementVisible selector="{{ContentManagementSection.Switcher}}" stepKey="waitForSwitcherDropdown2"/> + <selectOption selector="{{ContentManagementSection.Switcher}}" userInput="TinyMCE 3" stepKey="switchToVersion3"/> + <click selector="{{ContentManagementSection.WYSIWYGOptions}}" stepKey="collapseWYSIWYGOptions"/> + <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the configuration." stepKey="seeConfigurationSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/UnSelectTopDestinationsCountryActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/UnSelectTopDestinationsCountryActionGroup.xml new file mode 100644 index 0000000000000..54e4024be5f34 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/UnSelectTopDestinationsCountryActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="UnSelectTopDestinationsCountryActionGroup"> + <annotations> + <description>Un-selects the provided Countries under 'Top destinations' on the 'General' section of the 'Configuration' page. Clicks on the Save button.</description> + </annotations> + <arguments> + <argument name="countries" type="entity"/> + </arguments> + + <unselectOption selector="{{CountryOptionsSection.topDestinations}}" parameterArray="[{{countries.country}}]" stepKey="unSelectTopDestinationsCountry"/> + <click selector="#save" stepKey="saveConfig"/> + <waitForPageLoad stepKey="waitForSavingConfig"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/UseStaticURLForMediaContentInWYSIWYGActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/UseStaticURLForMediaContentInWYSIWYGActionGroup.xml new file mode 100644 index 0000000000000..b7c7ea2067db2 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/UseStaticURLForMediaContentInWYSIWYGActionGroup.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="UseStaticURLForMediaContentInWYSIWYGActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page for 'Content Management'. Sets 'Use Static URLs for Media Content in WYSIWYG' to the provided value. Clicks on the Save button.</description> + </annotations> + <arguments> + <argument name="value" defaultValue="Yes" type="string"/> + </arguments> + + <amOnPage url="{{ConfigurationStoresPage.url}}" stepKey="navigateToWYSIWYGConfigPage1"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <conditionalClick stepKey="expandWYSIWYGOptions" selector="{{ContentManagementSection.WYSIWYGOptions}}" dependentSelector="{{ContentManagementSection.CheckIfTabExpand}}" visible="true"/> + <waitForElementVisible selector="{{ContentManagementSection.EnableWYSIWYG}}" stepKey="waitForEnableWYSIWYGDropdown1"/> + <selectOption selector="{{ContentManagementSection.StaticURL}}" userInput="{{value}}" stepKey="selectOption1"/> + <click selector="{{ContentManagementSection.WYSIWYGOptions}}" stepKey="collapseWYSIWYGOptions"/> + <click selector="{{ContentManagementSection.Save}}" stepKey="saveConfig"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/Page/AdminConfigAdminPage.xml b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigAdminPage.xml new file mode 100644 index 0000000000000..661bb734bcbe4 --- /dev/null +++ b/app/code/Magento/Config/Test/Mftf/Page/AdminConfigAdminPage.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminConfigAdminPage" url="admin/system_config/edit/section/admin" module="Magento_Config" area="admin"> + <section name="AdminSection"/> + </page> +</pages> diff --git a/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml b/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml index ffe3f0076ca8d..a4fb3c7e32975 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/AdminConfigSection.xml @@ -19,5 +19,8 @@ <element name="defaultConfigButton" type="button" selector="#store-change-button" timeout="30"/> <element name="defaultConfigDropdown" type="button" selector="//ul[@class='dropdown-menu']" timeout="30"/> <element name="fieldError" type="text" selector="label.mage-error"/> + <element name="useSystemValue" type="checkbox" selector="#{{configRowId}} > .use-default > input" parameterized="true"/> + <element name="collapsibleSectionByTitle" type="text" selector="//form[@id='config-edit-form']//div[@class='section-config'][contains(.,'{{sectionTitle}}')]" parameterized="true" /> + <element name="expandedSectionByTitle" type="text" selector="//form[@id='config-edit-form']//div[@class='section-config active'][contains(.,'{{sectionTitle}}')]" parameterized="true" /> </section> </sections> diff --git a/app/code/Magento/Config/Test/Mftf/Section/AdminSection.xml b/app/code/Magento/Config/Test/Mftf/Section/AdminSection.xml index 7b6c9f8ab3b79..4aea038bec716 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/AdminSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/AdminSection.xml @@ -13,5 +13,7 @@ <element name="SecurityTab" type="button" selector="#admin_security-head"/> <element name="AdminAccountSharing" type="button" selector="#admin_security_admin_account_sharing"/> <element name="EnableSystemValue" type="button" selector="#admin_security_admin_account_sharing_inherit"/> + <element name="systemValueForMaximumLoginFailures" type="checkbox" selector="#admin_security_lockout_failures_inherit"/> + <element name="MaximumLoginFailures" type="input" selector="#admin_security_lockout_failures"/> </section> </sections> diff --git a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/FieldTest.php b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/FieldTest.php index 6be1fe04b68dd..d942af9352e6c 100644 --- a/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/FieldTest.php +++ b/app/code/Magento/Config/Test/Unit/Block/System/Config/Form/FieldTest.php @@ -7,8 +7,6 @@ /** * Test how class render field html element in Stores Configuration - * - * @package Magento\Config\Test\Unit\Block\System\Config\Form */ class FieldTest extends \PHPUnit\Framework\TestCase { @@ -72,6 +70,7 @@ protected function setUp() 'getCanUseDefaultValue', 'setDisabled', 'getTooltip', + 'setReadonly' ] ); @@ -179,7 +178,8 @@ public function testRenderInheritCheckbox() $this->_elementMock->expects($this->any())->method('getCanUseWebsiteValue')->will($this->returnValue(true)); $this->_elementMock->expects($this->any())->method('getCanUseDefaultValue')->will($this->returnValue(true)); $this->_elementMock->expects($this->once())->method('setDisabled')->with(true); - $this->_elementMock->expects($this->once())->method('getIsDisableInheritance')->willReturn(true); + $this->_elementMock->method('getIsDisableInheritance')->willReturn(true); + $this->_elementMock->method('setReadonly')->with(true); $expected = '<td class="use-default">'; $expected .= '<input id="' . @@ -187,7 +187,7 @@ public function testRenderInheritCheckbox() '_inherit" name="' . $this->_testData['name'] . '[inherit]" type="checkbox" value="1"' . - ' class="checkbox config-inherit" checked="checked"' . ' disabled="disabled"' . + ' class="checkbox config-inherit" checked="checked"' . ' disabled="disabled"' . ' readonly="1"' . ' onclick="toggleValueElements(this, Element.previous(this.parentNode))" /> '; $expected .= '<label for="' . $this->_testData['htmlId'] . '_inherit" class="inherit">Use Website</label>'; diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/Dependency/FieldTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/Dependency/FieldTest.php index 750a829eef7ec..22cf979a9bd63 100644 --- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/Dependency/FieldTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/Element/Dependency/FieldTest.php @@ -12,6 +12,8 @@ class FieldTest extends \PHPUnit\Framework\TestCase */ const SIMPLE_VALUE = 'someValue'; + const EMPTY_VALUE = ''; + const COMPLEX_VALUE1 = 'value_1'; const COMPLEX_VALUE2 = 'value_2'; @@ -150,8 +152,19 @@ public function getValuesDataProvider() return [ [$this->_getSimpleData(), true, [self::SIMPLE_VALUE]], [$this->_getSimpleData(), false, [self::SIMPLE_VALUE]], + [$this->_getSimpleEmptyData(), false, [static::EMPTY_VALUE]], [$this->_getComplexData(), true, $complexDataValues], [$this->_getComplexData(), false, $complexDataValues] ]; } + + /** + * Providing a field data with no field value + * + * @return array + */ + protected function _getSimpleEmptyData(): array + { + return ['dependPath' => ['section_2', 'group_3', 'field_4']]; + } } diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/StructureTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/StructureTest.php index a17faf8f35883..57e57fcd2503f 100644 --- a/app/code/Magento/Config/Test/Unit/Model/Config/StructureTest.php +++ b/app/code/Magento/Config/Test/Unit/Model/Config/StructureTest.php @@ -50,6 +50,9 @@ class StructureTest extends \PHPUnit\Framework\TestCase */ protected $_structureData; + /** + * @inheritdoc + */ protected function setUp() { $this->_flyweightFactory = $this->getMockBuilder(FlyweightFactory::class) @@ -82,7 +85,12 @@ protected function setUp() ); } - public function testGetTabsBuildsSectionTree() + /** + * Verify tabs build section tree + * + * @return void + */ + public function testGetTabsBuildsSectionTree(): void { $expected = ['tab1' => ['children' => ['section1' => ['tab' => 'tab1']]]]; @@ -108,7 +116,12 @@ public function testGetTabsBuildsSectionTree() $this->assertEquals($this->_tabIteratorMock, $model->getTabs()); } - public function testGetSectionList() + /** + * Verify get section list method + * + * @return void + */ + public function testGetSectionList(): void { $expected = [ 'section1_child_id_1' => true, @@ -152,6 +165,8 @@ public function testGetSectionList() } /** + * Verify Get Element return empty element if element is requested + * * @param string $path * @param string $expectedType * @param string $expectedId @@ -174,6 +189,8 @@ public function testGetElementReturnsEmptyElementIfNotExistingElementIsRequested } /** + * Verify get Element return empty by path element if not exist + * * @param string $path * @param string $expectedType * @param string $expectedId @@ -196,6 +213,8 @@ public function testGetElementReturnsEmptyByConfigPathElementIfNotExistingElemen } /** + * Verify Element return e,pty element if not exists + * * @param string $expectedType * @param string $expectedId * @param string $expectedPath @@ -234,14 +253,24 @@ public function emptyElementDataProvider() ]; } - public function testGetElementReturnsProperElementByPath() + /** + * Verify get element returns proper element by path + * + * @return void + */ + public function testGetElementReturnsProperElementByPath(): void { $elementMock = $this->getElementPathReturnsProperElementByPath(); $this->assertEquals($elementMock, $this->_model->getElement('section_1/group_level_1/field_3')); } - public function testGetElementByConfigPathReturnsProperElementByPath() + /** + * Verify get element by config path return proper path + * + * @return void + */ + public function testGetElementByConfigPathReturnsProperElementByPath(): void { $elementMock = $this->getElementPathReturnsProperElementByPath(); @@ -249,6 +278,8 @@ public function testGetElementByConfigPathReturnsProperElementByPath() } /** + * Build mock element + * * @return Mock */ private function getElementPathReturnsProperElementByPath() @@ -271,7 +302,12 @@ private function getElementPathReturnsProperElementByPath() return $elementMock; } - public function testGetElementByPathPartsIfSectionDataIsEmpty() + /** + * Verefy get element by path part + * + * @return void + */ + public function testGetElementByPathPartsIfSectionDataIsEmpty(): void { $fieldData = [ 'id' => 'field_3', @@ -342,7 +378,12 @@ public function testGetFirstSectionReturnsFirstAllowedSection() $this->assertEquals('currentSection', $this->_model->getFirstSection()->getData()); } - public function testGetElementReturnsProperElementByPathCachesObject() + /** + * Verify get element return element by path caches object + * + * @return void + */ + public function testGetElementReturnsProperElementByPathCachesObject(): void { $elementMock = $this->getElementReturnsProperElementByPathCachesObject(); @@ -350,7 +391,12 @@ public function testGetElementReturnsProperElementByPathCachesObject() $this->assertEquals($elementMock, $this->_model->getElement('section_1/group_level_1/field_3')); } - public function testGetElementByConfigPathReturnsProperElementByPathCachesObject() + /** + * Verify Get Element by id returns proper element + * + * @return void + */ + public function testGetElementByConfigPathReturnsProperElementByPathCachesObject(): void { $elementMock = $this->getElementReturnsProperElementByPathCachesObject(); @@ -393,6 +439,8 @@ public function testGetFieldPathsByAttribute($attributeName, $attributeValue, $p } /** + * DataProvider + * * @return array */ public function getFieldPathsByAttributeDataProvider() @@ -411,33 +459,53 @@ public function getFieldPathsByAttributeDataProvider() ]; } - public function testGetFieldPaths() + /** + * Verify get Fields paths method + * + * @dataProvider getFieldPaths + * @param array $expected + * @return void + */ + public function testGetFieldPaths(array $expected): void { - $expected = [ - 'section/group/field2' => [ - 'field_2' - ], - 'field_3' => [ - 'field_3', - 'field_3' - ], - 'field_3_1' => [ - 'field_3_1' - ], - 'field_3_1_1' => [ - 'field_3_1_1' - ], - 'section/group/field4' => [ - 'field_4', - ], - 'field_5' => [ - 'field_5', - ], - ]; - $this->assertSame( $expected, $this->_model->getFieldPaths() ); } + + /** + * dataprovider for Field Paths + * + * @return array + */ + public function getFieldPaths(): array + { + return [ + [ + [ + 'section/group/field2' => [ + 'field_2' + ], + 'field_3' => [ + 'field_3', + 'field_3' + ], + 'field_3_1' => [ + 'field_3_1' + ], + 'field_3_1_1' => [ + 'field_3_1_1' + ], + 'section/group/field4' => [ + 'field_4', + ], + 'field_5' => [ + 'field_5', + 'field_5' + ] + ] + ] + ]; + } } diff --git a/app/code/Magento/Config/Test/Unit/Model/_files/converted_config.php b/app/code/Magento/Config/Test/Unit/Model/_files/converted_config.php index ef2447338dc07..ed6cc868f8d08 100644 --- a/app/code/Magento/Config/Test/Unit/Model/_files/converted_config.php +++ b/app/code/Magento/Config/Test/Unit/Model/_files/converted_config.php @@ -158,14 +158,14 @@ ], '_elementType' => 'field', ], - 'field_5' => [ + 'field_5' => [ 'id' => 'field_5', 'translate' => 'label', 'showInWebsite' => '1', 'type' => 'text', 'label' => '', '_elementType' => 'field', - ], + ], ], '_elementType' => 'group', ], @@ -190,6 +190,29 @@ ], '_elementType' => 'section', ], + 'section_3' => [ + 'id' => 'section_3', + 'type' => 'text', + '_elementType' => 'section', + 'children' => [ + 'group_5' => [ + 'id' => 'group_5', + 'type' => 'text', + 'showInDefault' => 1, + 'showInWebsite' => 1, + 'showInStore' => 1, + '_elementType' => 'group', + 'children' => [ + 'field_5' => [ + 'id' => 'field_5', + 'showInWebsite' => '1', + 'type' => 'text', + '_elementType' => 'field', + ] + ] + ] + ] + ] ], ], ], diff --git a/app/code/Magento/Config/Test/Unit/Model/_files/system_2.xml b/app/code/Magento/Config/Test/Unit/Model/_files/system_2.xml index c4001f47ced0b..715d7cf4e8a61 100644 --- a/app/code/Magento/Config/Test/Unit/Model/_files/system_2.xml +++ b/app/code/Magento/Config/Test/Unit/Model/_files/system_2.xml @@ -86,5 +86,11 @@ </depends> </group> </section> + <section id="section_3" type="text"> + <group id="group_5" type="text" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="field_5" showInWebsite="1" type="text"> + </field> + </group> + </section> </system> </config> diff --git a/app/code/Magento/Config/registration.php b/app/code/Magento/Config/registration.php index 8a57e9d5c2217..a6dd84b3a23ef 100644 --- a/app/code/Magento/Config/registration.php +++ b/app/code/Magento/Config/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Config', __DIR__); diff --git a/app/code/Magento/ConfigurableImportExport/registration.php b/app/code/Magento/ConfigurableImportExport/registration.php index 103565fb30a93..305ff345893c0 100644 --- a/app/code/Magento/ConfigurableImportExport/registration.php +++ b/app/code/Magento/ConfigurableImportExport/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_ConfigurableImportExport', __DIR__); diff --git a/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php index 71db9d32aa593..55c0c8f6ca4ce 100644 --- a/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php @@ -182,11 +182,10 @@ public function getAllowProducts() { if (!$this->hasAllowProducts()) { $products = []; - $skipSaleableCheck = $this->catalogProduct->getSkipSaleableCheck(); $allProducts = $this->getProduct()->getTypeInstance()->getUsedProducts($this->getProduct(), null); /** @var $product \Magento\Catalog\Model\Product */ foreach ($allProducts as $product) { - if ($skipSaleableCheck || ((int) $product->getStatus()) === Status::STATUS_ENABLED) { + if ((int) $product->getStatus() === Status::STATUS_ENABLED) { $products[] = $product; } } diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php index c60953e33e9eb..5b50cc0ebd5e0 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php @@ -9,12 +9,14 @@ use Magento\Catalog\Api\Data\ProductAttributeInterface; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\Data\ProductInterfaceFactory; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Config; use Magento\Catalog\Model\Product\Gallery\ReadHandler as GalleryReadHandler; use Magento\ConfigurableProduct\Model\Product\Type\Collection\SalableProcessor; use Magento\Framework\App\ObjectManager; use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\Api\SearchCriteriaBuilder; /** * Configurable product type implementation @@ -194,9 +196,18 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType */ private $salableProcessor; + /** + * @var ProductAttributeRepositoryInterface|null + */ + private $productAttributeRepository; + + /** + * @var SearchCriteriaBuilder|null + */ + private $searchCriteriaBuilder; + /** * @codingStandardsIgnoreStart/End - * * @param \Magento\Catalog\Model\Product\Option $catalogProductOption * @param \Magento\Eav\Model\Config $eavConfig * @param \Magento\Catalog\Model\Product\Type $catalogProductType @@ -214,9 +225,13 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType * @param \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable $catalogProductTypeConfigurable * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $extensionAttributesJoinProcessor + * @param \Magento\Framework\Cache\FrontendInterface|null $cache + * @param \Magento\Customer\Model\Session|null $customerSession * @param \Magento\Framework\Serialize\Serializer\Json $serializer * @param ProductInterfaceFactory $productFactory * @param SalableProcessor $salableProcessor + * @param ProductAttributeRepositoryInterface|null $productAttributeRepository + * @param SearchCriteriaBuilder|null $searchCriteriaBuilder * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -241,7 +256,9 @@ public function __construct( \Magento\Customer\Model\Session $customerSession = null, \Magento\Framework\Serialize\Serializer\Json $serializer = null, ProductInterfaceFactory $productFactory = null, - SalableProcessor $salableProcessor = null + SalableProcessor $salableProcessor = null, + ProductAttributeRepositoryInterface $productAttributeRepository = null, + SearchCriteriaBuilder $searchCriteriaBuilder = null ) { $this->typeConfigurableFactory = $typeConfigurableFactory; $this->_eavAttributeFactory = $eavAttributeFactory; @@ -256,6 +273,10 @@ public function __construct( $this->productFactory = $productFactory ?: ObjectManager::getInstance() ->get(ProductInterfaceFactory::class); $this->salableProcessor = $salableProcessor ?: ObjectManager::getInstance()->get(SalableProcessor::class); + $this->productAttributeRepository = $productAttributeRepository ?: + ObjectManager::getInstance()->get(ProductAttributeRepositoryInterface::class); + $this->searchCriteriaBuilder = $searchCriteriaBuilder ?: + ObjectManager::getInstance()->get(SearchCriteriaBuilder::class); parent::__construct( $catalogProductOption, $eavConfig, @@ -1231,19 +1252,16 @@ public function isPossibleBuyFromList($product) /** * Returns array of sub-products for specified configurable product - * - * $requiredAttributeIds - one dimensional array, if provided * Result array contains all children for specified configurable product * * @param \Magento\Catalog\Model\Product $product - * @param array $requiredAttributeIds + * @param array $requiredAttributeIds Attributes to include in the select; one-dimensional array * @return ProductInterface[] - * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getUsedProducts($product, $requiredAttributeIds = null) { if (!$product->hasData($this->_usedProducts)) { - $collection = $this->getConfiguredUsedProductCollection($product, false); + $collection = $this->getConfiguredUsedProductCollection($product, false, $requiredAttributeIds); $usedProducts = array_values($collection->getItems()); $product->setData($this->_usedProducts, $usedProducts); } @@ -1390,16 +1408,18 @@ private function getUsedProductsCacheKey($keyParts) /** * Prepare collection for retrieving sub-products of specified configurable product - * * Retrieve related products collection with additional configuration * * @param \Magento\Catalog\Model\Product $product * @param bool $skipStockFilter + * @param array $requiredAttributeIds Attributes to include in the select * @return \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Product\Collection + * @throws \Magento\Framework\Exception\LocalizedException */ private function getConfiguredUsedProductCollection( \Magento\Catalog\Model\Product $product, - $skipStockFilter = true + $skipStockFilter = true, + $requiredAttributeIds = null ) { $collection = $this->getUsedProductCollection($product); @@ -1407,8 +1427,19 @@ private function getConfiguredUsedProductCollection( $collection->setFlag('has_stock_status_filter', true); } + $attributesForSelect = $this->getAttributesForCollection($product); + if ($requiredAttributeIds) { + $this->searchCriteriaBuilder->addFilter('attribute_id', $requiredAttributeIds, 'in'); + $requiredAttributes = $this->productAttributeRepository + ->getList($this->searchCriteriaBuilder->create())->getItems(); + $requiredAttributeCodes = []; + foreach ($requiredAttributes as $requiredAttribute) { + $requiredAttributeCodes[] = $requiredAttribute->getAttributeCode(); + } + $attributesForSelect = array_unique(array_merge($attributesForSelect, $requiredAttributeCodes)); + } $collection - ->addAttributeToSelect($this->getAttributesForCollection($product)) + ->addAttributeToSelect($attributesForSelect) ->addFilterByRequiredOptions() ->setStoreId($product->getStoreId()); diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php index 01549ffcd2755..aa801088de7fd 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php @@ -264,9 +264,6 @@ public function setProductId($value) /** * @inheritdoc - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __sleep() { @@ -278,9 +275,6 @@ public function __sleep() /** * @inheritdoc - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __wakeup() { diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php index 3aa90c7b3ce57..57f701721a6f3 100644 --- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php +++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute/Collection.php @@ -331,6 +331,7 @@ protected function loadOptions() 'use_default_value' => true ]; } + $item->setOptionsMap($values); $values = array_values($values); $item->setOptions($values); } @@ -357,9 +358,6 @@ protected function getIncludedOptions(array $usedProducts, AbstractAttribute $pr /** * @inheritdoc * @since 100.0.6 - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __sleep() { @@ -379,9 +377,6 @@ public function __sleep() /** * @inheritdoc * @since 100.0.6 - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __wakeup() { diff --git a/app/code/Magento/ConfigurableProduct/Plugin/SalesRule/Model/Rule/Condition/Product.php b/app/code/Magento/ConfigurableProduct/Plugin/SalesRule/Model/Rule/Condition/Product.php index 1ed4432347b7a..c97b24c295189 100644 --- a/app/code/Magento/ConfigurableProduct/Plugin/SalesRule/Model/Rule/Condition/Product.php +++ b/app/code/Magento/ConfigurableProduct/Plugin/SalesRule/Model/Rule/Condition/Product.php @@ -56,7 +56,10 @@ private function getProductToValidate( $attrCode = $subject->getAttribute(); /* Check for attributes which are not available for configurable products */ - if ($product->getTypeId() == Configurable::TYPE_CODE && !$product->hasData($attrCode)) { + if ($product->getTypeId() == Configurable::TYPE_CODE && + !$product->hasData($attrCode) && + count($model->getChildren()) + ) { /** @var \Magento\Catalog\Model\AbstractModel $childProduct */ $childProduct = current($model->getChildren())->getProduct(); if ($childProduct->hasData($attrCode)) { diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AddNewProductConfigurationAttributeActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AddNewProductConfigurationAttributeActionGroup.xml new file mode 100644 index 0000000000000..5914d935d3c49 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AddNewProductConfigurationAttributeActionGroup.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddNewProductConfigurationAttributeActionGroup"> + <annotations> + <description>Generates the Product Configurations for the 2 provided Attribute Names on the Configurable Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="attribute" type="entity"/> + <argument name="firstOption" type="entity"/> + <argument name="secondOption" type="entity"/> + </arguments> + + <!-- Create new attribute --> + <click selector="{{AdminCreateProductConfigurationsPanel.createNewAttribute}}" stepKey="clickOnNewAttribute"/> + <waitForPageLoad stepKey="waitForIFrame"/> + <switchToIFrame selector="{{AdminNewAttributePanel.newAttributeIFrame}}" stepKey="switchToNewAttributeIFrame"/> + <fillField selector="{{AdminNewAttributePanel.defaultLabel}}" userInput="{{attribute.default_label}}" stepKey="fillDefaultLabel"/> + <click selector="{{AdminNewAttributePanel.saveAttribute}}" stepKey="clickOnNewAttributePanel"/> + <waitForPageLoad stepKey="waitForSaveAttribute"/> + <switchToIFrame stepKey="switchOutOfIFrame"/> + <waitForPageLoad stepKey="waitForFilters"/> + + <!-- Find created below attribute and add option; save attribute --> + <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickOnFilters"/> + <fillField userInput="{{attribute.default_label}}" selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" stepKey="fillFilterAttributeCodeField"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminCreateProductConfigurationsPanel.firstCheckbox}}" stepKey="clickOnFirstCheckbox"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="waitCreateNewValueAppears"/> + <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateFirstNewValue"/> + <fillField userInput="{{firstOption.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewFirstOption"/> + <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute"/> + <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateSecondNewValue"/> + <fillField userInput="{{secondOption.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewSecondOption"/> + <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveAttribute"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAll}}" stepKey="clickOnSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnSecondNextButton"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnThirdNextButton"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnFourthNextButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AddProductToConfigurationsGridActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AddProductToConfigurationsGridActionGroup.xml new file mode 100644 index 0000000000000..f9bd485fa4511 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AddProductToConfigurationsGridActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddProductToConfigurationsGridActionGroup"> + <annotations> + <description>Adds the provided Product SKU to the provided Product Name.</description> + </annotations> + <arguments> + <argument name="sku" type="string"/> + <argument name="name" type="string"/> + </arguments> + + <click selector="{{AdminProductFormConfigurationsSection.actionsBtnByProductName(name)}}" stepKey="clickToExpandFirstActions"/> + <click selector="{{AdminProductFormConfigurationsSection.addProduct(name)}}" stepKey="clickChooseFirstDifferentProduct"/> + <switchToIFrame stepKey="switchOutOfIFrame"/> + <waitForPageLoad stepKey="waitForFilters"/> + <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickFilters"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{sku}}" stepKey="fillProductSkuFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <click selector="{{AdminProductGridFilterSection.firstRowBySku(sku)}}" stepKey="clickOnFirstRow"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AddUniqueImageToConfigurableProductOptionActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AddUniqueImageToConfigurableProductOptionActionGroup.xml new file mode 100644 index 0000000000000..26fe50d845dbc --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AddUniqueImageToConfigurableProductOptionActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddUniqueImageToConfigurableProductOptionActionGroup"> + <annotations> + <description>Adds the provided Image to a Configurable Product on the Admin Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="image" defaultValue="ProductImage"/> + <argument name="frontend_label" type="string"/> + <argument name="label" type="string"/> + </arguments> + + <click selector="{{AdminCreateProductConfigurationsPanel.applyUniqueImagesToEachSkus}}" stepKey="clickOnApplyUniqueImagesToEachSku"/> + <selectOption userInput="{{frontend_label}}" selector="{{AdminCreateProductConfigurationsPanel.selectImagesButton}}" stepKey="selectOption"/> + <attachFile selector="{{AdminCreateProductConfigurationsPanel.uploadImagesButton(label)}}" userInput="{{image.file}}" stepKey="uploadFile"/> + <waitForElementNotVisible selector="{{AdminCreateProductConfigurationsPanel.uploadProgressBar}}" stepKey="waitForUpload"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.imageFile(image.fileName)}}" stepKey="waitForThumbnail"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AddUniquePriceToConfigurableProductOptionActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AddUniquePriceToConfigurableProductOptionActionGroup.xml new file mode 100644 index 0000000000000..3852dc5c821ed --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AddUniquePriceToConfigurableProductOptionActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddUniquePriceToConfigurableProductOptionActionGroup"> + <annotations> + <description>On the 'Step 3: Bulk Images, Price and Quantity' page of the 'Create Product Configurations' model click on 'Apply unique prices...'. Select provided Option. Fill price.</description> + </annotations> + <arguments> + <argument name="frontend_label" type="string"/> + <argument name="label" type="string"/> + <argument name="price" type="string"/> + </arguments> + + <click selector="{{AdminCreateProductConfigurationsPanel.applyUniquePricesToEachSkus}}" stepKey="clickOnApplyUniquePricesToEachSku"/> + <selectOption userInput="{{frontend_label}}" selector="{{AdminCreateProductConfigurationsPanel.selectPriceButton}}" stepKey="selectOption"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.price(label)}}" userInput="{{price}}" stepKey="enterAttributeQuantity"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AddUniqueQuantityToConfigurableProductOptionActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AddUniqueQuantityToConfigurableProductOptionActionGroup.xml new file mode 100644 index 0000000000000..b92a847df8f1d --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AddUniqueQuantityToConfigurableProductOptionActionGroup.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="AddUniqueQuantityToConfigurableProductOptionActionGroup"> + <arguments> + <argument name="frontend_label" type="string" defaultValue="{{productAttributeColor.default_label}}"/> + <argument name="label" type="string" defaultValue="option1"/> + <argument name="quantity" type="string" defaultValue="10"/> + </arguments> + <click selector="{{AdminCreateProductConfigurationsPanel.applyUniqueQuantityToEachSkus}}" stepKey="clickOnApplyUniqueQuantitiesToEachSku"/> + <selectOption selector="{{AdminCreateProductConfigurationsPanel.selectQuantity}}" userInput="{{frontend_label}}" stepKey="selectOption"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.applyUniqueQuantity(label)}}" userInput="{{quantity}}" stepKey="enterAttributeQuantity"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminChangeConfigurableProductVariationQtyActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminChangeConfigurableProductVariationQtyActionGroup.xml new file mode 100644 index 0000000000000..3441b979226dd --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminChangeConfigurableProductVariationQtyActionGroup.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="AdminChangeConfigurableProductVariationQty"> + <annotations> + <description>Change quantity value for configurable product generated variation</description> + </annotations> + <arguments> + <argument name="rowIndex" type="string" defaultValue="0"/> + <argument name="quantity" type="string" defaultValue="0"/> + </arguments> + <fillField selector="{{AdminCreateProductConfigurationsPanel.variationQty(rowIndex)}}" userInput="{{quantity}}" stepKey="fillVariationQuantity"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml deleted file mode 100644 index 488667a4585bb..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml +++ /dev/null @@ -1,504 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Filter the product grid and view expected products--> - <actionGroup name="viewConfigurableProductInAdminGrid"> - <annotations> - <description>Goes to the Admin Product grid page. Validates the provided Configurable Product is present and correct in the grid.</description> - </annotations> - <arguments> - <argument name="product" defaultValue="_defaultProduct"/> - </arguments> - - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> - <waitForPageLoad stepKey="waitForPageLoadInitial"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillProductSkuFilter"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <seeNumberOfElements selector="{{AdminProductGridSection.productGridRows}}" userInput="3" stepKey="seeCorrectNumberOfProducts"/> - - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFiltersSimple"/> - <selectOption selector="{{AdminProductGridFilterSection.typeFilter}}" userInput="simple" stepKey="selectionProductType"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFiltersWithSimpleType"/> - <see selector="{{AdminProductGridSection.firstProductRow}}" userInput="{{product.name}}" stepKey="seeSimpleProductNameInGrid"/> - <see selector="{{AdminProductGridSection.firstProductRow}}" userInput="{{product.price}}" stepKey="seeSimpleProductPriceInGrid"/> - - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFiltersConfigurable"/> - <selectOption selector="{{AdminProductGridFilterSection.typeFilter}}" userInput="{{product.type_id}}" stepKey="selectionConfigurableProductType"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFiltersWithConfigurableType"/> - <see selector="{{AdminProductGridSection.firstProductRow}}" userInput="{{product.name}}" stepKey="seeConfigurableProductNameInGrid"/> - <dontSee selector="{{AdminProductGridSection.firstProductRow}}" userInput="{{product.price}}" stepKey="dontSeeProductPriceNameInGrid"/> - - <click selector="{{AdminProductGridFilterSection.clearFilters}}" stepKey="clickClearFiltersAfter"/> - </actionGroup> - - <!--Create a configurable product with three options for color: red, white, and blue - Expected start state = logged in as an admin - End state = on the product edit page in the admin--> - <actionGroup name="createConfigurableProduct"> - <annotations> - <description>Goes to the Admin Product grid page. Creates a Configurable Product using the default Product Options.</description> - </annotations> - <arguments> - <argument name="product" defaultValue="_defaultProduct"/> - <argument name="category" defaultValue="_defaultCategory"/> - </arguments> - - <!-- fill in basic configurable product values --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> - <waitForPageLoad time="30" stepKey="wait1"/> - <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickOnAddProductToggle"/> - <click selector="{{AdminProductGridActionSection.addConfigurableProduct}}" stepKey="clickOnAddConfigurableProduct"/> - <fillField userInput="{{product.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> - <fillField userInput="{{product.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> - <fillField userInput="{{product.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> - <fillField userInput="{{product.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> - <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{category.name}}]" stepKey="fillCategory"/> - <selectOption userInput="{{product.visibility}}" selector="{{AdminProductFormSection.visibility}}" stepKey="fillVisibility"/> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> - <fillField userInput="{{product.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> - - <!-- create configurations for colors the product is available in --> - <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickOnCreateConfigurations"/> - <click selector="{{AdminCreateProductConfigurationsPanel.createNewAttribute}}" stepKey="clickOnNewAttribute"/> - <waitForPageLoad stepKey="waitForIFrame"/> - <switchToIFrame selector="{{AdminNewAttributePanel.newAttributeIFrame}}" stepKey="switchToNewAttributeIFrame"/> - <fillField selector="{{AdminNewAttributePanel.defaultLabel}}" userInput="{{colorProductAttribute.default_label}}" stepKey="fillDefaultLabel"/> - <click selector="{{AdminNewAttributePanel.saveAttribute}}" stepKey="clickOnNewAttributePanel"/> - <waitForPageLoad stepKey="waitForSaveAttribute"/> - <switchToIFrame stepKey="switchOutOfIFrame"/> - <waitForPageLoad stepKey="waitForFilters"/> - <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickOnFilters"/> - <fillField userInput="{{colorProductAttribute.default_label}}" selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" stepKey="fillFilterAttributeCodeField"/> - <click selector="{{AdminCreateProductConfigurationsPanel.applyFilters}}" stepKey="clickApplyFiltersButton"/> - <click selector="{{AdminCreateProductConfigurationsPanel.firstCheckbox}}" stepKey="clickOnFirstCheckbox"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> - <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="waitCreateNewValueAppears"/> - <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue1"/> - <fillField userInput="{{colorProductAttribute1.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewAttribute1"/> - <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute1"/> - <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue2"/> - <fillField userInput="{{colorProductAttribute2.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewAttribute2"/> - <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute2"/> - <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue3"/> - <fillField userInput="{{colorProductAttribute3.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewAttribute3"/> - <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute3"/> - <click selector="{{AdminCreateProductConfigurationsPanel.selectAll}}" stepKey="clickOnSelectAll"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> - <click selector="{{AdminCreateProductConfigurationsPanel.applyUniquePricesByAttributeToEachSku}}" stepKey="clickOnApplyUniquePricesByAttributeToEachSku"/> - <selectOption selector="{{AdminCreateProductConfigurationsPanel.selectAttribute}}" userInput="{{colorProductAttribute.default_label}}" stepKey="selectAttributes"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.attribute1}}" userInput="{{colorProductAttribute1.price}}" stepKey="fillAttributePrice1"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.attribute2}}" userInput="{{colorProductAttribute2.price}}" stepKey="fillAttributePrice2"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.attribute3}}" userInput="{{colorProductAttribute3.price}}" stepKey="fillAttributePrice3"/> - <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="1" stepKey="enterAttributeQuantity"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton2"/> - <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> - <seeInTitle userInput="{{product.name}}" stepKey="seeProductNameInTitle"/> - </actionGroup> - - <actionGroup name="createConfigurableProductWithTwoAttributes" extends="createConfigurableProduct"> - <annotations> - <description>Goes to the Admin Product grid page. Creates a Configurable Product with 2 product attributes.</description> - </annotations> - <arguments> - <argument name="attribute1" defaultValue="ProductColorAttribute"/> - <argument name="attribute2" defaultValue="ProductSizeAttribute"/> - </arguments> - <remove keyForRemoval="clickOnNewAttribute"/> - <remove keyForRemoval="waitForIFrame"/> - <remove keyForRemoval="switchToNewAttributeIFrame"/> - <remove keyForRemoval="fillDefaultLabel"/> - <remove keyForRemoval="clickOnNewAttributePanel"/> - <remove keyForRemoval="waitForSaveAttribute"/> - <remove keyForRemoval="switchOutOfIFrame"/> - <remove keyForRemoval="waitForFilters"/> - <remove keyForRemoval="clickOnFilters"/> - <remove keyForRemoval="fillFilterAttributeCodeField"/> - <remove keyForRemoval="clickApplyFiltersButton"/> - <remove keyForRemoval="clickOnFirstCheckbox"/> - <click selector="{{AdminCreateProductConfigurationsPanel.attributeCheckbox(attribute1.attribute_code)}}" stepKey="selectAttribute1" after="clickOnCreateConfigurations"/> - <click selector="{{AdminCreateProductConfigurationsPanel.attributeCheckbox(attribute2.attribute_code)}}" stepKey="selectAttribute2" after="selectAttribute1"/> - <remove keyForRemoval="waitCreateNewValueAppears"/> - <remove keyForRemoval="clickOnCreateNewValue1"/> - <remove keyForRemoval="fillFieldForNewAttribute1"/> - <remove keyForRemoval="clickOnSaveNewAttribute1"/> - <remove keyForRemoval="clickOnCreateNewValue2"/> - <remove keyForRemoval="fillFieldForNewAttribute2"/> - <remove keyForRemoval="clickOnSaveNewAttribute2"/> - <remove keyForRemoval="clickOnCreateNewValue3"/> - <remove keyForRemoval="fillFieldForNewAttribute3"/> - <remove keyForRemoval="clickOnSaveNewAttribute3"/> - <remove keyForRemoval="clickOnSelectAll"/> - <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute(attribute1.frontend_label)}}" stepKey="selectAllOptionsOfAttribute1" before="clickOnNextButton2"/> - <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute(attribute2.frontend_label)}}" stepKey="selectAllOptionsOfAttribute2" before="clickOnNextButton2"/> - <remove keyForRemoval="applyUniquePricesByAttributeToEachSku"/> - <remove keyForRemoval="clickOnApplyUniquePricesByAttributeToEachSku"/> - <remove keyForRemoval="selectAttributes"/> - <remove keyForRemoval="fillAttributePrice1"/> - <remove keyForRemoval="fillAttributePrice2"/> - <remove keyForRemoval="fillAttributePrice3"/> - <remove keyForRemoval="clickOnSaveButton2"/> - <remove keyForRemoval="clickOnConfirmInPopup"/> - <remove keyForRemoval="seeSaveProductMessage"/> - <remove keyForRemoval="seeProductNameInTitle"/> - </actionGroup> - <actionGroup name="saveConfigurableProduct"> - <annotations> - <description>Save configurable product</description> - </annotations> - <arguments> - <argument name="product" defaultValue="_defaultProduct"/> - </arguments> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton"/> - <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> - <seeInTitle userInput="{{product.name}}" stepKey="seeProductNameInTitle"/> - </actionGroup> - - <actionGroup name="generateConfigurationsByAttributeCode"> - <annotations> - <description>Generates the Product Configurations for the provided Attribute Code on the Configurable Product creation/edit page.</description> - </annotations> - <arguments> - <argument name="attributeCode" type="string" defaultValue="SomeString"/> - </arguments> - - <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickCreateConfigurations"/> - <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickFilters"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" userInput="{{attributeCode}}" stepKey="fillFilterAttributeCodeField"/> - <click selector="{{AdminCreateProductConfigurationsPanel.applyFilters}}" stepKey="clickApplyFiltersButton"/> - <click selector="{{AdminCreateProductConfigurationsPanel.firstCheckbox}}" stepKey="clickOnFirstCheckbox"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> - <click selector="{{AdminCreateProductConfigurationsPanel.selectAll}}" stepKey="clickOnSelectAll"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> - <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="99" stepKey="enterAttributeQuantity"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> - </actionGroup> - <actionGroup name="createOptionsForAttribute"> - <arguments> - <argument name="attributeName" type="string" defaultValue="{{productAttributeColor.default_label}}"/> - <argument name="firstOptionName" type="string" defaultValue="option1"/> - <argument name="secondOptionName" type="string" defaultValue="option2"/> - </arguments> - <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickOnFilters"/> - <fillField userInput="{{attributeName}}" selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" stepKey="fillFilterAttributeCodeField"/> - <click selector="{{AdminCreateProductConfigurationsPanel.applyFilters}}" stepKey="clickApplyFiltersButton"/> - <click selector="{{AdminCreateProductConfigurationsPanel.firstCheckbox}}" stepKey="clickOnFirstCheckbox"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton"/> - <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="waitCreateNewValueAppears"/> - <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateFirstNewValue"/> - <fillField userInput="{{firstOptionName}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewFirstOption"/> - <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute"/> - <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateSecondNewValue"/> - <fillField userInput="{{secondOptionName}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewSecondOption"/> - <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveAttribute"/> - <click selector="{{AdminCreateProductConfigurationsPanel.selectAll}}" stepKey="clickOnSelectAll"/> - </actionGroup> - - <actionGroup name="createConfigurationsForAttribute" extends="generateConfigurationsByAttributeCode"> - <annotations> - <description>EXTENDS: generateConfigurationsByAttributeCode. Clicks on the Save button. Clicks on the Confirm button.</description> - </annotations> - <arguments> - <argument name="attributeCode" type="string" defaultValue="SomeString"/> - </arguments> - - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton2"/> - <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/> - </actionGroup> - - <actionGroup name="createConfigurationsForAttributeWithImages" extends="generateConfigurationsByAttributeCode"> - <annotations> - <description>EXTENDS: generateConfigurationsByAttributeCode. Adds the provided Attribute Image to the provided Attribute Code.</description> - </annotations> - <arguments> - <argument name="attributeCode" type="string" defaultValue="SomeString"/> - <argument name="image" defaultValue="ProductImage"/> - </arguments> - - <click selector="{{AdminCreateProductConfigurationsPanel.applySingleSetOfImages}}" stepKey="clickOnApplySingleImageSetToAllSku" after="enterAttributeQuantity"/> - <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.imageUploadButton}}" stepKey="seeImageSectionIsReady" after="clickOnApplySingleImageSetToAllSku"/> - <attachFile selector="{{AdminCreateProductConfigurationsPanel.imageFileUpload}}" userInput="{{image.file}}" stepKey="uploadFile" after="seeImageSectionIsReady"/> - <waitForElementNotVisible selector="{{AdminCreateProductConfigurationsPanel.uploadProgressBar}}" stepKey="waitForUpload" after="uploadFile"/> - <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.imageFile(image.fileName)}}" stepKey="waitForThumbnail" after="waitForUpload"/> - - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton2" after="clickOnNextButton4"/> - <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup" after="clickOnSaveButton2"/> - </actionGroup> - - <actionGroup name="createConfigurationsForTwoAttribute" extends="generateConfigurationsByAttributeCode"> - <annotations> - <description>EXTENDS: generateConfigurationsByAttributeCode. Generates the Product Configurations for the 2 provided Attribute Codes on the Configurable Product creation/edit page.</description> - </annotations> - <arguments> - <argument name="secondAttributeCode" type="string"/> - </arguments> - - <remove keyForRemoval="clickOnSelectAll"/> - <remove keyForRemoval="clickFilters"/> - <remove keyForRemoval="fillFilterAttributeCodeField"/> - <remove keyForRemoval="clickApplyFiltersButton"/> - <remove keyForRemoval="clickOnFirstCheckbox"/> - - <click selector="{{AdminCreateProductConfigurationsPanel.attributeCheckbox(attributeCode)}}" stepKey="clickOnFirstAttributeCheckbox" after="clickCreateConfigurations"/> - <click selector="{{AdminCreateProductConfigurationsPanel.attributeCheckbox(secondAttributeCode)}}" stepKey="clickOnSecondAttributeCheckbox" after="clickOnFirstAttributeCheckbox"/> - <grabTextFrom selector="{{AdminCreateProductConfigurationsPanel.defaultLabel(attributeCode)}}" stepKey="grabFirstAttributeDefaultLabel" after="clickOnSecondAttributeCheckbox"/> - <grabTextFrom selector="{{AdminCreateProductConfigurationsPanel.defaultLabel(secondAttributeCode)}}" stepKey="grabSecondAttributeDefaultLabel" after="grabFirstAttributeDefaultLabel"/> - <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute({$grabFirstAttributeDefaultLabel})}}" stepKey="clickOnSelectAllForFirstAttribute" after="clickOnNextButton1"/> - <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute({$grabSecondAttributeDefaultLabel})}}" stepKey="clickOnSelectAllForSecondAttribute" after="clickOnSelectAllForFirstAttribute"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton2"/> - <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/> - </actionGroup> - - <actionGroup name="saveConfiguredProduct"> - <annotations> - <description>Save the Configurable Product on the Configurable Product creation/edit page. Validates that the Success Message is present.</description> - </annotations> - - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton2"/> - <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> - </actionGroup> - - <!--Generate and save configurable product after setting options--> - <actionGroup name="GenerateAndSaveConfiguredProductAfterSettingOptions" extends="saveConfiguredProduct"> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" before="clickOnSaveButton2" stepKey="clickOnNextButton"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" after="clickOnNextButton" stepKey="clickOnGenerateProductsButton"/> - </actionGroup> - - <actionGroup name="addNewProductConfigurationAttribute"> - <annotations> - <description>Generates the Product Configurations for the 2 provided Attribute Names on the Configurable Product creation/edit page.</description> - </annotations> - <arguments> - <argument name="attribute" type="entity"/> - <argument name="firstOption" type="entity"/> - <argument name="secondOption" type="entity"/> - </arguments> - - <!-- Create new attribute --> - <click selector="{{AdminCreateProductConfigurationsPanel.createNewAttribute}}" stepKey="clickOnNewAttribute"/> - <waitForPageLoad stepKey="waitForIFrame"/> - <switchToIFrame selector="{{AdminNewAttributePanel.newAttributeIFrame}}" stepKey="switchToNewAttributeIFrame"/> - <fillField selector="{{AdminNewAttributePanel.defaultLabel}}" userInput="{{attribute.default_label}}" stepKey="fillDefaultLabel"/> - <click selector="{{AdminNewAttributePanel.saveAttribute}}" stepKey="clickOnNewAttributePanel"/> - <waitForPageLoad stepKey="waitForSaveAttribute"/> - <switchToIFrame stepKey="switchOutOfIFrame"/> - <waitForPageLoad stepKey="waitForFilters"/> - - <!-- Find created below attribute and add option; save attribute --> - <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickOnFilters"/> - <fillField userInput="{{attribute.default_label}}" selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" stepKey="fillFilterAttributeCodeField"/> - <click selector="{{AdminCreateProductConfigurationsPanel.applyFilters}}" stepKey="clickApplyFiltersButton"/> - <click selector="{{AdminCreateProductConfigurationsPanel.firstCheckbox}}" stepKey="clickOnFirstCheckbox"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton"/> - <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="waitCreateNewValueAppears"/> - <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateFirstNewValue"/> - <fillField userInput="{{firstOption.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewFirstOption"/> - <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute"/> - <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateSecondNewValue"/> - <fillField userInput="{{secondOption.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewSecondOption"/> - <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveAttribute"/> - <click selector="{{AdminCreateProductConfigurationsPanel.selectAll}}" stepKey="clickOnSelectAll"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnSecondNextButton"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnThirdNextButton"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnFourthNextButton"/> - </actionGroup> - <actionGroup name="selectCreatedAttributeAndCreateTwoOptions" extends="addNewProductConfigurationAttribute"> - <remove keyForRemoval="clickOnNewAttribute"/> - <remove keyForRemoval="waitForIFrame"/> - <remove keyForRemoval="switchToNewAttributeIFrame"/> - <remove keyForRemoval="fillDefaultLabel"/> - <remove keyForRemoval="clickOnNewAttributePanel"/> - <remove keyForRemoval="waitForSaveAttribute"/> - <remove keyForRemoval="switchOutOfIFrame"/> - <remove keyForRemoval="waitForFilters"/> - <fillField userInput="{{attribute.attribute_code}}" selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" stepKey="fillFilterAttributeCodeField"/> - <fillField userInput="{{firstOption.label}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewFirstOption"/> - <fillField userInput="{{secondOption.label}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewSecondOption"/> - <remove keyForRemoval="clickOnSelectAll"/> - <remove keyForRemoval="clickOnSecondNextButton"/> - <remove keyForRemoval="clickOnThirdNextButton"/> - <remove keyForRemoval="clickOnFourthNextButton"/> - </actionGroup> - <actionGroup name="changeProductConfigurationsInGrid"> - <annotations> - <description>Edit the Product Configuration via the Admin Product grid page.</description> - </annotations> - <arguments> - <argument name="firstOption" type="entity"/> - <argument name="secondOption" type="entity"/> - </arguments> - - <fillField userInput="{{firstOption.name}}" selector="{{AdminProductFormConfigurationsSection.confProductNameCell(firstOption.name)}}" stepKey="fillFieldNameForFirstAttributeOption"/> - <fillField userInput="{{secondOption.name}}" selector="{{AdminProductFormConfigurationsSection.confProductNameCell(secondOption.name)}}" stepKey="fillFieldNameForSecondAttributeOption"/> - <fillField userInput="{{firstOption.sku}}" selector="{{AdminProductFormConfigurationsSection.confProductSkuCell(firstOption.name)}}" stepKey="fillFieldSkuForFirstAttributeOption"/> - <fillField userInput="{{secondOption.sku}}" selector="{{AdminProductFormConfigurationsSection.confProductSkuCell(secondOption.name)}}" stepKey="fillFieldSkuForSecondAttributeOption"/> - <fillField userInput="{{firstOption.price}}" selector="{{AdminProductFormConfigurationsSection.confProductPriceCell(firstOption.name)}}" stepKey="fillFieldPriceForFirstAttributeOption"/> - <fillField userInput="{{secondOption.price}}" selector="{{AdminProductFormConfigurationsSection.confProductPriceCell(secondOption.name)}}" stepKey="fillFieldPriceForSecondAttributeOption"/> - <fillField userInput="{{firstOption.quantity}}" selector="{{AdminProductFormConfigurationsSection.confProductQuantityCell(firstOption.name)}}" stepKey="fillFieldQuantityForFirstAttributeOption"/> - <fillField userInput="{{secondOption.quantity}}" selector="{{AdminProductFormConfigurationsSection.confProductQuantityCell(secondOption.name)}}" stepKey="fillFieldQuantityForSecondAttributeOption"/> - <fillField userInput="{{firstOption.weight}}" selector="{{AdminProductFormConfigurationsSection.confProductWeightCell(firstOption.name)}}" stepKey="fillFieldWeightForFirstAttributeOption"/> - <fillField userInput="{{secondOption.weight}}" selector="{{AdminProductFormConfigurationsSection.confProductWeightCell(secondOption.name)}}" stepKey="fillFieldWeightForSecondAttributeOption"/> - </actionGroup> - <actionGroup name="changeConfigurableProductChildProductPrice"> - <annotations> - <description>Change the price of a configurable child product in the grid under configurations.</description> - </annotations> - <arguments> - <argument name="productAttributes" type="string"/> - <argument name="productPrice" type="string"/> - </arguments> - <fillField userInput="{{productPrice}}" selector="{{AdminProductFormConfigurationsSection.confProductPriceCell(productAttributes)}}" stepKey="fillPriceForConfigurableProductAttributeOption"/> - </actionGroup> - <actionGroup name="changeProductConfigurationsInGridExceptSku" extends="changeProductConfigurationsInGrid"> - <annotations> - <description>EXTENDS: changeProductConfigurationsInGrid. Removes 'fillFieldSkuForFirstAttributeOption' and 'fillFieldSkuForSecondAttributeOption'.</description> - </annotations> - - <remove keyForRemoval="fillFieldSkuForFirstAttributeOption"/> - <remove keyForRemoval="fillFieldSkuForSecondAttributeOption"/> - </actionGroup> - - <actionGroup name="addProductToConfigurationsGrid"> - <annotations> - <description>Adds the provided Product SKU to the provided Product Name.</description> - </annotations> - <arguments> - <argument name="sku" type="string"/> - <argument name="name" type="string"/> - </arguments> - - <click selector="{{AdminProductFormConfigurationsSection.actionsBtnByProductName(name)}}" stepKey="clickToExpandFirstActions"/> - <click selector="{{AdminProductFormConfigurationsSection.addProduct(name)}}" stepKey="clickChooseFirstDifferentProduct"/> - <switchToIFrame stepKey="switchOutOfIFrame"/> - <waitForPageLoad stepKey="waitForFilters"/> - <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickFilters"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{sku}}" stepKey="fillProductSkuFilter"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <click selector="{{AdminProductGridFilterSection.firstRowBySku(sku)}}" stepKey="clickOnFirstRow"/> - </actionGroup> - - <actionGroup name="addUniqueImageToConfigurableProductOption"> - <annotations> - <description>Adds the provided Image to a Configurable Product on the Admin Product creation/edit page.</description> - </annotations> - <arguments> - <argument name="image" defaultValue="ProductImage"/> - <argument name="frontend_label" type="string"/> - <argument name="label" type="string"/> - </arguments> - - <click selector="{{AdminCreateProductConfigurationsPanel.applyUniqueImagesToEachSkus}}" stepKey="clickOnApplyUniqueImagesToEachSku"/> - <selectOption userInput="{{frontend_label}}" selector="{{AdminCreateProductConfigurationsPanel.selectImagesButton}}" stepKey="selectOption"/> - <attachFile selector="{{AdminCreateProductConfigurationsPanel.uploadImagesButton(label)}}" userInput="{{image.file}}" stepKey="uploadFile"/> - <waitForElementNotVisible selector="{{AdminCreateProductConfigurationsPanel.uploadProgressBar}}" stepKey="waitForUpload"/> - <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.imageFile(image.fileName)}}" stepKey="waitForThumbnail"/> - </actionGroup> - - <actionGroup name="addUniquePriceToConfigurableProductOption"> - <annotations> - <description>On the 'Step 3: Bulk Images, Price and Quantity' page of the 'Create Product Configurations' model click on 'Apply unique prices...'. Select provided Option. Fill price.</description> - </annotations> - <arguments> - <argument name="frontend_label" type="string"/> - <argument name="label" type="string"/> - <argument name="price" type="string"/> - </arguments> - - <click selector="{{AdminCreateProductConfigurationsPanel.applyUniquePricesToEachSkus}}" stepKey="clickOnApplyUniquePricesToEachSku"/> - <selectOption userInput="{{frontend_label}}" selector="{{AdminCreateProductConfigurationsPanel.selectPriceButton}}" stepKey="selectOption"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.price(label)}}" userInput="{{price}}" stepKey="enterAttributeQuantity"/> - </actionGroup> - <actionGroup name="addUniqueQuantityToConfigurableProductOption"> - <arguments> - <argument name="frontend_label" type="string" defaultValue="{{productAttributeColor.default_label}}"/> - <argument name="label" type="string" defaultValue="option1"/> - <argument name="quantity" type="string" defaultValue="10"/> - </arguments> - <click selector="{{AdminCreateProductConfigurationsPanel.applyUniqueQuantityToEachSkus}}" stepKey="clickOnApplyUniqueQuantitiesToEachSku"/> - <selectOption selector="{{AdminCreateProductConfigurationsPanel.selectQuantity}}" userInput="{{frontend_label}}" stepKey="selectOption"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.applyUniqueQuantity(label)}}" userInput="{{quantity}}" stepKey="enterAttributeQuantity"/> - </actionGroup> - - <actionGroup name="saveConfigurableProductWithNewAttributeSet"> - <annotations> - <description>Clicks on 'Save'. Clicks radio for '...new Attribute Set...' in the 'Choose Affected Attribute Set' modal. Clicks on 'Confirm' in the model on the Configurable Product creation/edit page.</description> - </annotations> - - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveConfigurableProduct"/> - <waitForElementVisible selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" time="30" stepKey="waitForAttributeSetConfirmation"/> - <click selector="{{AdminChooseAffectedAttributeSetPopup.addNewAttrSet}}" stepKey="clickAddNewAttributeSet"/> - <fillField selector="{{AdminChooseAffectedAttributeSetPopup.createNewAttrSetName}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="fillFieldNewAttrSetName"/> - <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickConfirmAttributeSet"/> - <see selector="You saved the product" stepKey="seeConfigurableSaveConfirmation" after="clickConfirmAttributeSet"/> - </actionGroup> - - <actionGroup name="saveConfigurableProductAddToCurrentAttributeSet"> - <annotations> - <description>Clicks on 'Save'. Clicks on 'Confirm' in the 'Choose Affected Attribute Set' model on the Configurable Product creation/edit page.</description> - </annotations> - - <waitForElementVisible selector="{{AdminProductFormActionSection.saveButton}}" stepKey="waitForSaveBtnVisible"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProductAgain"/> - <waitForElementVisible selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="waitPopUpVisible"/> - <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmPopup"/> - <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSaveProductMessage"/> - </actionGroup> - - <actionGroup name="assertConfigurableProductOnAdminProductPage"> - <annotations> - <description>Validates that the provided Configurable Product Name, SKU and Price are present and correct on the Configurable Product creation/edit page. PLEASE NOTE: The Product Data is Hardcoded.</description> - </annotations> - <arguments> - <argument name="product" type="entity"/> - </arguments> - - <seeInField userInput="{{ApiConfigurableProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="seeNameRequired"/> - <seeInField userInput="{{ApiConfigurableProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="seeSkuRequired"/> - <dontSeeInField userInput="{{ApiConfigurableProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="dontSeePriceRequired"/> - </actionGroup> - - <!--Click in Next Step and see Title--> - <actionGroup name="AdminConfigurableWizardMoveToNextStepActionGroup"> - <annotations> - <description>Click on the 'Next' button in the 'Create Product Configurations' panel on the Configurable Product creation/edit page.</description> - </annotations> - <arguments> - <argument name="title" type="string"/> - </arguments> - - <click selector="{{ConfigurableProductSection.nextButton}}" stepKey="clickNextButton"/> - <waitForPageLoad stepKey="waitForNextStepLoaded"/> - <see userInput="{{title}}" selector="{{AdminProductFormConfigurationsSection.stepsWizardTitle}}" stepKey="seeStepTitle"/> - </actionGroup> - <actionGroup name="AdminConfigurableProductDisableConfigurationsActionGroup"> - <arguments> - <argument name="productName" type="string" defaultValue="{{SimpleProduct.name}}"/> - </arguments> - <click selector="{{AdminProductFormConfigurationsSection.actionsBtnByProductName(productName)}}" stepKey="clickToExpandActionsSelect"/> - <click selector="{{AdminProductFormConfigurationsSection.disableProductBtn}}" stepKey="clickDisableChildProduct"/> - <see selector="{{AdminProductFormConfigurationsSection.confProductOptionStatusCell(productName)}}" userInput="Disabled" stepKey="seeConfigDisabled"/> - </actionGroup> - - <!--You are on AdminProductEditPage--> - <!--Start create configurations for attribute and fill quantity--> - <actionGroup name="StartCreateConfigurationsForAttribute" extends="generateConfigurationsByAttributeCode"> - <remove keyForRemoval="clickOnNextButton3"/> - <remove keyForRemoval="clickOnNextButton4"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductDisableConfigurationsActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductDisableConfigurationsActionGroup.xml new file mode 100644 index 0000000000000..69b2c37b6e850 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductDisableConfigurationsActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminConfigurableProductDisableConfigurationsActionGroup"> + <arguments> + <argument name="productName" type="string" defaultValue="{{SimpleProduct.name}}"/> + </arguments> + <click selector="{{AdminProductFormConfigurationsSection.actionsBtnByProductName(productName)}}" stepKey="clickToExpandActionsSelect"/> + <click selector="{{AdminProductFormConfigurationsSection.disableProductBtn}}" stepKey="clickDisableChildProduct"/> + <see selector="{{AdminProductFormConfigurationsSection.confProductOptionStatusCell(productName)}}" userInput="Disabled" stepKey="seeConfigDisabled"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableWizardMoveToNextStepActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableWizardMoveToNextStepActionGroup.xml new file mode 100644 index 0000000000000..50621b7114002 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableWizardMoveToNextStepActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminConfigurableWizardMoveToNextStepActionGroup"> + <annotations> + <description>Click on the 'Next' button in the 'Create Product Configurations' panel on the Configurable Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="title" type="string"/> + </arguments> + + <click selector="{{ConfigurableProductSection.nextButton}}" stepKey="clickNextButton"/> + <waitForPageLoad stepKey="waitForNextStepLoaded"/> + <see userInput="{{title}}" selector="{{AdminProductFormConfigurationsSection.stepsWizardTitle}}" stepKey="seeStepTitle"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml index 2cdee5468a457..6dba2f09a707b 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml @@ -65,21 +65,4 @@ <requiredEntity createDataKey="createConfigChildProduct2"/> </createData> </actionGroup> - - <!-- Create the configurable product, children are not visible individually --> - <actionGroup name="AdminCreateApiConfigurableProductWithHiddenChildActionGroup" extends="AdminCreateApiConfigurableProductActionGroup"> - <annotations> - <description>EXTENDS: AdminCreateApiConfigurableProductActionGroup. Adds 2 Hidden Product Options.</description> - </annotations> - - <!-- Create the 2 children that will be a part of the configurable product --> - <createData entity="ApiSimpleOneHidden" stepKey="createConfigChildProduct1"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - </createData> - <createData entity="ApiSimpleTwoHidden" stepKey="createConfigChildProduct2"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - </createData> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductWithHiddenChildActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductWithHiddenChildActionGroup.xml new file mode 100644 index 0000000000000..2562c8cc20e6e --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductWithHiddenChildActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateApiConfigurableProductWithHiddenChildActionGroup" extends="AdminCreateApiConfigurableProductActionGroup"> + <annotations> + <description>EXTENDS: AdminCreateApiConfigurableProductActionGroup. Adds 2 Hidden Product Options.</description> + </annotations> + + <!-- Create the 2 children that will be a part of the configurable product --> + <createData entity="ApiSimpleOneHidden" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleTwoHidden" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateConfigurableProductWithAttributeUncheckOptionActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateConfigurableProductWithAttributeUncheckOptionActionGroup.xml new file mode 100644 index 0000000000000..2f494bb003bfa --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateConfigurableProductWithAttributeUncheckOptionActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateConfigurableProductWithAttributeUncheckOptionActionGroup" extends="GenerateConfigurationsByAttributeCodeActionGroup"> + <annotations> + <description>EXTENDS: generateConfigurationsByAttributeCode. Click to uncheck created option. Enter Attribute price</description> + </annotations> + <arguments> + <argument name="attributeOption" type="string" defaultValue="option1"/> + <argument name="price" type="string" defaultValue="100"/> + </arguments> + <click selector="{{AdminCreateProductConfigurationsPanel.attributeOption('attributeOption')}}" after="clickOnSelectAll" stepKey="clickToUncheckOption"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" after="clickToUncheckOption" stepKey="clickOnNextButton22"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.applySinglePriceToAllSkus}}" after="clickOnNextButton22" stepKey="waitForNextPageOpened2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySinglePriceToAllSkus}}" after="waitForNextPageOpened2" stepKey="clickOnApplySinglePriceToAllSkus"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.singlePrice}}" userInput="{{price}}" before="clickOnApplySingleQuantityToEachSku" stepKey="enterAttributePrice"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateConfigurationsForAttributeActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateConfigurationsForAttributeActionGroup.xml new file mode 100644 index 0000000000000..57468165fe4f9 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateConfigurationsForAttributeActionGroup.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="AdminCreateConfigurationsForAttributeActionGroup" extends="GenerateConfigurationsByAttributeCodeActionGroup"> + <annotations> + <description>EXTENDS: generateConfigurationsByAttributeCode. Click to apply single price to all Skus. Enter Attribute price</description> + </annotations> + <arguments> + <argument name="price" type="string" defaultValue="100"/> + </arguments> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.applySinglePriceToAllSkus}}" after="clickOnNextButton2" stepKey="waitForNextPageOpened2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySinglePriceToAllSkus}}" after="waitForNextPageOpened2" stepKey="clickOnApplySinglePriceToAllSkus"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.singlePrice}}" userInput="{{price}}" before="clickOnApplySingleQuantityToEachSku" stepKey="enterAttributePrice"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminFilterAttributeInConfigurableAttributesGridActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminFilterAttributeInConfigurableAttributesGridActionGroup.xml new file mode 100644 index 0000000000000..56a70a0ba29f9 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminFilterAttributeInConfigurableAttributesGridActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminFilterAttributeInConfigurableAttributesGrid"> + <annotations> + <description>Filter attribute in configurable attributes grid by attribute code value</description> + </annotations> + <arguments> + <argument name="attributeCode" type="string" defaultValue="{{newProductAttribute.attribute_code}}"/> + </arguments> + <conditionalClick selector="{{AdminDataGridFilterSection.clear}}" visible="true" dependentSelector="{{AdminDataGridFilterSection.clear}}" stepKey="clearFilters"/> + <waitForPageLoad stepKey="waitForFiltersReset"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="waitForFiltersAppear"/> + <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="expandFilters"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" userInput="{{attributeCode}}" stepKey="fillFilterValue"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applyFilters}}" stepKey="clickApplyFilters"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminSelectAttributeInConfigurableAttributesGridActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminSelectAttributeInConfigurableAttributesGridActionGroup.xml new file mode 100644 index 0000000000000..72ab16df87dac --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminSelectAttributeInConfigurableAttributesGridActionGroup.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="AdminSelectAttributeInConfigurableAttributesGrid" extends="AdminFilterAttributeInConfigurableAttributesGrid"> + <annotations> + <description>EXTENDS: AdminFilterAttributeInConfigurableAttributesGrid. Select first filtered attribute.</description> + </annotations> + <click selector="{{AdminCreateProductConfigurationsPanel.firstCheckbox}}" stepKey="checkAttributeInGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AssertConfigurableProductOnAdminProductPageActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AssertConfigurableProductOnAdminProductPageActionGroup.xml new file mode 100644 index 0000000000000..88e170e3a3a90 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AssertConfigurableProductOnAdminProductPageActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertConfigurableProductOnAdminProductPageActionGroup"> + <annotations> + <description>Validates that the provided Configurable Product Name, SKU and Price are present and correct on the Configurable Product creation/edit page. PLEASE NOTE: The Product Data is Hardcoded.</description> + </annotations> + <arguments> + <argument name="product" type="entity"/> + </arguments> + + <seeInField userInput="{{ApiConfigurableProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="seeNameRequired"/> + <seeInField userInput="{{ApiConfigurableProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="seeSkuRequired"/> + <dontSeeInField userInput="{{ApiConfigurableProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="dontSeePriceRequired"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AssertConfigurableProductWithSpecialPriceOnStorefrontProductPageActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AssertConfigurableProductWithSpecialPriceOnStorefrontProductPageActionGroup.xml new file mode 100644 index 0000000000000..b998b4443f446 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AssertConfigurableProductWithSpecialPriceOnStorefrontProductPageActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertConfigurableProductWithSpecialPriceOnStorefrontProductPageActionGroup"> + <annotations> + <description>Validates that Special Price for a Configurable Product is present and correct when the provided Product Option is selected.</description> + </annotations> + <arguments> + <argument name="option" type="string"/> + <argument name="price" type="string"/> + <argument name="specialPrice" defaultValue="specialProductPrice"/> + </arguments> + + <selectOption userInput="{{option}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOptionWithSpecialPrice"/> + <see userInput="{{specialProductPrice.price}}" selector="{{StorefrontProductInfoMainSection.productSpecialPrice}}" stepKey="seeSpecialProductPrice"/> + <see userInput="Regular Price" selector="{{StorefrontProductInfoMainSection.specialProductText}}" stepKey="seeText"/> + <see userInput="{{price}}" selector="{{StorefrontProductInfoMainSection.oldProductPrice}}" stepKey="seeOldProductPrice"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AssertOptionImageAndPriceInStorefrontProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AssertOptionImageAndPriceInStorefrontProductActionGroup.xml new file mode 100644 index 0000000000000..e13d1909b5a10 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AssertOptionImageAndPriceInStorefrontProductActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertOptionImageAndPriceInStorefrontProductActionGroup"> + <annotations> + <description>Validates that the provided Product Image and Price are present when the provided Product Option is selected.</description> + </annotations> + <arguments> + <argument name="label" type="string"/> + <argument name="image" type="string"/> + <argument name="price" type="string"/> + </arguments> + + <selectOption userInput="{{label}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption"/> + <seeElement selector="{{StorefrontProductMediaSection.imageFile(image)}}" stepKey="seeImage"/> + <see userInput="{{price}}" selector="{{StorefrontProductInfoMainSection.price}}" stepKey="seeProductPrice"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AssertOptionImageInStorefrontProductPageActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AssertOptionImageInStorefrontProductPageActionGroup.xml new file mode 100644 index 0000000000000..ba41d36d95970 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AssertOptionImageInStorefrontProductPageActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertOptionImageInStorefrontProductPageActionGroup"> + <annotations> + <description>Validates that the provided Product Image is present when the provided Product Option is selected.</description> + </annotations> + <arguments> + <argument name="product" type="entity"/> + <argument name="label" type="string"/> + <argument name="image" defaultValue="MagentoLogo"/> + </arguments> + + <seeInCurrentUrl url="/{{product.urlKey}}.html" stepKey="checkUrl"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <selectOption userInput="{{label}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption1"/> + <seeElement selector="{{StorefrontProductMediaSection.imageFile(image.filename)}}" stepKey="seeFirstImage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ChangeConfigurableProductChildProductPriceActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ChangeConfigurableProductChildProductPriceActionGroup.xml new file mode 100644 index 0000000000000..b244681afbbb9 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ChangeConfigurableProductChildProductPriceActionGroup.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="ChangeConfigurableProductChildProductPriceActionGroup"> + <annotations> + <description>Change the price of a configurable child product in the grid under configurations.</description> + </annotations> + <arguments> + <argument name="productAttributes" type="string"/> + <argument name="productPrice" type="string"/> + </arguments> + <fillField userInput="{{productPrice}}" selector="{{AdminProductFormConfigurationsSection.confProductPriceCell(productAttributes)}}" stepKey="fillPriceForConfigurableProductAttributeOption"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ChangeProductConfigurationsInGridActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ChangeProductConfigurationsInGridActionGroup.xml new file mode 100644 index 0000000000000..48523fe693c44 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ChangeProductConfigurationsInGridActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ChangeProductConfigurationsInGridActionGroup"> + <annotations> + <description>Edit the Product Configuration via the Admin Product grid page.</description> + </annotations> + <arguments> + <argument name="firstOption" type="entity"/> + <argument name="secondOption" type="entity"/> + </arguments> + + <fillField userInput="{{firstOption.name}}" selector="{{AdminProductFormConfigurationsSection.confProductNameCell(firstOption.name)}}" stepKey="fillFieldNameForFirstAttributeOption"/> + <fillField userInput="{{secondOption.name}}" selector="{{AdminProductFormConfigurationsSection.confProductNameCell(secondOption.name)}}" stepKey="fillFieldNameForSecondAttributeOption"/> + <fillField userInput="{{firstOption.sku}}" selector="{{AdminProductFormConfigurationsSection.confProductSkuCell(firstOption.name)}}" stepKey="fillFieldSkuForFirstAttributeOption"/> + <fillField userInput="{{secondOption.sku}}" selector="{{AdminProductFormConfigurationsSection.confProductSkuCell(secondOption.name)}}" stepKey="fillFieldSkuForSecondAttributeOption"/> + <fillField userInput="{{firstOption.price}}" selector="{{AdminProductFormConfigurationsSection.confProductPriceCell(firstOption.name)}}" stepKey="fillFieldPriceForFirstAttributeOption"/> + <fillField userInput="{{secondOption.price}}" selector="{{AdminProductFormConfigurationsSection.confProductPriceCell(secondOption.name)}}" stepKey="fillFieldPriceForSecondAttributeOption"/> + <fillField userInput="{{firstOption.quantity}}" selector="{{AdminProductFormConfigurationsSection.confProductQuantityCell(firstOption.name)}}" stepKey="fillFieldQuantityForFirstAttributeOption"/> + <fillField userInput="{{secondOption.quantity}}" selector="{{AdminProductFormConfigurationsSection.confProductQuantityCell(secondOption.name)}}" stepKey="fillFieldQuantityForSecondAttributeOption"/> + <fillField userInput="{{firstOption.weight}}" selector="{{AdminProductFormConfigurationsSection.confProductWeightCell(firstOption.name)}}" stepKey="fillFieldWeightForFirstAttributeOption"/> + <fillField userInput="{{secondOption.weight}}" selector="{{AdminProductFormConfigurationsSection.confProductWeightCell(secondOption.name)}}" stepKey="fillFieldWeightForSecondAttributeOption"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ChangeProductConfigurationsInGridExceptSkuActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ChangeProductConfigurationsInGridExceptSkuActionGroup.xml new file mode 100644 index 0000000000000..740eda889637f --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ChangeProductConfigurationsInGridExceptSkuActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ChangeProductConfigurationsInGridExceptSkuActionGroup" extends="ChangeProductConfigurationsInGridActionGroup"> + <annotations> + <description>EXTENDS: changeProductConfigurationsInGrid. Removes 'fillFieldSkuForFirstAttributeOption' and 'fillFieldSkuForSecondAttributeOption'.</description> + </annotations> + + <remove keyForRemoval="fillFieldSkuForFirstAttributeOption"/> + <remove keyForRemoval="fillFieldSkuForSecondAttributeOption"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml deleted file mode 100644 index 8863cfaf4aeaa..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml +++ /dev/null @@ -1,158 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="GotoCatalogProductsPage"> - <annotations> - <description>Goes to the Admin Products grid via the Admin Side Menu.</description> - </annotations> - - <click stepKey="clickOnCatalogItem" selector="{{CatalogProductsSection.catalogItem}}"/> - <waitForPageLoad stepKey="waitForCatalogLoad"/> - <click stepKey="clickOnProductItem" selector="{{CatalogProductsSection.productItem}}"/> - <waitForPageLoad stepKey="waitForCatalogProductPageLoad"/> - <seeInCurrentUrl stepKey="assertWeAreOnTheCatalogProductPage" url="{{assertionData.catalogProduct}}"/> - </actionGroup> - - <actionGroup name="GotoConfigurableProductPage"> - <annotations> - <description>Clicks on create Configurable Product from the Admin Products grid page.</description> - </annotations> - - <click stepKey="clickOnAddProductItem" selector="{{ConfigurableProductSection.addProductItem}}"/> - <click stepKey="clickOnConfigurationProductItem" selector="{{ConfigurableProductSection.configProductItem}}"/> - <waitForPageLoad stepKey="waitForConfigurableProductPageLoad"/> - <seeInCurrentUrl stepKey="assertWeAreOnTheConfigurableProductPage" url="{{assertionData.configurableProduct}}"/> - </actionGroup> - - <actionGroup name="FillAllRequiredFields"> - <annotations> - <description>Fills the Product Name, Price and Weight fields. Clicks on 'Create Configurations'. Clicks on 'Create New Attribute'.</description> - </annotations> - - <fillField stepKey="fillInProductNameFields" selector="{{NewProduct.productName}}" userInput="{{NewProductsData.productName}}"/> - <fillField stepKey="fillInPriceFields" selector="{{NewProduct.price}}" userInput="{{NewProductsData.price}}"/> - <fillField stepKey="fillInWeightFields" selector="{{NewProduct.weight}}" userInput="{{NewProductsData.weight}}"/> - <click stepKey="clickOnCreateConfigurationsButton" selector="{{NewProduct.createConfigurationButton}}"/> - <waitForPageLoad stepKey="waitForCreateProductConfigurationsPageLoad"/> - <click stepKey="clickOnCreateNewAttributeButton" selector="{{NewProduct.createNewAttributeButton}}"/> - <waitForPageLoad stepKey="waitForNewAttributePageLoad"/> - </actionGroup> - - <actionGroup name="CreateNewAttribute"> - <annotations> - <description>Creates a new Product Attribute via the Admin Products creation/edit page. PLEASE NOTE: The Product Attributes are Hardcoded.</description> - </annotations> - - <switchToIFrame stepKey="NewAttributePage" selector="{{NewProduct.newAttributeIFrame}}"/> - <fillField stepKey="fillInDefaultLabelField" selector="{{NewProduct.defaultLabel}}" userInput="{{NewProductsData.defaultLabel}}"/> - - <!--Add option 1 to attribute--> - <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption1"/> - <waitForPageLoad stepKey="waitForOption1"/> - <fillField stepKey="fillInAdminFieldRed" selector="{{NewProduct.adminFieldRed}}" userInput="{{NewProductsData.adminFieldRed}}"/> - <fillField stepKey="fillInDefaultStoreViewFieldRed" selector="{{NewProduct.defaultStoreViewFieldRed}}" userInput="{{NewProductsData.defaultStoreViewFieldRed}}"/> - - <!--Add option 2 to attribute--> - <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption2"/> - <waitForPageLoad stepKey="waitForOption2"/> - <fillField stepKey="fillInAdminFieldBlue" selector="{{NewProduct.adminFieldBlue}}" userInput="{{NewProductsData.adminFieldBlue}}"/> - <fillField stepKey="fillInDefaultStoreViewFieldBlue" selector="{{NewProduct.defaultStoreViewFieldBlue}}" userInput="{{NewProductsData.defaultStoreViewFieldBlue}}"/> - - <!--Add option 3 to attribute--> - <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption3"/> - <waitForPageLoad stepKey="waitForOption3"/> - <fillField stepKey="fillInAdminFieldYellow" selector="{{NewProduct.adminFieldYellow}}" userInput="{{NewProductsData.adminFieldYellow}}"/> - <fillField stepKey="fillInDefaultStoreViewFieldYellow" selector="{{NewProduct.defaultStoreViewFieldYellow}}" userInput="{{NewProductsData.defaultStoreViewFieldYellow}}"/> - - <!--Add option 4 to attribute--> - <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption4"/> - <waitForPageLoad stepKey="waitForOption4"/> - <fillField stepKey="fillInAdminFieldGreen" selector="{{NewProduct.adminFieldGreen}}" userInput="{{NewProductsData.adminFieldGreen}}"/> - <fillField stepKey="fillInDefaultStoreViewFieldGreen" selector="{{NewProduct.defaultStoreViewFieldGreen}}" userInput="{{NewProductsData.defaultStoreViewFieldGreen}}"/> - - <!--Add option 5 to attribute--> - <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption5"/> - <waitForPageLoad stepKey="waitForOption5"/> - <fillField stepKey="fillInAdminFieldBlack" selector="{{NewProduct.adminFieldBlack}}" userInput="{{NewProductsData.adminFieldBlack}}"/> - <fillField stepKey="fillInDefaultStoreViewFieldBlack" selector="{{NewProduct.defaultStoreViewFieldBlack}}" userInput="{{NewProductsData.defaultStoreViewFieldBlack}}"/> - - <!--Click Save Attribute button--> - <click selector="{{NewProduct.saveAttributeButton}}" stepKey="clickSaveAttributeButton"/> - <waitForPageLoad stepKey="waitForSavingSettings"/> - - <!--Select created Attribute --> - <click selector="{{ConfigurableProductSection.selectCreatedAttribute}}" stepKey="selectCreatedAttribute"/> - - <!--Click Next button--> - <click selector="{{ConfigurableProductSection.nextButton}}" stepKey="clickNextButton"/> - <waitForPageLoad stepKey="waitForNextPageLoaded"/> - - <!--Select all the options of all the attributes button--> - <click selector="{{CreateProductConfigurations.checkboxRed}}" stepKey="selectCheckboxRed"/> - <click selector="{{CreateProductConfigurations.checkboxBlue}}" stepKey="selectCheckboxBlue"/> - <click selector="{{CreateProductConfigurations.checkboxYellow}}" stepKey="selectCheckboxYellow"/> - <click selector="{{CreateProductConfigurations.checkboxGreen}}" stepKey="selectCheckboxGreen"/> - <click selector="{{CreateProductConfigurations.checkboxBlack}}" stepKey="selectCheckboxBlack"/> - - <!--Click Next button--> - <click selector="{{ConfigurableProductSection.nextButton}}" stepKey="clickNextButton2"/> - <waitForPageLoad stepKey="waitForBulkImagesPricePageLoaded"/> - - <!--Click Next button--> - <click selector="{{ConfigurableProductSection.nextButton}}" stepKey="clickNextButton3"/> - <waitForPageLoad stepKey="waitForSummaryPageLoaded"/> - - <!--Click Generate Configure button--> - <click selector="{{ConfigurableProductSection.generateConfigure}}" stepKey="generateConfigure"/> - <waitForPageLoad stepKey="waitForGenerateConfigure"/> - - <!-- This Error message shouldn't appear: Test will pass when bug will be fixed--> - <dontSee selector="{{CreateProductConfigurations.errorMessage}}" userInput="{{assertionData.errorMessage}}" stepKey="dontSeeError"/> - - <!--Close frame--> - <conditionalClick selector="{{ConfigurableProductSection.closeFrame}}" dependentSelector="{{ConfigurableProductSection.closeFrame}}" visible="1" stepKey="closeFrame"/> - <waitForPageLoad stepKey="waitForClosingFrame"/> - </actionGroup> - - <actionGroup name="DeleteCreatedAttribute"> - <annotations> - <description>Deletes the Configurable Product Attribute.</description> - </annotations> - - <!--Click on Stores item--> - <click stepKey="clickOnStoresItem" selector="{{CatalogProductsSection.storesItem}}"/> - <waitForPageLoad stepKey="waitForNavigationPanel"/> - - <!--Click on Products item--> - <waitForElementVisible selector="{{CatalogProductsSection.storesProductItem}}" stepKey="waitForCatalogLoad"/> - <click stepKey="clickOnStoresProductItem" selector="{{CatalogProductsSection.storesProductItem}}"/> - <waitForPageLoad stepKey="waitForStoresProductPageLoad"/> - - <!--Click on created Attribute --> - <fillField stepKey="searchProductDefaultLabel" selector="{{CatalogProductsSection.searchDefaultLabelField}}" userInput="{{NewProductsData.defaultLabel}}"/> - <click stepKey="clickSearchButton" selector="{{CatalogProductsSection.searchButton}}"/> - <waitForPageLoad stepKey="waitForCreatedAttributeLoad"/> - <click stepKey="clickOnCreatedAttributeItem" selector="{{CatalogProductsSection.createdAttributeItem}}"/> - <waitForPageLoad stepKey="waitForAttributePropertiesPageLoad"/> - - <!--Click on Delete Attribute item--> - <click stepKey="clickOnDeleteAttributeItem" selector="{{CatalogProductsSection.deleteAttributeItem}}"/> - <waitForPageLoad stepKey="waitForDeletedDialogOpened"/> - - <!--Click on OK button--> - <click stepKey="clickOnOKButton" selector="{{CatalogProductsSection.okButton}}"/> - <waitForPageLoad stepKey="waitFordAttributeDeleted"/> - <see userInput="You deleted the product attribute." stepKey="seeDeletedTheProductAttributeMessage"/> - - <!-- Click Reset Filter button--> - <click stepKey="clickResetFilterButton" selector="{{CatalogProductsSection.resetFilter}}"/> - <waitForPageLoad stepKey="waitForAllFilterReset"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateConfigurableProductActionGroup.xml new file mode 100644 index 0000000000000..cb903222822eb --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateConfigurableProductActionGroup.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="CreateConfigurableProductActionGroup"> + <annotations> + <description>Goes to the Admin Product grid page. Creates a Configurable Product using the default Product Options.</description> + </annotations> + <arguments> + <argument name="product" defaultValue="_defaultProduct"/> + <argument name="category" defaultValue="_defaultCategory"/> + </arguments> + + <!-- fill in basic configurable product values --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> + <waitForPageLoad time="30" stepKey="wait1"/> + <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickOnAddProductToggle"/> + <click selector="{{AdminProductGridActionSection.addConfigurableProduct}}" stepKey="clickOnAddConfigurableProduct"/> + <fillField userInput="{{product.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> + <fillField userInput="{{product.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/> + <fillField userInput="{{product.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/> + <fillField userInput="{{product.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{category.name}}]" stepKey="fillCategory"/> + <selectOption userInput="{{product.visibility}}" selector="{{AdminProductFormSection.visibility}}" stepKey="fillVisibility"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> + <fillField userInput="{{product.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> + + <!-- create configurations for colors the product is available in --> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickOnCreateConfigurations"/> + <click selector="{{AdminCreateProductConfigurationsPanel.createNewAttribute}}" stepKey="clickOnNewAttribute"/> + <waitForPageLoad stepKey="waitForIFrame"/> + <switchToIFrame selector="{{AdminNewAttributePanel.newAttributeIFrame}}" stepKey="switchToNewAttributeIFrame"/> + <fillField selector="{{AdminNewAttributePanel.defaultLabel}}" userInput="{{colorProductAttribute.default_label}}" stepKey="fillDefaultLabel"/> + <click selector="{{AdminNewAttributePanel.saveAttribute}}" stepKey="clickOnNewAttributePanel"/> + <waitForPageLoad stepKey="waitForSaveAttribute"/> + <switchToIFrame stepKey="switchOutOfIFrame"/> + <waitForPageLoad stepKey="waitForFilters"/> + <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickOnFilters"/> + <fillField userInput="{{colorProductAttribute.default_label}}" selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" stepKey="fillFilterAttributeCodeField"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminCreateProductConfigurationsPanel.firstCheckbox}}" stepKey="clickOnFirstCheckbox"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="waitCreateNewValueAppears"/> + <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue1"/> + <fillField userInput="{{colorProductAttribute1.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewAttribute1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue2"/> + <fillField userInput="{{colorProductAttribute2.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewAttribute2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue3"/> + <fillField userInput="{{colorProductAttribute3.name}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewAttribute3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAll}}" stepKey="clickOnSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applyUniquePricesByAttributeToEachSku}}" stepKey="clickOnApplyUniquePricesByAttributeToEachSku"/> + <selectOption selector="{{AdminCreateProductConfigurationsPanel.selectAttribute}}" userInput="{{colorProductAttribute.default_label}}" stepKey="selectAttributes"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.attribute1}}" userInput="{{colorProductAttribute1.price}}" stepKey="fillAttributePrice1"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.attribute2}}" userInput="{{colorProductAttribute2.price}}" stepKey="fillAttributePrice2"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.attribute3}}" userInput="{{colorProductAttribute3.price}}" stepKey="fillAttributePrice3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="1" stepKey="enterAttributeQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton2"/> + <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> + <seeInTitle userInput="{{product.name}}" stepKey="seeProductNameInTitle"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateConfigurableProductWithAttributeSetActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateConfigurableProductWithAttributeSetActionGroup.xml new file mode 100644 index 0000000000000..cf8d36b393178 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateConfigurableProductWithAttributeSetActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateConfigurableProductWithAttributeSetActionGroup"> + <annotations> + <description>Admin edit created product as configurable. Choose created options</description> + </annotations> + <arguments> + <argument name="product" defaultValue="_defaultProduct"/> + <argument name="category" defaultValue="_defaultCategory"/> + <argument name="label" type="string" defaultValue="mySet"/> + <argument name="option" type="string" defaultValue="['option1', 'option2', 'option3', 'option4']"/> + </arguments> + <click selector="{{AdminProductFormSection.attributeSet}}" stepKey="startEditAttrSet"/> + <fillField selector="{{AdminProductFormSection.attributeSetFilter}}" userInput="{{label}}" stepKey="searchForAttrSet"/> + <click selector="{{AdminProductFormSection.attributeSetFilterResult}}" stepKey="selectAttrSet"/> + <fillField userInput="{{product.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> + <selectOption selector="{{AdminProductFormSection.additionalOptions}}" parameterArray="{{option}}" stepKey="searchAndMultiSelectCreatedOption"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateConfigurableProductWithTwoAttributesActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateConfigurableProductWithTwoAttributesActionGroup.xml new file mode 100644 index 0000000000000..aeb39fc9cbb0a --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateConfigurableProductWithTwoAttributesActionGroup.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateConfigurableProductWithTwoAttributesActionGroup" extends="CreateConfigurableProductActionGroup"> + <annotations> + <description>Goes to the Admin Product grid page. Creates a Configurable Product with 2 product attributes.</description> + </annotations> + <arguments> + <argument name="attribute1" defaultValue="ProductColorAttribute"/> + <argument name="attribute2" defaultValue="ProductSizeAttribute"/> + </arguments> + <remove keyForRemoval="clickOnNewAttribute"/> + <remove keyForRemoval="waitForIFrame"/> + <remove keyForRemoval="switchToNewAttributeIFrame"/> + <remove keyForRemoval="fillDefaultLabel"/> + <remove keyForRemoval="clickOnNewAttributePanel"/> + <remove keyForRemoval="waitForSaveAttribute"/> + <remove keyForRemoval="switchOutOfIFrame"/> + <remove keyForRemoval="waitForFilters"/> + <remove keyForRemoval="clickOnFilters"/> + <remove keyForRemoval="fillFilterAttributeCodeField"/> + <remove keyForRemoval="clickApplyFiltersButton"/> + <remove keyForRemoval="clickOnFirstCheckbox"/> + <click selector="{{AdminCreateProductConfigurationsPanel.attributeCheckbox(attribute1.attribute_code)}}" stepKey="selectAttribute1" after="clickOnCreateConfigurations"/> + <click selector="{{AdminCreateProductConfigurationsPanel.attributeCheckbox(attribute2.attribute_code)}}" stepKey="selectAttribute2" after="selectAttribute1"/> + <remove keyForRemoval="waitCreateNewValueAppears"/> + <remove keyForRemoval="clickOnCreateNewValue1"/> + <remove keyForRemoval="fillFieldForNewAttribute1"/> + <remove keyForRemoval="clickOnSaveNewAttribute1"/> + <remove keyForRemoval="clickOnCreateNewValue2"/> + <remove keyForRemoval="fillFieldForNewAttribute2"/> + <remove keyForRemoval="clickOnSaveNewAttribute2"/> + <remove keyForRemoval="clickOnCreateNewValue3"/> + <remove keyForRemoval="fillFieldForNewAttribute3"/> + <remove keyForRemoval="clickOnSaveNewAttribute3"/> + <remove keyForRemoval="clickOnSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute(attribute1.frontend_label)}}" stepKey="selectAllOptionsOfAttribute1" before="clickOnNextButton2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute(attribute2.frontend_label)}}" stepKey="selectAllOptionsOfAttribute2" before="clickOnNextButton2"/> + <remove keyForRemoval="applyUniquePricesByAttributeToEachSku"/> + <remove keyForRemoval="clickOnApplyUniquePricesByAttributeToEachSku"/> + <remove keyForRemoval="selectAttributes"/> + <remove keyForRemoval="fillAttributePrice1"/> + <remove keyForRemoval="fillAttributePrice2"/> + <remove keyForRemoval="fillAttributePrice3"/> + <remove keyForRemoval="clickOnSaveButton2"/> + <remove keyForRemoval="clickOnConfirmInPopup"/> + <remove keyForRemoval="seeSaveProductMessage"/> + <remove keyForRemoval="seeProductNameInTitle"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateConfigurationsForAttributeActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateConfigurationsForAttributeActionGroup.xml new file mode 100644 index 0000000000000..bcfad4849276d --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateConfigurationsForAttributeActionGroup.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="CreateConfigurationsForAttributeActionGroup" extends="GenerateConfigurationsByAttributeCodeActionGroup"> + <annotations> + <description>EXTENDS: generateConfigurationsByAttributeCode. Clicks on the Save button. Clicks on the Confirm button.</description> + </annotations> + <arguments> + <argument name="attributeCode" type="string" defaultValue="SomeString"/> + </arguments> + + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton2"/> + <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateConfigurationsForAttributeWithImagesActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateConfigurationsForAttributeWithImagesActionGroup.xml new file mode 100644 index 0000000000000..b74300b0d5a13 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateConfigurationsForAttributeWithImagesActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateConfigurationsForAttributeWithImagesActionGroup" extends="GenerateConfigurationsByAttributeCodeActionGroup"> + <annotations> + <description>EXTENDS: generateConfigurationsByAttributeCode. Adds the provided Attribute Image to the provided Attribute Code.</description> + </annotations> + <arguments> + <argument name="attributeCode" type="string" defaultValue="SomeString"/> + <argument name="image" defaultValue="ProductImage"/> + </arguments> + + <click selector="{{AdminCreateProductConfigurationsPanel.applySingleSetOfImages}}" stepKey="clickOnApplySingleImageSetToAllSku" after="enterAttributeQuantity"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.imageUploadButton}}" stepKey="seeImageSectionIsReady" after="clickOnApplySingleImageSetToAllSku"/> + <attachFile selector="{{AdminCreateProductConfigurationsPanel.imageFileUpload}}" userInput="{{image.file}}" stepKey="uploadFile" after="seeImageSectionIsReady"/> + <waitForElementNotVisible selector="{{AdminCreateProductConfigurationsPanel.uploadProgressBar}}" stepKey="waitForUpload" after="uploadFile"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.imageFile(image.fileName)}}" stepKey="waitForThumbnail" after="waitForUpload"/> + + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton2" after="clickOnNextButton4"/> + <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup" after="clickOnSaveButton2"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateConfigurationsForTwoAttributeActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateConfigurationsForTwoAttributeActionGroup.xml new file mode 100644 index 0000000000000..e241d1dfa3921 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateConfigurationsForTwoAttributeActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateConfigurationsForTwoAttributeActionGroup" extends="GenerateConfigurationsByAttributeCodeActionGroup"> + <annotations> + <description>EXTENDS: generateConfigurationsByAttributeCode. Generates the Product Configurations for the 2 provided Attribute Codes on the Configurable Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="secondAttributeCode" type="string"/> + </arguments> + + <remove keyForRemoval="clickOnSelectAll"/> + <remove keyForRemoval="clickFilters"/> + <remove keyForRemoval="fillFilterAttributeCodeField"/> + <remove keyForRemoval="clickApplyFiltersButton"/> + <remove keyForRemoval="clickOnFirstCheckbox"/> + + <click selector="{{AdminCreateProductConfigurationsPanel.attributeCheckbox(attributeCode)}}" stepKey="clickOnFirstAttributeCheckbox" after="clickCreateConfigurations"/> + <click selector="{{AdminCreateProductConfigurationsPanel.attributeCheckbox(secondAttributeCode)}}" stepKey="clickOnSecondAttributeCheckbox" after="clickOnFirstAttributeCheckbox"/> + <grabTextFrom selector="{{AdminCreateProductConfigurationsPanel.defaultLabel(attributeCode)}}" stepKey="grabFirstAttributeDefaultLabel" after="clickOnSecondAttributeCheckbox"/> + <grabTextFrom selector="{{AdminCreateProductConfigurationsPanel.defaultLabel(secondAttributeCode)}}" stepKey="grabSecondAttributeDefaultLabel" after="grabFirstAttributeDefaultLabel"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute({$grabFirstAttributeDefaultLabel})}}" stepKey="clickOnSelectAllForFirstAttribute" after="clickOnNextButton1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute({$grabSecondAttributeDefaultLabel})}}" stepKey="clickOnSelectAllForSecondAttribute" after="clickOnSelectAllForFirstAttribute"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton2"/> + <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateNewAttributeActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateNewAttributeActionGroup.xml new file mode 100644 index 0000000000000..9925aba09fb82 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateNewAttributeActionGroup.xml @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateNewAttributeActionGroup"> + <annotations> + <description>Creates a new Product Attribute via the Admin Products creation/edit page. PLEASE NOTE: The Product Attributes are Hardcoded.</description> + </annotations> + + <switchToIFrame stepKey="NewAttributePage" selector="{{NewProduct.newAttributeIFrame}}"/> + <fillField stepKey="fillInDefaultLabelField" selector="{{NewProduct.defaultLabel}}" userInput="{{NewProductsData.defaultLabel}}"/> + + <!--Add option 1 to attribute--> + <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption1"/> + <waitForPageLoad stepKey="waitForOption1"/> + <fillField stepKey="fillInAdminFieldRed" selector="{{NewProduct.adminFieldRed}}" userInput="{{NewProductsData.adminFieldRed}}"/> + <fillField stepKey="fillInDefaultStoreViewFieldRed" selector="{{NewProduct.defaultStoreViewFieldRed}}" userInput="{{NewProductsData.defaultStoreViewFieldRed}}"/> + + <!--Add option 2 to attribute--> + <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption2"/> + <waitForPageLoad stepKey="waitForOption2"/> + <fillField stepKey="fillInAdminFieldBlue" selector="{{NewProduct.adminFieldBlue}}" userInput="{{NewProductsData.adminFieldBlue}}"/> + <fillField stepKey="fillInDefaultStoreViewFieldBlue" selector="{{NewProduct.defaultStoreViewFieldBlue}}" userInput="{{NewProductsData.defaultStoreViewFieldBlue}}"/> + + <!--Add option 3 to attribute--> + <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption3"/> + <waitForPageLoad stepKey="waitForOption3"/> + <fillField stepKey="fillInAdminFieldYellow" selector="{{NewProduct.adminFieldYellow}}" userInput="{{NewProductsData.adminFieldYellow}}"/> + <fillField stepKey="fillInDefaultStoreViewFieldYellow" selector="{{NewProduct.defaultStoreViewFieldYellow}}" userInput="{{NewProductsData.defaultStoreViewFieldYellow}}"/> + + <!--Add option 4 to attribute--> + <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption4"/> + <waitForPageLoad stepKey="waitForOption4"/> + <fillField stepKey="fillInAdminFieldGreen" selector="{{NewProduct.adminFieldGreen}}" userInput="{{NewProductsData.adminFieldGreen}}"/> + <fillField stepKey="fillInDefaultStoreViewFieldGreen" selector="{{NewProduct.defaultStoreViewFieldGreen}}" userInput="{{NewProductsData.defaultStoreViewFieldGreen}}"/> + + <!--Add option 5 to attribute--> + <click selector="{{NewProduct.addOptionButton}}" stepKey="clickAddOption5"/> + <waitForPageLoad stepKey="waitForOption5"/> + <fillField stepKey="fillInAdminFieldBlack" selector="{{NewProduct.adminFieldBlack}}" userInput="{{NewProductsData.adminFieldBlack}}"/> + <fillField stepKey="fillInDefaultStoreViewFieldBlack" selector="{{NewProduct.defaultStoreViewFieldBlack}}" userInput="{{NewProductsData.defaultStoreViewFieldBlack}}"/> + + <!--Click Save Attribute button--> + <click selector="{{NewProduct.saveAttributeButton}}" stepKey="clickSaveAttributeButton"/> + <waitForPageLoad stepKey="waitForSavingSettings"/> + + <!--Select created Attribute --> + <click selector="{{ConfigurableProductSection.selectCreatedAttribute}}" stepKey="selectCreatedAttribute"/> + + <!--Click Next button--> + <click selector="{{ConfigurableProductSection.nextButton}}" stepKey="clickNextButton"/> + <waitForPageLoad stepKey="waitForNextPageLoaded"/> + + <!--Select all the options of all the attributes button--> + <click selector="{{CreateProductConfigurations.checkboxRed}}" stepKey="selectCheckboxRed"/> + <click selector="{{CreateProductConfigurations.checkboxBlue}}" stepKey="selectCheckboxBlue"/> + <click selector="{{CreateProductConfigurations.checkboxYellow}}" stepKey="selectCheckboxYellow"/> + <click selector="{{CreateProductConfigurations.checkboxGreen}}" stepKey="selectCheckboxGreen"/> + <click selector="{{CreateProductConfigurations.checkboxBlack}}" stepKey="selectCheckboxBlack"/> + + <!--Click Next button--> + <click selector="{{ConfigurableProductSection.nextButton}}" stepKey="clickNextButton2"/> + <waitForPageLoad stepKey="waitForBulkImagesPricePageLoaded"/> + + <!--Click Next button--> + <click selector="{{ConfigurableProductSection.nextButton}}" stepKey="clickNextButton3"/> + <waitForPageLoad stepKey="waitForSummaryPageLoaded"/> + + <!--Click Generate Configure button--> + <click selector="{{ConfigurableProductSection.generateConfigure}}" stepKey="generateConfigure"/> + <waitForPageLoad stepKey="waitForGenerateConfigure"/> + + <!-- This Error message shouldn't appear: Test will pass when bug will be fixed--> + <dontSee selector="{{CreateProductConfigurations.errorMessage}}" userInput="{{assertionData.errorMessage}}" stepKey="dontSeeError"/> + + <!--Close frame--> + <conditionalClick selector="{{ConfigurableProductSection.closeFrame}}" dependentSelector="{{ConfigurableProductSection.closeFrame}}" visible="1" stepKey="closeFrame"/> + <waitForPageLoad stepKey="waitForClosingFrame"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateOptionsForAttributeActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateOptionsForAttributeActionGroup.xml new file mode 100644 index 0000000000000..d691564f331f1 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/CreateOptionsForAttributeActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateOptionsForAttributeActionGroup"> + <arguments> + <argument name="attributeName" type="string" defaultValue="{{productAttributeColor.default_label}}"/> + <argument name="firstOptionName" type="string" defaultValue="option1"/> + <argument name="secondOptionName" type="string" defaultValue="option2"/> + </arguments> + <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickOnFilters"/> + <fillField userInput="{{attributeName}}" selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" stepKey="fillFilterAttributeCodeField"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminCreateProductConfigurationsPanel.firstCheckbox}}" stepKey="clickOnFirstCheckbox"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="waitCreateNewValueAppears"/> + <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateFirstNewValue"/> + <fillField userInput="{{firstOptionName}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewFirstOption"/> + <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute"/> + <click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateSecondNewValue"/> + <fillField userInput="{{secondOptionName}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewSecondOption"/> + <click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveAttribute"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAll}}" stepKey="clickOnSelectAll"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/DeleteCreatedAttributeActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/DeleteCreatedAttributeActionGroup.xml new file mode 100644 index 0000000000000..ff8cfecf83ab9 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/DeleteCreatedAttributeActionGroup.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteCreatedAttributeActionGroup"> + <annotations> + <description>Deletes the Configurable Product Attribute.</description> + </annotations> + + <!--Click on Stores item--> + <click stepKey="clickOnStoresItem" selector="{{CatalogProductsSection.storesItem}}"/> + <waitForPageLoad stepKey="waitForNavigationPanel"/> + + <!--Click on Products item--> + <waitForElementVisible selector="{{CatalogProductsSection.storesProductItem}}" stepKey="waitForCatalogLoad"/> + <click stepKey="clickOnStoresProductItem" selector="{{CatalogProductsSection.storesProductItem}}"/> + <waitForPageLoad stepKey="waitForStoresProductPageLoad"/> + + <!--Click on created Attribute --> + <fillField stepKey="searchProductDefaultLabel" selector="{{CatalogProductsSection.searchDefaultLabelField}}" userInput="{{NewProductsData.defaultLabel}}"/> + <click stepKey="clickSearchButton" selector="{{CatalogProductsSection.searchButton}}"/> + <waitForPageLoad stepKey="waitForCreatedAttributeLoad"/> + <click stepKey="clickOnCreatedAttributeItem" selector="{{CatalogProductsSection.createdAttributeItem}}"/> + <waitForPageLoad stepKey="waitForAttributePropertiesPageLoad"/> + + <!--Click on Delete Attribute item--> + <click stepKey="clickOnDeleteAttributeItem" selector="{{CatalogProductsSection.deleteAttributeItem}}"/> + <waitForPageLoad stepKey="waitForDeletedDialogOpened"/> + + <!--Click on OK button--> + <click stepKey="clickOnOKButton" selector="{{CatalogProductsSection.okButton}}"/> + <waitForPageLoad stepKey="waitFordAttributeDeleted"/> + <see userInput="You deleted the product attribute." stepKey="seeDeletedTheProductAttributeMessage"/> + + <!-- Click Reset Filter button--> + <click stepKey="clickResetFilterButton" selector="{{CatalogProductsSection.resetFilter}}"/> + <waitForPageLoad stepKey="waitForAllFilterReset"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/FillAllRequiredFieldsActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/FillAllRequiredFieldsActionGroup.xml new file mode 100644 index 0000000000000..9021225dd6fca --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/FillAllRequiredFieldsActionGroup.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="FillAllRequiredFieldsActionGroup"> + <annotations> + <description>Fills the Product Name, Price and Weight fields. Clicks on 'Create Configurations'. Clicks on 'Create New Attribute'.</description> + </annotations> + + <fillField stepKey="fillInProductNameFields" selector="{{NewProduct.productName}}" userInput="{{NewProductsData.productName}}"/> + <fillField stepKey="fillInPriceFields" selector="{{NewProduct.price}}" userInput="{{NewProductsData.price}}"/> + <fillField stepKey="fillInWeightFields" selector="{{NewProduct.weight}}" userInput="{{NewProductsData.weight}}"/> + <click stepKey="clickOnCreateConfigurationsButton" selector="{{NewProduct.createConfigurationButton}}"/> + <waitForPageLoad stepKey="waitForCreateProductConfigurationsPageLoad"/> + <click stepKey="clickOnCreateNewAttributeButton" selector="{{NewProduct.createNewAttributeButton}}"/> + <waitForPageLoad stepKey="waitForNewAttributePageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/GenerateAndSaveConfiguredProductAfterSettingOptionsActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/GenerateAndSaveConfiguredProductAfterSettingOptionsActionGroup.xml new file mode 100644 index 0000000000000..a4eade1549b07 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/GenerateAndSaveConfiguredProductAfterSettingOptionsActionGroup.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="GenerateAndSaveConfiguredProductAfterSettingOptionsActionGroup" extends="SaveConfiguredProductActionGroup"> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" before="clickOnSaveButton2" stepKey="clickOnNextButton"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" after="clickOnNextButton" stepKey="clickOnGenerateProductsButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/GenerateConfigurationsByAttributeCodeActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/GenerateConfigurationsByAttributeCodeActionGroup.xml new file mode 100644 index 0000000000000..80248cf5e00f8 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/GenerateConfigurationsByAttributeCodeActionGroup.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="GenerateConfigurationsByAttributeCodeActionGroup"> + <annotations> + <description>Generates the Product Configurations for the provided Attribute Code on the Configurable Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="attributeCode" type="string" defaultValue="SomeString"/> + </arguments> + + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickCreateConfigurations"/> + <click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickFilters"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" userInput="{{attributeCode}}" stepKey="fillFilterAttributeCodeField"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminCreateProductConfigurationsPanel.firstCheckbox}}" stepKey="clickOnFirstCheckbox"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAll}}" stepKey="clickOnSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> + <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="99" stepKey="enterAttributeQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/GotoCatalogProductsPageActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/GotoCatalogProductsPageActionGroup.xml new file mode 100644 index 0000000000000..237c55dd591ae --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/GotoCatalogProductsPageActionGroup.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="GotoCatalogProductsPageActionGroup"> + <annotations> + <description>Goes to the Admin Products grid via the Admin Side Menu.</description> + </annotations> + + <click stepKey="clickOnCatalogItem" selector="{{CatalogProductsSection.catalogItem}}"/> + <waitForPageLoad stepKey="waitForCatalogLoad"/> + <click stepKey="clickOnProductItem" selector="{{CatalogProductsSection.productItem}}"/> + <waitForPageLoad stepKey="waitForCatalogProductPageLoad"/> + <seeInCurrentUrl stepKey="assertWeAreOnTheCatalogProductPage" url="{{assertionData.catalogProduct}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/GotoConfigurableProductPageActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/GotoConfigurableProductPageActionGroup.xml new file mode 100644 index 0000000000000..a16d99944003b --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/GotoConfigurableProductPageActionGroup.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="GotoConfigurableProductPageActionGroup"> + <annotations> + <description>Clicks on create Configurable Product from the Admin Products grid page.</description> + </annotations> + + <click stepKey="clickOnAddProductItem" selector="{{ConfigurableProductSection.addProductItem}}"/> + <click stepKey="clickOnConfigurationProductItem" selector="{{ConfigurableProductSection.configProductItem}}"/> + <waitForPageLoad stepKey="waitForConfigurableProductPageLoad"/> + <seeInCurrentUrl stepKey="assertWeAreOnTheConfigurableProductPage" url="{{assertionData.configurableProduct}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/SaveConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/SaveConfigurableProductActionGroup.xml new file mode 100644 index 0000000000000..3b77a3b00c960 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/SaveConfigurableProductActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SaveConfigurableProductActionGroup"> + <annotations> + <description>Save configurable product</description> + </annotations> + <arguments> + <argument name="product" defaultValue="_defaultProduct"/> + </arguments> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton"/> + <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> + <seeInTitle userInput="{{product.name}}" stepKey="seeProductNameInTitle"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/SaveConfigurableProductAddToCurrentAttributeSetActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/SaveConfigurableProductAddToCurrentAttributeSetActionGroup.xml new file mode 100644 index 0000000000000..2b45b52e39d8f --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/SaveConfigurableProductAddToCurrentAttributeSetActionGroup.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="SaveConfigurableProductAddToCurrentAttributeSetActionGroup"> + <annotations> + <description>Clicks on 'Save'. Clicks on 'Confirm' in the 'Choose Affected Attribute Set' model on the Configurable Product creation/edit page.</description> + </annotations> + + <waitForElementVisible selector="{{AdminProductFormActionSection.saveButton}}" stepKey="waitForSaveBtnVisible"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProductAgain"/> + <waitForElementVisible selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="waitPopUpVisible"/> + <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmPopup"/> + <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSaveProductMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/SaveConfigurableProductWithNewAttributeSetActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/SaveConfigurableProductWithNewAttributeSetActionGroup.xml new file mode 100644 index 0000000000000..449c97cb5da6e --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/SaveConfigurableProductWithNewAttributeSetActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SaveConfigurableProductWithNewAttributeSetActionGroup"> + <annotations> + <description>Clicks on 'Save'. Clicks radio for '...new Attribute Set...' in the 'Choose Affected Attribute Set' modal. Clicks on 'Confirm' in the model on the Configurable Product creation/edit page.</description> + </annotations> + + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveConfigurableProduct"/> + <waitForElementVisible selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" time="30" stepKey="waitForAttributeSetConfirmation"/> + <click selector="{{AdminChooseAffectedAttributeSetPopup.addNewAttrSet}}" stepKey="clickAddNewAttributeSet"/> + <fillField selector="{{AdminChooseAffectedAttributeSetPopup.createNewAttrSetName}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="fillFieldNewAttrSetName"/> + <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickConfirmAttributeSet"/> + <see selector="You saved the product" stepKey="seeConfigurableSaveConfirmation" after="clickConfirmAttributeSet"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/SaveConfiguredProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/SaveConfiguredProductActionGroup.xml new file mode 100644 index 0000000000000..011c1f195dbd1 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/SaveConfiguredProductActionGroup.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="SaveConfiguredProductActionGroup"> + <annotations> + <description>Save the Configurable Product on the Configurable Product creation/edit page. Validates that the Success Message is present.</description> + </annotations> + + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton2"/> + <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/SelectCreatedAttributeAndCreateTwoOptionsActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/SelectCreatedAttributeAndCreateTwoOptionsActionGroup.xml new file mode 100644 index 0000000000000..ef06518205196 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/SelectCreatedAttributeAndCreateTwoOptionsActionGroup.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="SelectCreatedAttributeAndCreateTwoOptionsActionGroup" extends="AddNewProductConfigurationAttributeActionGroup"> + <remove keyForRemoval="clickOnNewAttribute"/> + <remove keyForRemoval="waitForIFrame"/> + <remove keyForRemoval="switchToNewAttributeIFrame"/> + <remove keyForRemoval="fillDefaultLabel"/> + <remove keyForRemoval="clickOnNewAttributePanel"/> + <remove keyForRemoval="waitForSaveAttribute"/> + <remove keyForRemoval="switchOutOfIFrame"/> + <remove keyForRemoval="waitForFilters"/> + <fillField userInput="{{attribute.attribute_code}}" selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}" stepKey="fillFilterAttributeCodeField"/> + <fillField userInput="{{firstOption.label}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewFirstOption"/> + <fillField userInput="{{secondOption.label}}" selector="{{AdminCreateProductConfigurationsPanel.attributeName}}" stepKey="fillFieldForNewSecondOption"/> + <remove keyForRemoval="clickOnSelectAll"/> + <remove keyForRemoval="clickOnSecondNextButton"/> + <remove keyForRemoval="clickOnThirdNextButton"/> + <remove keyForRemoval="clickOnFourthNextButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/SelectSingleAttributeAndAddToCartActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/SelectSingleAttributeAndAddToCartActionGroup.xml new file mode 100644 index 0000000000000..681e3a319eac6 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/SelectSingleAttributeAndAddToCartActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SelectSingleAttributeAndAddToCartActionGroup"> + <annotations> + <description>Selects a Product Option. Clicks 'Add to Cart'. Validates that the Product Added Success message appears.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + <argument name="attributeCode" type="string"/> + <argument name="optionName" type="string"/> + </arguments> + + <selectOption selector="{{StorefrontProductInfoMainSection.attributeSelectByAttributeID(attributeCode)}}" userInput="{{optionName}}" stepKey="selectAttribute"/> + <click stepKey="addProduct" selector="{{StorefrontProductActionSection.addToCart}}"/> + <waitForElementVisible selector="{{StorefrontQuickSearchResultsSection.messageSection}}" time="30" stepKey="waitForProductAdded"/> + <see selector="{{StorefrontQuickSearchResultsSection.messageSection}}" userInput="You added {{productName}} to your shopping cart." stepKey="seeAddedToCartMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StartCreateConfigurationsForAttributeActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StartCreateConfigurationsForAttributeActionGroup.xml new file mode 100644 index 0000000000000..d91d918c9ec79 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StartCreateConfigurationsForAttributeActionGroup.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="StartCreateConfigurationsForAttributeActionGroup" extends="GenerateConfigurationsByAttributeCodeActionGroup"> + <remove keyForRemoval="clickOnNextButton3"/> + <remove keyForRemoval="clickOnNextButton4"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml deleted file mode 100644 index 8b61f0e109591..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!-- Check configurable product on the category page --> - <actionGroup name="StorefrontCheckCategoryConfigurableProduct"> - <annotations> - <description>Validates that the provided Configurable Product is present and correct on a Category page.</description> - </annotations> - <arguments> - <argument name="product"/> - <argument name="optionProduct"/> - </arguments> - - <seeElement selector="{{StorefrontCategoryProductSection.ProductTitleByName(product.name)}}" stepKey="assertProductName"/> - <see userInput="${{optionProduct.price}}.00" selector="{{StorefrontCategoryProductSection.ProductPriceByName(product.name)}}" stepKey="AssertProductPrice"/> - <!-- @TODO: MAGETWO-80272 Move to Magento_Checkout --> - <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(product.name)}}" stepKey="moveMouseOverProduct"/> - <!-- @TODO: MAGETWO-80272 Move to Magento_Checkout --> - <seeElement selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(product.name)}}" stepKey="AssertAddToCart"/> - </actionGroup> - - <!-- Check configurable product out of stock on the category page --> - <actionGroup name="StorefrontCheckCategoryOutOfStockConfigurableProduct"> - <annotations> - <description>Validates that the provided Configurable Product is present and correct on a Category page.</description> - </annotations> - <arguments> - <argument name="product" type="entity"/> - </arguments> - - <seeElement selector="{{StorefrontCategoryProductSection.ProductTitleByName(product.name)}}" stepKey="assertProductName"/> - <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(product.name)}}" stepKey="moveMouseOverProduct"/> - <seeElement selector="{{StorefrontCategoryProductSection.ProductStockUnavailable}}" stepKey="AssertOutOfStock"/> - <dontSeeElement selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(product.name)}}" stepKey="AssertAddToCart"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCheckCategoryConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCheckCategoryConfigurableProductActionGroup.xml new file mode 100644 index 0000000000000..d33675e2c6b06 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCheckCategoryConfigurableProductActionGroup.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"> + <!-- Check configurable product on the category page --> + <actionGroup name="StorefrontCheckCategoryConfigurableProductActionGroup"> + <annotations> + <description>Validates that the provided Configurable Product is present and correct on a Category page.</description> + </annotations> + <arguments> + <argument name="product"/> + <argument name="optionProduct"/> + </arguments> + + <seeElement selector="{{StorefrontCategoryProductSection.ProductTitleByName(product.name)}}" stepKey="assertProductName"/> + <see userInput="${{optionProduct.price}}.00" selector="{{StorefrontCategoryProductSection.ProductPriceByName(product.name)}}" stepKey="AssertProductPrice"/> + <!-- @TODO: MAGETWO-80272 Move to Magento_Checkout --> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(product.name)}}" stepKey="moveMouseOverProduct"/> + <!-- @TODO: MAGETWO-80272 Move to Magento_Checkout --> + <seeElement selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(product.name)}}" stepKey="AssertAddToCart"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCheckCategoryOutOfStockConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCheckCategoryOutOfStockConfigurableProductActionGroup.xml new file mode 100644 index 0000000000000..1de28e36d83ae --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCheckCategoryOutOfStockConfigurableProductActionGroup.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="StorefrontCheckCategoryOutOfStockConfigurableProductActionGroup"> + <annotations> + <description>Validates that the provided Configurable Product is present and correct on a Category page.</description> + </annotations> + <arguments> + <argument name="product" type="entity"/> + </arguments> + + <seeElement selector="{{StorefrontCategoryProductSection.ProductTitleByName(product.name)}}" stepKey="assertProductName"/> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(product.name)}}" stepKey="moveMouseOverProduct"/> + <seeElement selector="{{StorefrontCategoryProductSection.ProductStockUnavailable}}" stepKey="AssertOutOfStock"/> + <dontSeeElement selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(product.name)}}" stepKey="AssertAddToCart"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCheckConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCheckConfigurableProductActionGroup.xml new file mode 100644 index 0000000000000..e21c2e11fcc4c --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCheckConfigurableProductActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- Check the configurable product on the product page --> + <actionGroup name="StorefrontCheckConfigurableProductActionGroup"> + <annotations> + <description>Goes to the provided Storefront Product page. Validates that the Product details are present and correct.</description> + </annotations> + <arguments> + <argument name="product"/> + <argument name="optionProduct"/> + </arguments> + + <seeInCurrentUrl url="/{{product.custom_attributes[url_key]}}.html" stepKey="checkUrl"/> + <seeInTitle userInput="{{product.name}}" stepKey="AssertProductNameInTitle"/> + <see userInput="{{product.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="assertProductName"/> + <see userInput="{{product.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="assertProductSku"/> + <see userInput="${{optionProduct.price}}.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="assertProductPrice"/> + <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertInStock"/> + <seeElement selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="assertAddToCart"/> + <see userInput="{{product.custom_attributes[description]}}" selector="{{StorefrontProductInfoMainSection.productDescription}}" stepKey="assertProductDescription"/> + <see userInput="{{product.custom_attributes[short_description]}}" selector="{{StorefrontProductInfoMainSection.productShortDescription}}" stepKey="assertProductShortDescription"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCheckConfigurableProductOptionsActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCheckConfigurableProductOptionsActionGroup.xml new file mode 100644 index 0000000000000..bee1181601f9b --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontCheckConfigurableProductOptionsActionGroup.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="StorefrontCheckConfigurableProductOptionsActionGroup"> + <annotations> + <description>Validates that the Options for a Configurable Product are present and correct.</description> + </annotations> + <arguments> + <argument name="product" type="entity"/> + <argument name="firstOption" type="entity"/> + <argument name="secondOption" type="entity"/> + </arguments> + + <selectOption userInput="{{firstOption.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption1"/> + <see userInput="{{product.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeConfigurableProductName"/> + <see userInput="{{firstOption.price}}" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="assertProductPricePresent"/> + <see userInput="{{product.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="seeConfigurableProductSku"/> + <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertInStock"/> + <see userInput="{{colorProductAttribute.default_label}}" selector="{{StorefrontProductInfoMainSection.productAttributeTitle1}}" stepKey="seeColorAttributeName"/> + <dontSee userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="dontSeeProductPriceLabel1"/> + <selectOption userInput="{{secondOption.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption2"/> + <dontSee userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="dontSeeProductPriceLabel2"/> + <see userInput="{{secondOption.price}}" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice2"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontOpenMinicartAndCheckConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontOpenMinicartAndCheckConfigurableProductActionGroup.xml new file mode 100644 index 0000000000000..d2567aa9c87f8 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontOpenMinicartAndCheckConfigurableProductActionGroup.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="StorefrontOpenMinicartAndCheckConfigurableProductActionGroup"> + <annotations> + <description>Validates that the provided Option Price is present and correct in the Mini Shopping Cart on the Storefront.</description> + </annotations> + <arguments> + <argument name="product"/> + <argument name="optionProduct"/> + </arguments> + + <waitForElement selector="{{StorefrontMinicartSection.productLinkByName(product.name)}}" stepKey="waitForMinicartProduct"/> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickShowMinicart"/> + <see userInput="${{optionProduct.price}}.00" selector="{{StorefrontMinicartSection.productPriceByName(product.name)}}" stepKey="assertProductPrice"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml deleted file mode 100644 index e5d537f350299..0000000000000 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml +++ /dev/null @@ -1,134 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!-- Check the configurable product on the product page --> - <actionGroup name="StorefrontCheckConfigurableProduct"> - <annotations> - <description>Goes to the provided Storefront Product page. Validates that the Product details are present and correct.</description> - </annotations> - <arguments> - <argument name="product"/> - <argument name="optionProduct"/> - </arguments> - - <seeInCurrentUrl url="/{{product.custom_attributes[url_key]}}.html" stepKey="checkUrl"/> - <seeInTitle userInput="{{product.name}}" stepKey="AssertProductNameInTitle"/> - <see userInput="{{product.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="assertProductName"/> - <see userInput="{{product.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="assertProductSku"/> - <see userInput="${{optionProduct.price}}.00" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="assertProductPrice"/> - <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertInStock"/> - <seeElement selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="assertAddToCart"/> - <see userInput="{{product.custom_attributes[description]}}" selector="{{StorefrontProductInfoMainSection.productDescription}}" stepKey="assertProductDescription"/> - <see userInput="{{product.custom_attributes[short_description]}}" selector="{{StorefrontProductInfoMainSection.productShortDescription}}" stepKey="assertProductShortDescription"/> - </actionGroup> - - <!-- Check Storefront Configurable Product Option --> - <actionGroup name="VerifyOptionInProductStorefront"> - <annotations> - <description>Validates that the provided Option Code and Option Name are present and correct on a Configurable Product page.</description> - </annotations> - <arguments> - <argument name="attributeCode" type="string"/> - <argument name="optionName" type="string"/> - </arguments> - - <seeElement selector="{{StorefrontProductInfoMainSection.attributeOptionByAttributeID(attributeCode, optionName)}}" stepKey="verifyOptionExists"/> - </actionGroup> - - <!-- Adds Single Option Configurable Product to cart--> - <actionGroup name="SelectSingleAttributeAndAddToCart"> - <annotations> - <description>Selects a Product Option. Clicks 'Add to Cart'. Validates that the Product Added Success message appears.</description> - </annotations> - <arguments> - <argument name="productName" type="string"/> - <argument name="attributeCode" type="string"/> - <argument name="optionName" type="string"/> - </arguments> - - <selectOption selector="{{StorefrontProductInfoMainSection.attributeSelectByAttributeID(attributeCode)}}" userInput="{{optionName}}" stepKey="selectAttribute"/> - <click stepKey="addProduct" selector="{{StorefrontProductActionSection.addToCart}}"/> - <waitForElementVisible selector="{{StorefrontQuickSearchResultsSection.messageSection}}" time="30" stepKey="waitForProductAdded"/> - <see selector="{{StorefrontQuickSearchResultsSection.messageSection}}" userInput="You added {{productName}} to your shopping cart." stepKey="seeAddedToCartMessage"/> - </actionGroup> - - <!-- Verify configurable product options in storefront product view --> - <actionGroup name="storefrontCheckConfigurableProductOptions"> - <annotations> - <description>Validates that the Options for a Configurable Product are present and correct.</description> - </annotations> - <arguments> - <argument name="product" type="entity"/> - <argument name="firstOption" type="entity"/> - <argument name="secondOption" type="entity"/> - </arguments> - - <selectOption userInput="{{firstOption.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption1"/> - <see userInput="{{product.name}}" selector="{{StorefrontProductInfoMainSection.productName}}" stepKey="seeConfigurableProductName"/> - <see userInput="{{firstOption.price}}" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="assertProductPricePresent"/> - <see userInput="{{product.sku}}" selector="{{StorefrontProductInfoMainSection.productSku}}" stepKey="seeConfigurableProductSku"/> - <see userInput="IN STOCK" selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="assertInStock"/> - <see userInput="{{colorProductAttribute.default_label}}" selector="{{StorefrontProductInfoMainSection.productAttributeTitle1}}" stepKey="seeColorAttributeName"/> - <dontSee userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="dontSeeProductPriceLabel1"/> - <selectOption userInput="{{secondOption.name}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption2"/> - <dontSee userInput="As low as" selector="{{StorefrontProductInfoMainSection.productPriceLabel}}" stepKey="dontSeeProductPriceLabel2"/> - <see userInput="{{secondOption.price}}" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeProductPrice2"/> - </actionGroup> - - <!-- Assert option image in storefront product page --> - <actionGroup name="assertOptionImageInStorefrontProductPage"> - <annotations> - <description>Validates that the provided Product Image is present when the provided Product Option is selected.</description> - </annotations> - <arguments> - <argument name="product" type="entity"/> - <argument name="label" type="string"/> - <argument name="image" defaultValue="MagentoLogo"/> - </arguments> - - <seeInCurrentUrl url="/{{product.urlKey}}.html" stepKey="checkUrl"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <selectOption userInput="{{label}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption1"/> - <seeElement selector="{{StorefrontProductMediaSection.imageFile(image.filename)}}" stepKey="seeFirstImage"/> - </actionGroup> - - <!-- Assert option image and price in storefront product page --> - <actionGroup name="AssertOptionImageAndPriceInStorefrontProductActionGroup"> - <annotations> - <description>Validates that the provided Product Image and Price are present when the provided Product Option is selected.</description> - </annotations> - <arguments> - <argument name="label" type="string"/> - <argument name="image" type="string"/> - <argument name="price" type="string"/> - </arguments> - - <selectOption userInput="{{label}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOption"/> - <seeElement selector="{{StorefrontProductMediaSection.imageFile(image)}}" stepKey="seeImage"/> - <see userInput="{{price}}" selector="{{StorefrontProductInfoMainSection.price}}" stepKey="seeProductPrice"/> - </actionGroup> - - <!-- Assert configurable product with special price in storefront product page --> - <actionGroup name="assertConfigurableProductWithSpecialPriceOnStorefrontProductPage"> - <annotations> - <description>Validates that Special Price for a Configurable Product is present and correct when the provided Product Option is selected.</description> - </annotations> - <arguments> - <argument name="option" type="string"/> - <argument name="price" type="string"/> - <argument name="specialPrice" defaultValue="specialProductPrice"/> - </arguments> - - <selectOption userInput="{{option}}" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" stepKey="selectOptionWithSpecialPrice"/> - <see userInput="{{specialProductPrice.price}}" selector="{{StorefrontProductInfoMainSection.productSpecialPrice}}" stepKey="seeSpecialProductPrice"/> - <see userInput="Regular Price" selector="{{StorefrontProductInfoMainSection.specialProductText}}" stepKey="seeText"/> - <see userInput="{{price}}" selector="{{StorefrontProductInfoMainSection.oldProductPrice}}" stepKey="seeOldProductPrice"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml index 965f452861f7c..28c23955aa1f8 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml @@ -25,17 +25,5 @@ </actionGroup> <!-- Open the Minicart and check Configurable Product --> - <actionGroup name="StorefrontOpenMinicartAndCheckConfigurableProductActionGroup"> - <annotations> - <description>Validates that the provided Option Price is present and correct in the Mini Shopping Cart on the Storefront.</description> - </annotations> - <arguments> - <argument name="product"/> - <argument name="optionProduct"/> - </arguments> - <waitForElement selector="{{StorefrontMinicartSection.productLinkByName(product.name)}}" stepKey="waitForMinicartProduct"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickShowMinicart"/> - <see userInput="${{optionProduct.price}}.00" selector="{{StorefrontMinicartSection.productPriceByName(product.name)}}" stepKey="assertProductPrice"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/VerifyOptionInProductStorefrontActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/VerifyOptionInProductStorefrontActionGroup.xml new file mode 100644 index 0000000000000..05623ee9c8ee4 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/VerifyOptionInProductStorefrontActionGroup.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="VerifyOptionInProductStorefrontActionGroup"> + <annotations> + <description>Validates that the provided Option Code and Option Name are present and correct on a Configurable Product page.</description> + </annotations> + <arguments> + <argument name="attributeCode" type="string"/> + <argument name="optionName" type="string"/> + </arguments> + + <seeElement selector="{{StorefrontProductInfoMainSection.attributeOptionByAttributeID(attributeCode, optionName)}}" stepKey="verifyOptionExists"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ViewConfigurableProductInAdminGridActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ViewConfigurableProductInAdminGridActionGroup.xml new file mode 100644 index 0000000000000..135bd21aad3e9 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ViewConfigurableProductInAdminGridActionGroup.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ViewConfigurableProductInAdminGridActionGroup"> + <annotations> + <description>Goes to the Admin Product grid page. Validates the provided Configurable Product is present and correct in the grid.</description> + </annotations> + <arguments> + <argument name="product" defaultValue="_defaultProduct"/> + </arguments> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> + <waitForPageLoad stepKey="waitForPageLoadInitial"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillProductSkuFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <seeNumberOfElements selector="{{AdminProductGridSection.productGridRows}}" userInput="3" stepKey="seeCorrectNumberOfProducts"/> + + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFiltersSimple"/> + <selectOption selector="{{AdminProductGridFilterSection.typeFilter}}" userInput="simple" stepKey="selectionProductType"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFiltersWithSimpleType"/> + <see selector="{{AdminProductGridSection.firstProductRow}}" userInput="{{product.name}}" stepKey="seeSimpleProductNameInGrid"/> + <see selector="{{AdminProductGridSection.firstProductRow}}" userInput="{{product.price}}" stepKey="seeSimpleProductPriceInGrid"/> + + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFiltersConfigurable"/> + <selectOption selector="{{AdminProductGridFilterSection.typeFilter}}" userInput="{{product.type_id}}" stepKey="selectionConfigurableProductType"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFiltersWithConfigurableType"/> + <see selector="{{AdminProductGridSection.firstProductRow}}" userInput="{{product.name}}" stepKey="seeConfigurableProductNameInGrid"/> + <dontSee selector="{{AdminProductGridSection.firstProductRow}}" userInput="{{product.price}}" stepKey="dontSeeProductPriceNameInGrid"/> + + <click selector="{{AdminProductGridFilterSection.clearFilters}}" stepKey="clickClearFiltersAfter"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml index 6ae3c4f4e16cf..3edb7c0f17ab9 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductData.xml @@ -77,6 +77,21 @@ <var key="sku" entityKey="sku" entityType="product" /> <var key="childSku" entityKey="sku" entityType="product2"/> </entity> + <entity name="ConfigurableProductWithAttributeSet" type="product"> + <data key="sku" unique="suffix">configurable</data> + <data key="type_id">configurable</data> + <data key="attribute_set_id">4</data> + <data key="attribute_set_name">mySet</data> + <data key="visibility">4</data> + <data key="name" unique="suffix">Configurable product</data> + <data key="price">1.00</data> + <data key="weight">2</data> + <data key="urlKey" unique="suffix">configurableurlkey</data> + <data key="status">1</data> + <data key="quantity">100</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + </entity> <entity name="ConfigurableProductPrice10Qty1" type="product"> <data key="sku" unique="suffix">configurable-product_</data> <data key="type_id">configurable</data> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml index f3c628d002e7a..92e2450ef4f3d 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml @@ -9,6 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCreateProductConfigurationsPanel"> + <element name="attributeOption" type="checkbox" selector="li[data-attribute-option-title='{{colorOption}}']" parameterized="true"/> <element name="next" type="button" selector=".steps-wizard-navigation .action-next-step" timeout="30"/> <element name="createNewAttribute" type="button" selector=".select-attributes-actions button[title='Create New Attribute']" timeout="30"/> <element name="filters" type="button" selector="button[data-action='grid-filter-expand']"/> @@ -16,6 +17,8 @@ <element name="applyFilters" type="button" selector="button[data-action='grid-filter-apply']" timeout="30"/> <element name="firstCheckbox" type="input" selector="tr[data-repeat-index='0'] .admin__control-checkbox"/> <element name="id" type="text" selector="//tr[contains(@data-repeat-index, '0')]/td[2]/div"/> + <element name="variationsGrid" type="block" selector=".admin__field[data-index='configurable-matrix']"/> + <element name="variationQty" type="input" selector=".admin__field[data-index='configurable-matrix'] input[name='configurable-matrix[{{rowIndex}}][qty]']" parameterized="true"/> <element name="attributeCheckbox" type="checkbox" selector="//div[contains(text(), '{{arg}}')]/ancestor::tr//input[@data-action='select-row']" parameterized="true"/> <element name="defaultLabel" type="text" selector="//div[contains(text(), '{{arg}}')]/ancestor::tr//td[3]/div[@class='data-grid-cell-content']" parameterized="true"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddDefaultImageConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddDefaultImageConfigurableTest.xml index 92928c9384672..2f9347157ef33 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddDefaultImageConfigurableTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddDefaultImageConfigurableTest.xml @@ -93,28 +93,28 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="productIndexPage"/> <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGrid"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGrid"> <argument name="product" value="$$baseConfigProductHandle$$"/> </actionGroup> - <actionGroup ref="openProducForEditByClickingRowXColumnYInProductGrid" stepKey="openProducForEditByClickingRow1Column2InProductGrid1"/> + <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProducForEditByClickingRow1Column2InProductGrid1"/> <!-- Add image to product --> - <actionGroup ref="addProductImage" stepKey="addImageForProduct"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImageForProduct"> <argument name="image" value="MagentoLogo"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Assert product image in admin product form --> - <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertProductImageAdminProductPage"/> + <actionGroup ref="AssertProductImageAdminProductPageActionGroup" stepKey="assertProductImageAdminProductPage"/> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="$$baseConfigProductHandle$$"/> </actionGroup> <!-- Assert product image in storefront product page --> - <actionGroup ref="assertProductImageStorefrontProductPage2" stepKey="assertProductImageStorefrontProductPage"> + <actionGroup ref="AssertProductImageStorefrontProductPage2ActionGroup" stepKey="assertProductImageStorefrontProductPageActionGroup"> <argument name="product" value="$$baseConfigProductHandle$$"/> <argument name="image" value="MagentoLogo"/> </actionGroup> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddingNewOptionsWithImagesAndPricesToConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddingNewOptionsWithImagesAndPricesToConfigurableProductTest.xml index a9d2f1c3379df..86c4d0398b2cc 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddingNewOptionsWithImagesAndPricesToConfigurableProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddingNewOptionsWithImagesAndPricesToConfigurableProductTest.xml @@ -29,6 +29,11 @@ <deleteData createDataKey="createConfigProductAttributeCreateConfigurableProduct" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createConfigChildProduct1CreateConfigurableProduct" stepKey="deleteConfigChildProduct1"/> <deleteData createDataKey="createConfigChildProduct2CreateConfigurableProduct" stepKey="deleteConfigChildProduct2"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> + <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct1"> + <argument name="productName" value="$$createConfigProductAttributeCreateConfigurableProduct.name$$"/> + </actionGroup> <actionGroup ref="logout" stepKey="logout"/> </after> @@ -62,17 +67,17 @@ selector="{{AdminCreateProductConfigurationsPanel.selectImagesButton}}" stepKey="selectAttributeOption"/> <!-- Add images to configurable product attribute options --> - <actionGroup ref="addUniqueImageToConfigurableProductOption" stepKey="addImageToConfigurableProductOptionOne"> + <actionGroup ref="AddUniqueImageToConfigurableProductOptionActionGroup" stepKey="addImageToConfigurableProductOptionOne"> <argument name="image" value="ImageUpload"/> <argument name="frontend_label" value="$$createConfigProductAttributeCreateConfigurableProduct.default_frontend_label$$"/> <argument name="label" value="$$getConfigAttributeOption1CreateConfigurableProduct.label$$"/> </actionGroup> - <actionGroup ref="addUniqueImageToConfigurableProductOption" stepKey="addImageToConfigurableProductOptionTwo"> + <actionGroup ref="AddUniqueImageToConfigurableProductOptionActionGroup" stepKey="addImageToConfigurableProductOptionTwo"> <argument name="image" value="ImageUpload_1"/> <argument name="frontend_label" value="$$createConfigProductAttributeCreateConfigurableProduct.default_frontend_label$$"/> <argument name="label" value="$$getConfigAttributeOption2CreateConfigurableProduct.label$$"/> </actionGroup> - <actionGroup ref="addUniqueImageToConfigurableProductOption" stepKey="addImageToConfigurableProductOptionThree"> + <actionGroup ref="AddUniqueImageToConfigurableProductOptionActionGroup" stepKey="addImageToConfigurableProductOptionThree"> <argument name="image" value="ImageUpload3"/> <argument name="frontend_label" value="$$createConfigProductAttributeCreateConfigurableProduct.default_frontend_label$$"/> <argument name="label" value="{{colorDefaultProductAttribute1.name}}"/> @@ -99,7 +104,7 @@ <click selector="{{ConfigurableProductSection.generateConfigure}}" stepKey="clickGenerateConfigure"/> <waitForPageLoad stepKey="waitForProductPageLoad"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!--Go to frontend and check image and price--> <amOnPage url="{{StorefrontProductPage.url($$createConfigProductCreateConfigurableProduct.custom_attributes[url_key]$$)}}" stepKey="goToProductPage"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAssertNoticeThatExistingSkuAutomaticallyChangedWhenSavingProductWithSameSkuTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAssertNoticeThatExistingSkuAutomaticallyChangedWhenSavingProductWithSameSkuTest.xml index 68bf703ecdab4..1613452dfc47c 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAssertNoticeThatExistingSkuAutomaticallyChangedWhenSavingProductWithSameSkuTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAssertNoticeThatExistingSkuAutomaticallyChangedWhenSavingProductWithSameSkuTest.xml @@ -22,12 +22,12 @@ </before> <after> <!-- Delete configurable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!-- Delete product attribute --> - <actionGroup ref="deleteProductAttributeByLabel" stepKey="deleteProductAttribute"> + <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> <argument name="ProductAttribute" value="colorProductAttribute"/> </actionGroup> @@ -38,12 +38,12 @@ <!-- Create configurable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="goToCreateProductPage" stepKey="createConfigurableProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="createConfigurableProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!-- Fill configurable product values --> - <actionGroup ref="fillMainProductForm" stepKey="fillConfigurableProductValues"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillConfigurableProductValues"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> @@ -52,7 +52,7 @@ <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewAttribute}}" time="30" stepKey="waitForConfigurationModalOpen" after="clickCreateConfigurations"/> <!--Create new attribute with two option --> - <actionGroup ref="addNewProductConfigurationAttribute" stepKey="createProductConfigurationAttribute"> + <actionGroup ref="AddNewProductConfigurationAttributeActionGroup" stepKey="createProductConfigurationAttribute"> <argument name="attribute" value="colorProductAttribute"/> <argument name="firstOption" value="colorConfigurableProductAttribute1"/> <argument name="secondOption" value="colorConfigurableProductAttribute2"/> @@ -69,7 +69,7 @@ <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmPopup"/> <!-- Assert product auto incremented sku notice message; see success message --> - <see selector="{{AdminMessagesSection.noticeMessage}}" stepKey="seeNoticeMessage" userInput="SKU for product {{ApiConfigurableProduct.name}} has been changed to {{ApiConfigurableProduct.sku}}-2."/> - <see selector="{{AdminMessagesSection.successMessage}}" stepKey="seeSuccessMessage" userInput="You saved the product."/> + <see selector="{{AdminMessagesSection.notice}}" stepKey="seeNoticeMessage" userInput="SKU for product {{ApiConfigurableProduct.name}} has been changed to {{ApiConfigurableProduct.sku}}-2."/> + <see selector="{{AdminMessagesSection.success}}" stepKey="seeSuccessMessage" userInput="You saved the product."/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckConfigurableProductAttributeValueUniquenessTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckConfigurableProductAttributeValueUniquenessTest.xml index df6afdcfd2243..98cb03916bdab 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckConfigurableProductAttributeValueUniquenessTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckConfigurableProductAttributeValueUniquenessTest.xml @@ -26,11 +26,11 @@ <after> <!--Delete created data--> <comment userInput="Delete created data" stepKey="deleteCreatedData"/> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteConfigurableProductAndOptions"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteConfigurableProductAndOptions"> <argument name="product" value="$$createConfigProduct$$"/> </actionGroup> <waitForPageLoad stepKey="waitForProductIndexPage"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProductAttribute" stepKey="deleteAttribute"/> <actionGroup ref="logout" stepKey="logOut"/> @@ -45,7 +45,7 @@ <comment userInput="Go to created product page" stepKey="goToProdPage"/> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="goToProductGrid"/> <waitForPageLoad stepKey="waitForProductPage1"/> - <actionGroup ref="filterProductGridByName2" stepKey="filterByName"> + <actionGroup ref="FilterProductGridByName2ActionGroup" stepKey="filterByName"> <argument name="name" value="$$createConfigProduct.name$$"/> </actionGroup> <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductName"/> @@ -55,7 +55,7 @@ <conditionalClick selector="{{AdminProductFormConfigurationsSection.sectionHeader}}" dependentSelector="{{AdminProductFormConfigurationsSection.createConfigurations}}" visible="false" stepKey="expandConfigurationsTab1"/> <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickOnCreateConfigurations1"/> <waitForPageLoad stepKey="waitForSelectAttributesPage1"/> - <actionGroup ref="selectCreatedAttributeAndCreateTwoOptions" stepKey="selectCreatedAttributeAndCreateOptions"> + <actionGroup ref="SelectCreatedAttributeAndCreateTwoOptionsActionGroup" stepKey="selectCreatedAttributeAndCreateOptions"> <argument name="attribute" value="dropdownProductAttribute"/> <argument name="firstOption" value="productAttributeOption1"/> <argument name="secondOption" value="productAttributeOption1"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml new file mode 100644 index 0000000000000..fabd4a2c253b6 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml @@ -0,0 +1,204 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckResultsOfColorAndOtherFiltersTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Checking filters results"/> + <title value="Checking results of color and other filters"/> + <description value="Checking results of filters: color and other filters"/> + <severity value="MAJOR"/> + <testCaseId value="MC-6192"/> + <useCaseId value="MAGETWO-91753"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <!-- Create default category with subcategory --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="SubCategoryWithParent" stepKey="createSubcategory"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- Create three configurable product --> + <createData entity="ConfigurableProductWithAttributeSet" stepKey="createFirstConfigurableProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ConfigurableProductWithAttributeSet" stepKey="createSecondConfigurableProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ConfigurableProductWithAttributeSet" stepKey="createThirdConfigurableProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="_defaultProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- Add first attribute with 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> + <createData entity="productAttributeOption4" stepKey="createConfigProductAttributeOption4"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption5" stepKey="createConfigProductAttributeOption5"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <!-- Add second attribute with options--> + <createData entity="multipleSelectProductAttribute" stepKey="createConfigProductAttribute2"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption12"> + <requiredEntity createDataKey="createConfigProductAttribute2"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption6"> + <requiredEntity createDataKey="createConfigProductAttribute2"/> + </createData> + <createData entity="productAttributeOption3" stepKey="createConfigProductAttributeOption7"> + <requiredEntity createDataKey="createConfigProductAttribute2"/> + </createData> + <createData entity="productAttributeOption4" stepKey="createConfigProductAttributeOption8"> + <requiredEntity createDataKey="createConfigProductAttribute2"/> + </createData> + <!-- Login as Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- Add created attributes with options to Attribute Set --> + <actionGroup ref="AdminAddUnassignedAttributeToGroupActionGroup" stepKey="createDefaultAttributeSet"> + <argument name="label" value="mySet"/> + <argument name="firstOption" value="$$createConfigProductAttribute.attribute_code$$"/> + <argument name="secondOption" value="$$createConfigProductAttribute2.attribute_code$$"/> + <argument name="group" value="Product Details"/> + </actionGroup> + </before> + <after> + <!-- Delete all created data --> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createFirstConfigurableProduct" stepKey="deleteFirstProduct"/> + <deleteData createDataKey="createSecondConfigurableProduct" stepKey="deleteSecondProduct"/> + <deleteData createDataKey="createThirdConfigurableProduct" stepKey="deleteThirdProduct"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <!-- Delete attribute set --> + <actionGroup ref="DeleteAttributeSetByLabelActionGroup" stepKey="deleteAttributeSet"> + <argument name="label" value="mySet"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearAttributeSetsFilter"/> + <!-- Delete First attribute --> + <actionGroup ref="OpenProductAttributeFromSearchResultInGridActionGroup" stepKey="openProductAttributeFromSearchResultInGrid"> + <argument name="productAttributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="DeleteProductAttributeByAttributeCodeActionGroup" stepKey="deleteProductAttributeByAttributeCode"> + <argument name="productAttributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + </actionGroup> + <!-- Delete Second attribute --> + <actionGroup ref="OpenProductAttributeFromSearchResultInGridActionGroup" stepKey="openSecondProductAttributeFromSearchResultInGrid"> + <argument name="productAttributeCode" value="$$createConfigProductAttribute2.attribute_code$$"/> + </actionGroup> + <actionGroup ref="DeleteProductAttributeByAttributeCodeActionGroup" stepKey="deleteSecondProductAttributeByAttributeCode"> + <argument name="productAttributeCode" value="$$createConfigProductAttribute2.attribute_code$$"/> + </actionGroup> + <actionGroup ref="AssertProductAttributeRemovedSuccessfullyActionGroup" stepKey="deleteProductAttributeSuccess"/> + <!-- Clear filters --> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductAttributesFilter"/> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductsGridFilter"/> + <!-- Log out --> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Create three configurable products with options --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> + <waitForPageLoad time="30" stepKey="wait1"/> + <!-- Edit created first product as configurable product with options --> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterGridByFirstProduct"> + <argument name="product" value="$$createFirstConfigurableProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProductFirst"> + <argument name="product" value="$$createFirstConfigurableProduct$$"/> + </actionGroup> + <actionGroup ref="CreateConfigurableProductWithAttributeSetActionGroup" stepKey="createProductFirst"> + <argument name="product" value="$$createFirstConfigurableProduct$$"/> + <argument name="category" value="$$createCategory$$"/> + <argument name="label" value="mySet"/> + <argument name="option1" value="['option1', 'option2', 'option3', 'option4']"/> + </actionGroup> + <actionGroup ref="AdminCreateConfigurationsForAttributeActionGroup" stepKey="createConfigurationFirst"> + <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + <argument name="price" value="34"/> + </actionGroup> + <waitForPageLoad stepKey="waitForPageLoad"/> + <click selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtnFirst" /> + <click selector="{{CmsNewPagePageActionsSection.saveAndClose}}" stepKey="clickSaveAndCloseFirst"/> + <waitForPageLoad stepKey="waitForMessage"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessageFirst"/> + <!-- Edit created second product as configurable product with options --> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterGridBySecondProduct"> + <argument name="product" value="$$createSecondConfigurableProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProductSecond"> + <argument name="product" value="$$createSecondConfigurableProduct$$"/> + </actionGroup> + <actionGroup ref="CreateConfigurableProductWithAttributeSetActionGroup" stepKey="createProductSecond"> + <argument name="product" value="$$createSecondConfigurableProduct$$"/> + <argument name="category" value="$$createCategory$$"/> + <argument name="label" value="mySet"/> + <argument name="option1" value="['option1', 'option2', 'option3']"/> + </actionGroup> + <actionGroup ref="AdminCreateConfigurableProductWithAttributeUncheckOptionActionGroup" stepKey="createConfigurationSecond"> + <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + <argument name="price" value="34"/> + <argument name="attributeOption" value="option5"/> + </actionGroup> + <waitForPageLoad stepKey="waitForPageLoadThird"/> + <click selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtnSecond" /> + <click selector="{{CmsNewPagePageActionsSection.saveAndClose}}" stepKey="clickSaveAndCloseSecond"/> + <waitForPageLoad stepKey="waitForSuccessMessage"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessageSecond"/> + <!-- Edit created third product as configurable product with options --> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterGridByThirdProduct"> + <argument name="product" value="$$createThirdConfigurableProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditProductThird"> + <argument name="product" value="$$createThirdConfigurableProduct$$"/> + </actionGroup> + <actionGroup ref="CreateConfigurableProductWithAttributeSetActionGroup" stepKey="createProductThird"> + <argument name="product" value="$$createThirdConfigurableProduct$$"/> + <argument name="category" value="$$createCategory$$"/> + <argument name="label" value="mySet"/> + <argument name="option1" value="['option2', 'option3', 'option4']"/> + </actionGroup> + <actionGroup ref="AdminCreateConfigurableProductWithAttributeUncheckOptionActionGroup" stepKey="createConfigurationThird"> + <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> + <argument name="price" value="34"/> + <argument name="attributeOption" value="option1"/> + </actionGroup> + <click selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtnThird" /> + <click selector="{{CmsNewPagePageActionsSection.saveAndClose}}" stepKey="clickSaveAndCloseThird"/> + <waitForPageLoad stepKey="waitForProductPage"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveConfigurableProductMessage"/> + <!-- Create Simple product with options --> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterGridBySimpleProduct"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="openEditSimpleProduct"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <actionGroup ref="CreateConfigurableProductWithAttributeSetActionGroup" stepKey="createSimpleProduct"> + <argument name="product" value="$$createSimpleProduct$$"/> + <argument name="category" value="$$createCategory$$"/> + <argument name="label" value="mySet"/> + <argument name="option1" value="['option1', 'option2']"/> + </actionGroup> + <click selector="{{AdminGridMainControls.save}}" stepKey="clickToSaveProduct"/> + <waitForPageLoad stepKey="waitForNewSimpleProductPage"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessageThird"/> + <!--TODO: REMOVE AFTER FIX MC-21717 --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush full_page" stepKey="flushCache"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml index dd641fd370ba7..5bdccf15b19d3 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml @@ -34,13 +34,13 @@ <!--Delete created data--> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <actionGroup ref="deleteProductBySku" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> <argument name="sku" value="{{ApiConfigurableProduct.name}}-thisIsShortName"/> </actionGroup> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> <!-- Remove attribute --> - <actionGroup ref="deleteProductAttribute" stepKey="deleteAttribute"> + <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteAttribute"> <argument name="ProductAttribute" value="productDropDownAttribute"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> @@ -51,7 +51,7 @@ <waitForPageLoad stepKey="waitForAdminProductPageLoad"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <actionGroup ref="filterProductGridBySku" stepKey="findCreatedProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <waitForPageLoad stepKey="waitForProductFilterLoad"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml index 430007ae761f7..2a1c38ee135eb 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml @@ -30,7 +30,7 @@ </after> <!-- Create a configurable product via the UI --> - <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> <argument name="product" value="_defaultProduct"/> <argument name="category" value="$$createCategory$$"/> </actionGroup> @@ -130,7 +130,7 @@ <click selector="{{DropdownAttributeOptionsSection.deleteButton(1)}}" stepKey="deleteOption"/> <click selector="{{AttributePropertiesSection.Save}}" stepKey="saveAttribute"/> <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGrid2"/> - <actionGroup stepKey="deleteProduct1" ref="deleteProductBySku"> + <actionGroup stepKey="deleteProduct1" ref="DeleteProductBySkuActionGroup"> <argument name="sku" value="$grabTextFromContent"/> </actionGroup> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml index 33a6da9dabf34..83e428b454c46 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml @@ -82,7 +82,7 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> <waitForPageLoad stepKey="wait2"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" stepKey="clearAll" visible="true"/> - <actionGroup ref="searchProductGridByKeyword" stepKey="searchForProduct"> + <actionGroup ref="SearchProductGridByKeywordActionGroup" stepKey="searchForProduct"> <argument name="keyword" value="ApiConfigurableProduct.name"/> </actionGroup> <click selector="label.data-grid-checkbox-cell-inner" stepKey="clickCheckbox"/> @@ -231,7 +231,7 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> <waitForPageLoad stepKey="wait1"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" stepKey="clearAll" visible="true"/> - <actionGroup ref="searchProductGridByKeyword" stepKey="searchForProduct"> + <actionGroup ref="SearchProductGridByKeywordActionGroup" stepKey="searchForProduct"> <argument name="keyword" value="ApiConfigurableProduct.name"/> </actionGroup> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductLongSkuTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductLongSkuTest.xml index cd09cbd295877..baea299581052 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductLongSkuTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductLongSkuTest.xml @@ -44,7 +44,7 @@ <after> <!--Clean up products--> - <actionGroup ref="deleteProductByName" stepKey="cleanUpProducts"> + <actionGroup ref="DeleteProductByNameActionGroup" stepKey="cleanUpProducts"> <argument name="sku" value="{{ProductWithLongNameSku.sku}}"/> <argument name="name" value="{{ProductWithLongNameSku.name}}"/> </actionGroup> @@ -63,7 +63,7 @@ <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{ProductWithLongNameSku.price}}" stepKey="fillProductPrice"/> <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="selectCategory"/> <!--Setup configurations--> - <actionGroup ref="generateConfigurationsByAttributeCode" stepKey="setupConfigurations"> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="setupConfigurations"> <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> </actionGroup> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest.xml index 51b3e49f51913..6bba4aa6b43ce 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest.xml @@ -94,7 +94,7 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> <waitForPageLoad stepKey="waitForAdminProductGridLoad"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <actionGroup ref="filterProductGridBySku" stepKey="findCreatedProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct"> <argument name="product" value="ApiSimpleOne"/> </actionGroup> <waitForPageLoad stepKey="waitForFiltersToBeApplied"/> @@ -114,7 +114,7 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage2"/> <waitForPageLoad stepKey="waitForAdminProductGridLoad2"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial2"/> - <actionGroup ref="filterProductGridBySku" stepKey="findCreatedProduct2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct2"> <argument name="product" value="ApiSimpleTwo"/> </actionGroup> <waitForPageLoad stepKey="waitForFiltersToBeApplied2"/> @@ -212,7 +212,7 @@ <see stepKey="checkForOutOfStock" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK" /> <!-- Delete the first simple product --> - <actionGroup stepKey="deleteProduct1" ref="deleteProductBySku"> + <actionGroup stepKey="deleteProduct1" ref="DeleteProductBySkuActionGroup"> <argument name="sku" value="{{ApiSimpleOne.sku}}"/> </actionGroup> @@ -222,7 +222,7 @@ <see stepKey="checkForOutOfStock2" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK"/> <!-- Delete the second simple product --> - <actionGroup stepKey="deleteProduct2" ref="deleteProductBySku"> + <actionGroup stepKey="deleteProduct2" ref="DeleteProductBySkuActionGroup"> <argument name="sku" value="{{ApiSimpleTwo.sku}}"/> </actionGroup> @@ -314,7 +314,7 @@ <see stepKey="checkForOutOfStock" selector="{{StorefrontProductInfoMainSection.stockIndication}}" userInput="IN STOCK" /> <!-- Delete the first simple product --> - <actionGroup stepKey="deleteProduct1" ref="deleteProductBySku"> + <actionGroup stepKey="deleteProduct1" ref="DeleteProductBySkuActionGroup"> <argument name="sku" value="{{ApiSimpleOne.sku}}"/> </actionGroup> @@ -327,7 +327,7 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage2"/> <waitForPageLoad stepKey="waitForAdminProductGridLoad2"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial2"/> - <actionGroup ref="filterProductGridBySku" stepKey="findCreatedProduct2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct2"> <argument name="product" value="ApiSimpleTwo"/> </actionGroup> <waitForPageLoad stepKey="waitForFiltersToBeApplied2"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml index 410c85d314904..889ca5b24b242 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml @@ -78,7 +78,7 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> <waitForPageLoad stepKey="wait1"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" stepKey="clearAll" visible="true"/> - <actionGroup ref="searchProductGridByKeyword" stepKey="searchForProduct"> + <actionGroup ref="SearchProductGridByKeywordActionGroup" stepKey="searchForProduct"> <argument name="keyword" value="ApiConfigurableProduct.name"/> </actionGroup> <waitForPageLoad stepKey="wait2"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSetEditContentTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSetEditContentTest.xml index 42e12852f563f..0da4c265a73af 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSetEditContentTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSetEditContentTest.xml @@ -21,16 +21,16 @@ </annotations> <after> <!-- Delete configurable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> </after> <!-- Create product --> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillProductForm"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml index dba481b64810a..c437b39a405cd 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml @@ -117,7 +117,7 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> <waitForPageLoad stepKey="waitForAdminProductPageLoad"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <actionGroup ref="filterProductGridBySku" stepKey="findCreatedProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <waitForPageLoad stepKey="waitForProductFilterLoad"/> @@ -227,7 +227,7 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> <waitForPageLoad stepKey="waitForAdminProductPageLoad"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <actionGroup ref="filterProductGridBySku" stepKey="findCreatedProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct"> <argument name="product" value="$$createConfigProduct$$"/> </actionGroup> <waitForPageLoad stepKey="waitForProductFilterLoad"/> @@ -265,7 +265,7 @@ <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> <!-- Find the simple product that we just created using the product grid and delete it --> - <actionGroup ref="deleteProductBySku" stepKey="findCreatedProduct2"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="findCreatedProduct2"> <argument name="sku" value="{{ApiConfigurableProduct.sku}}2-simple"/> </actionGroup> </test> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml index 5d5590011a852..da0c5a65f944d 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml @@ -17,9 +17,6 @@ <testCaseId value="MC-88"/> <group value="ConfigurableProduct"/> <severity value="AVERAGE"/> - <skip> - <issueId value="MC-17140"/> - </skip> </annotations> <before> @@ -37,15 +34,21 @@ </before> <after> - <actionGroup ref="logout" stepKey="adminLogout"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct1" stepKey="deleteFirstProduct"/> + <deleteData createDataKey="createProduct2" stepKey="deleteSecondProduct"/> + <deleteData createDataKey="createProduct3" stepKey="deleteThirdProduct"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="logout" stepKey="logout"/> </after> <!-- Search for prefix of the 3 products we created via api --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> <waitForPageLoad stepKey="wait1"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" stepKey="clearAll" visible="true"/> - <actionGroup ref="searchProductGridByKeyword" stepKey="searchForProduct"> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clearAll"/> + <actionGroup ref="SearchProductGridByKeywordActionGroup" stepKey="searchForProduct"> <argument name="keyword" value="ApiConfigurableProduct.name"/> </actionGroup> @@ -59,25 +62,25 @@ <!-- Update the description --> <click selector="{{AdminUpdateAttributesSection.toggleDescription}}" stepKey="clickToggleDescription"/> <fillField selector="{{AdminUpdateAttributesSection.description}}" userInput="MFTF automation!" stepKey="fillDescription"/> - <click selector="{{AdminUpdateAttributesSection.saveButton}}" stepKey="clickSave"/> - <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeSaveSuccess"/> + <click selector="{{AdminEditProductAttributesSection.Save}}" stepKey="clickSave"/> + <waitForElementVisible selector="{{AdminProductMessagesSection.successMessage}}" time="60" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminProductMessagesSection.successMessage}}" userInput="Message is added to queue" stepKey="seeAttributeUpdateSuccessMsg"/> <!-- Run cron twice --> - <magentoCLI command="cron:run" stepKey="runCron1"/> - <magentoCLI command="cron:run" stepKey="runCron2"/> - <reloadPage stepKey="refreshPage"/> - <waitForPageLoad stepKey="waitFormToReload1"/> + <magentoCLI command="cron:run" arguments="--group=consumers" stepKey="runCron1"/> + <magentoCLI command="cron:run" arguments="--group=consumers" stepKey="runCron2"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> <!-- Check storefront for description --> - <amOnPage url="$$createProduct1.sku$$.html" stepKey="gotoProduct1"/> - <waitForPageLoad stepKey="wait3"/> - <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeDescription1"/> - <amOnPage url="$$createProduct2.sku$$.html" stepKey="gotoProduct2"/> - <waitForPageLoad stepKey="wait4"/> - <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeDescription2"/> - <amOnPage url="$$createProduct3.sku$$.html" stepKey="gotoProduct3"/> - <waitForPageLoad stepKey="wait5"/> - <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeDescription3"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct1.custom_attributes[url_key]$$)}}" stepKey="goToFirstProductPageOnStorefront"/> + <waitForPageLoad stepKey="waitForFirstProductPageLoad"/> + <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeFirstDescription"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct2.custom_attributes[url_key]$$)}}" stepKey="goToSecondProductPageOnStorefront"/> + <waitForPageLoad stepKey="waitForSecondProductPageLoad"/> + <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeSecondDescription"/> + <amOnPage url="{{StorefrontProductPage.url($$createProduct3.custom_attributes[url_key]$$)}}" stepKey="goToThirdProductPageOnStorefront"/> + <waitForPageLoad stepKey="waitForThirdProductPageLoad"/> + <see selector="{{StorefrontProductInfoMainSection.productDescription}}" userInput="MFTF automation!" stepKey="seeThirdDescription"/> </test> <test name="AdminConfigurableProductRemoveAnOptionTest"> @@ -281,7 +284,7 @@ </after> <!-- Create a configurable product via the UI --> - <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> <argument name="product" value="_defaultProduct"/> <argument name="category" value="$$createCategory$$"/> </actionGroup> @@ -330,7 +333,7 @@ </after> <!-- Create a configurable product via the UI --> - <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> <argument name="product" value="_defaultProduct"/> <argument name="category" value="$$createCategory$$"/> </actionGroup> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableSetEditRelatedProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableSetEditRelatedProductsTest.xml index 11f1e9bb33c10..bba8232139d69 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableSetEditRelatedProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableSetEditRelatedProductsTest.xml @@ -24,7 +24,7 @@ </before> <after> <!-- Delete configurable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="_defaultProduct"/> </actionGroup> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> @@ -32,7 +32,7 @@ <!-- Create product --> <remove keyForRemoval="goToCreateProduct"/> - <actionGroup ref="createConfigurableProduct" stepKey="fillProductForm"> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="fillProductForm"> <argument name="product" value="_defaultProduct"/> <argument name="category" value="$$createCategory$$"/> </actionGroup> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndEditConfigurableProductSettingsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndEditConfigurableProductSettingsTest.xml index fd80c41b64962..6632cbcee30f2 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndEditConfigurableProductSettingsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndEditConfigurableProductSettingsTest.xml @@ -29,12 +29,12 @@ <!-- Create a configurable product --> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToProductIndexPage"/> - <actionGroup ref="goToCreateProductPage" stepKey="createConfigurableProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="createConfigurableProduct"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> <!-- Fill configurable product values --> - <actionGroup ref="fillMainProductForm" stepKey="fillConfigurableProductValues"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillConfigurableProductValues"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> @@ -47,7 +47,7 @@ </actionGroup> <!-- Save product form --> - <actionGroup ref="saveProductForm" stepKey="clickSaveButton"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveButton"/> <!-- Open product page --> <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openStorefrontProductPage"> @@ -87,7 +87,7 @@ <actionGroup ref="AdminSwitchProductGiftMessageStatusActionGroup" stepKey="disableGiftMessageSettings"/> <!-- Save product form --> - <actionGroup ref="saveProductForm" stepKey="clickSaveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveProduct"/> <!-- Verify Url Key after changing --> <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openProductPage"> @@ -102,7 +102,7 @@ <dontSeeElement selector="{{StorefrontProductCartGiftOptionSection.giftOptions}}" stepKey="dontSeeGiftOptionBtn"/> <!-- Delete created configurable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml index 2cc71964042a4..a5ad45048ce96 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml @@ -19,10 +19,10 @@ <group value="catalog"/> <group value="mtf_migrated"/> </annotations> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="openProductFillForm"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> <argument name="productType" value="configurable"/> </actionGroup> - <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="_defaultProduct"/> </actionGroup> <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Simple Product" stepKey="seeProductTypeInGrid"/> @@ -38,7 +38,7 @@ <group value="catalog"/> <group value="mtf_migrated"/> </annotations> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="openProductFillForm"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> <argument name="productType" value="configurable"/> </actionGroup> <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Virtual Product" stepKey="seeProductTypeInGrid"/> @@ -63,19 +63,19 @@ <after> <deleteData stepKey="deleteAttribute" createDataKey="createConfigProductAttribute"/> </after> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="openProductFillForm"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> <argument name="productType" value="virtual"/> </actionGroup> - <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="_defaultProduct"/> </actionGroup> <comment before="createConfiguration" stepKey="beforeCreateConfiguration" userInput="Adding Configuration to Product"/> - <actionGroup ref="generateConfigurationsByAttributeCode" stepKey="createConfiguration" after="fillProductForm"> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="createConfiguration" after="fillProductForm"> <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> </actionGroup> - <actionGroup ref="saveConfiguredProduct" stepKey="saveProductForm"/> + <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveProductForm"/> <see selector="{{AdminProductGridSection.productGridCell('2', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> - <actionGroup ref="VerifyOptionInProductStorefront" stepKey="verifyConfigurableOption" after="AssertProductInStorefrontProductPage"> + <actionGroup ref="VerifyOptionInProductStorefrontActionGroup" stepKey="verifyConfigurableOption" after="AssertProductInStorefrontProductPage"> <argument name="attributeCode" value="$createConfigProductAttribute.default_frontend_label$"/> <argument name="optionName" value="$createConfigProductAttributeOption1.option[store_labels][1][label]$"/> </actionGroup> @@ -100,23 +100,23 @@ <after> <deleteData stepKey="deleteAttribute" createDataKey="createConfigProductAttribute"/> </after> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="openProductFillForm"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> <argument name="productType" value="simple"/> </actionGroup> <!-- Create configurable product from simple product page--> <comment userInput="Create configurable product" stepKey="commentCreateProduct"/> - <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="_defaultProduct"/> </actionGroup> <comment before="createConfiguration" stepKey="beforeCreateConfiguration" userInput="Adding Configuration to Product"/> - <actionGroup ref="generateConfigurationsByAttributeCode" stepKey="createConfiguration" after="fillProductForm"> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="createConfiguration" after="fillProductForm"> <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> </actionGroup> - <actionGroup ref="saveConfiguredProduct" stepKey="saveProductForm"/> + <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveProductForm"/> <see selector="{{AdminProductGridSection.productGridCell('2', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> <!-- Verify product on store front --> <comment userInput="Verify product on store front" stepKey="commentVerifyProductGrid"/> - <actionGroup ref="VerifyOptionInProductStorefront" stepKey="verifyConfigurableOption" after="AssertProductInStorefrontProductPage"> + <actionGroup ref="VerifyOptionInProductStorefrontActionGroup" stepKey="verifyConfigurableOption" after="AssertProductInStorefrontProductPage"> <argument name="attributeCode" value="$createConfigProductAttribute.default_frontend_label$"/> <argument name="optionName" value="$createConfigProductAttributeOption1.option[store_labels][1][label]$"/> </actionGroup> @@ -141,11 +141,11 @@ </createData> </before> <after> - <actionGroup ref="GoToProductCatalogPage" stepKey="goToProductCatalogPage"/> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteConfigurableProduct"> + <actionGroup ref="GoToProductCatalogPageActionGroup" stepKey="goToProductCatalogPage"/> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteConfigurableProduct"> <argument name="product" value="_defaultProduct"/> </actionGroup> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetSearch"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetSearch"/> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> <deleteData stepKey="deleteAttribute" createDataKey="createConfigProductAttribute"/> <actionGroup ref="logout" stepKey="logout"/> @@ -155,36 +155,36 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> <!-- Open Dropdown and select downloadable product option --> <comment stepKey="beforeOpenProductFillForm" userInput="Selecting Product from the Add Product Dropdown"/> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="openProductFillForm"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> <argument name="productType" value="downloadable"/> </actionGroup> <scrollTo selector="{{AdminProductDownloadableSection.sectionHeader}}" stepKey="scrollToDownloadableInfo" /> <uncheckOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkIsDownloadable"/> <!-- Fill form for Downloadable Product Type --> <comment stepKey="beforeFillProductForm" userInput="Filling Product Form"/> - <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="_defaultProduct"/> </actionGroup> - <actionGroup ref="SetProductUrlKey" stepKey="setProductUrl"> + <actionGroup ref="SetProductUrlKeyActionGroup" stepKey="setProductUrl"> <argument name="product" value="_defaultProduct"/> </actionGroup> <comment before="createConfiguration" stepKey="beforeCreateConfiguration" userInput="Adding Configuration to Product"/> - <actionGroup ref="generateConfigurationsByAttributeCode" stepKey="createConfiguration"> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="createConfiguration"> <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> </actionGroup> - <actionGroup ref="saveConfiguredProduct" stepKey="saveProductForm"/> + <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveProductForm"/> <!-- Check that product was added with implicit type change --> <comment stepKey="beforeVerify" userInput="Verify Product Type Assigned Correctly"/> - <actionGroup ref="GoToProductCatalogPage" stepKey="goToProductCatalogPage"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetSearch"/> - <actionGroup ref="filterProductGridByName" stepKey="searchForProduct"> + <actionGroup ref="GoToProductCatalogPageActionGroup" stepKey="goToProductCatalogPage"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetSearch"/> + <actionGroup ref="FilterProductGridByNameActionGroup" stepKey="searchForProduct"> <argument name="product" value="_defaultProduct"/> </actionGroup> <see selector="{{AdminProductGridSection.productGridCell('2', 'Type')}}" userInput="Configurable Product" stepKey="seeProductTypeInGrid"/> - <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="assertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="assertProductInStorefrontProductPage"> <argument name="product" value="_defaultProduct"/> </actionGroup> - <actionGroup ref="VerifyOptionInProductStorefront" stepKey="verifyConfigurableOption"> + <actionGroup ref="VerifyOptionInProductStorefrontActionGroup" stepKey="verifyConfigurableOption"> <argument name="attributeCode" value="$createConfigProductAttribute.default_frontend_label$"/> <argument name="optionName" value="$createConfigProductAttributeOption1.option[store_labels][1][label]$"/> </actionGroup> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductBasedOnParentSkuTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductBasedOnParentSkuTest.xml index f4f607e9119b6..578e908664ecb 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductBasedOnParentSkuTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductBasedOnParentSkuTest.xml @@ -22,12 +22,12 @@ </before> <after> <!-- Delete configurable product with children products --> - <actionGroup ref="deleteProductBySku" stepKey="deleteProducts"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProducts"> <argument name="sku" value="{{ApiConfigurableProduct.sku}}"/> </actionGroup> <!-- Delete product attribute --> - <actionGroup ref="deleteProductAttributeByLabel" stepKey="deleteProductAttribute"> + <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> <argument name="ProductAttribute" value="colorProductAttribute"/> </actionGroup> @@ -38,12 +38,12 @@ <!-- Create configurable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="goToCreateProductPage" stepKey="createConfigurableProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="createConfigurableProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!-- Fill configurable product values --> - <actionGroup ref="fillMainProductForm" stepKey="fillConfigurableProductValues"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillConfigurableProductValues"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> @@ -52,31 +52,31 @@ <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewAttribute}}" time="30" stepKey="waitForConfigurationModalOpen" after="clickCreateConfigurations"/> <!--Create new attribute with two option --> - <actionGroup ref="addNewProductConfigurationAttribute" stepKey="createProductConfigurationAttribute"> + <actionGroup ref="AddNewProductConfigurationAttributeActionGroup" stepKey="createProductConfigurationAttribute"> <argument name="attribute" value="colorProductAttribute"/> <argument name="firstOption" value="colorConfigurableProductAttribute1"/> <argument name="secondOption" value="colorConfigurableProductAttribute2"/> </actionGroup> <!-- Change product configurations except sku --> - <actionGroup ref="changeProductConfigurationsInGridExceptSku" stepKey="changeProductConfigurationsInGridExceptSku"> + <actionGroup ref="ChangeProductConfigurationsInGridExceptSkuActionGroup" stepKey="changeProductConfigurationsInGridExceptSku"> <argument name="firstOption" value="colorConfigurableProductAttribute1"/> <argument name="secondOption" value="colorConfigurableProductAttribute2"/> </actionGroup> <!-- Save product --> - <actionGroup ref="saveConfigurableProductAddToCurrentAttributeSet" stepKey="saveProduct"/> + <actionGroup ref="SaveConfigurableProductAddToCurrentAttributeSetActionGroup" stepKey="saveProduct"/> <!-- Assert child products generated sku in grid --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="openProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPageLoad"/> - <actionGroup ref="filterProductGridByName2" stepKey="filterFirstProductByNameInGrid"> + <actionGroup ref="FilterProductGridByName2ActionGroup" stepKey="filterFirstProductByNameInGrid"> <argument name="name" value="{{colorConfigurableProductAttribute1.name}}"/> </actionGroup> <see selector="{{AdminProductGridSection.productGridCell('1', 'SKU')}}" userInput="{{ApiConfigurableProduct.sku}}-{{colorConfigurableProductAttribute1.name}}" stepKey="seeFirstProductSkuInGrid"/> - <actionGroup ref="filterProductGridByName2" stepKey="filterSecondProductByNameInGrid"> + <actionGroup ref="FilterProductGridByName2ActionGroup" stepKey="filterSecondProductByNameInGrid"> <argument name="name" value="{{colorConfigurableProductAttribute2.name}}"/> </actionGroup> <see selector="{{AdminProductGridSection.productGridCell('1', 'SKU')}}" userInput="{{ApiConfigurableProduct.sku}}-{{colorConfigurableProductAttribute2.name}}" stepKey="seeSecondProductSkuInGrid"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithCreatingCategoryAndAttributeTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithCreatingCategoryAndAttributeTest.xml index a7242b43c2b5f..363daa0ec8bb2 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithCreatingCategoryAndAttributeTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithCreatingCategoryAndAttributeTest.xml @@ -23,25 +23,25 @@ </before> <after> <!-- Delete configurable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!-- Delete children products --> - <actionGroup ref="deleteProductBySku" stepKey="deleteFirstChildProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteFirstChildProduct"> <argument name="sku" value="{{colorConfigurableProductAttribute1.sku}}"/> </actionGroup> - <actionGroup ref="deleteProductBySku" stepKey="deleteSecondChildProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteSecondChildProduct"> <argument name="sku" value="{{colorConfigurableProductAttribute2.sku}}"/> </actionGroup> <!-- Delete product attribute --> - <actionGroup ref="deleteProductAttributeByLabel" stepKey="deleteProductAttribute"> + <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> <argument name="ProductAttribute" value="colorProductAttribute"/> </actionGroup> <!-- Delete attribute set --> - <actionGroup ref="deleteAttributeSetByLabel" stepKey="deleteAttributeSet"> + <actionGroup ref="DeleteAttributeSetByLabelActionGroup" stepKey="deleteAttributeSet"> <argument name="label" value="{{ProductAttributeFrontendLabel.label}}"/> </actionGroup> @@ -55,7 +55,7 @@ <!-- Create configurable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="goToCreateProductPage" stepKey="createConfigurableProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="createConfigurableProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> @@ -72,32 +72,32 @@ <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewAttribute}}" time="30" stepKey="waitForConfigurationModalOpen" after="clickCreateConfigurations"/> <!--Create new attribute with two option --> - <actionGroup ref="addNewProductConfigurationAttribute" stepKey="createProductConfigurationAttribute"> + <actionGroup ref="AddNewProductConfigurationAttributeActionGroup" stepKey="createProductConfigurationAttribute"> <argument name="attribute" value="colorProductAttribute"/> <argument name="firstOption" value="colorConfigurableProductAttribute1"/> <argument name="secondOption" value="colorConfigurableProductAttribute2"/> </actionGroup> <!-- Change product configurations in grid --> - <actionGroup ref="changeProductConfigurationsInGrid" stepKey="changeProductConfigurationsInGrid"> + <actionGroup ref="ChangeProductConfigurationsInGridActionGroup" stepKey="changeProductConfigurationsInGrid"> <argument name="firstOption" value="colorConfigurableProductAttribute1"/> <argument name="secondOption" value="colorConfigurableProductAttribute2"/> </actionGroup> <!-- Save configurable product; add product to new attribute set --> - <actionGroup ref="saveConfigurableProductWithNewAttributeSet" stepKey="saveConfigurableProduct"/> + <actionGroup ref="SaveConfigurableProductWithNewAttributeSetActionGroup" stepKey="saveConfigurableProduct"/> <!-- Find configurable product in grid --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> <waitForPageLoad stepKey="waitForAdminProductPageLoad"/> - <actionGroup ref="filterProductGridBySku" stepKey="findCreatedConfigurableProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedConfigurableProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!-- Assert configurable product on admin product page --> <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductPage"/> <waitForPageLoad stepKey="waitForProductPageLoad"/> - <actionGroup ref="assertConfigurableProductOnAdminProductPage" stepKey="assertConfigurableProductOnAdminProductPage"> + <actionGroup ref="AssertConfigurableProductOnAdminProductPageActionGroup" stepKey="assertConfigurableProductOnAdminProductPage"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> @@ -107,7 +107,7 @@ <!--Assert configurable product in category --> <amOnPage url="$$createCategory.name$$.html" stepKey="amOnCategoryPage"/> <waitForPageLoad stepKey="waitForCategoryPageLoad"/> - <actionGroup ref="StorefrontCheckCategoryConfigurableProduct" stepKey="assertConfigurableProductInCategory"> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="assertConfigurableProductInCategory"> <argument name="product" value="ApiConfigurableProduct"/> <argument name="optionProduct" value="colorConfigurableProductAttribute1"/> </actionGroup> @@ -115,7 +115,7 @@ <!--Assert configurable product on product page --> <amOnPage url="{{ApiConfigurableProduct.urlKey}}.html" stepKey="amOnProductPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> - <actionGroup ref="storefrontCheckConfigurableProductOptions" stepKey="checkConfigurableProductOptions"> + <actionGroup ref="StorefrontCheckConfigurableProductOptionsActionGroup" stepKey="checkConfigurableProductOptions"> <argument name="product" value="ApiConfigurableProduct"/> <argument name="firstOption" value="colorConfigurableProductAttribute1"/> <argument name="secondOption" value="colorConfigurableProductAttribute2"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithDisabledChildrenProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithDisabledChildrenProductsTest.xml index 49f3f8b5ea931..f64ba4a63cec9 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithDisabledChildrenProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithDisabledChildrenProductsTest.xml @@ -40,10 +40,10 @@ </before> <after> <!-- Don't display out of stock product --> - <actionGroup ref="noDisplayOutOfStockProduct" stepKey="revertDisplayOutOfStockProduct"/> + <actionGroup ref="NoDisplayOutOfStockProductActionGroup" stepKey="revertDisplayOutOfStockProduct"/> <!-- Delete configurable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> @@ -59,17 +59,17 @@ <!-- Create configurable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="goToCreateProductPage" stepKey="createConfigurableProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="createConfigurableProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!-- Fill configurable product values --> - <actionGroup ref="fillMainProductForm" stepKey="fillConfigurableProductValues"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillConfigurableProductValues"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!-- Create product configurations and add attribute and select all options --> - <actionGroup ref="generateConfigurationsByAttributeCode" stepKey="generateConfigurationsByAttributeCode" after="fillConfigurableProductValues"> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="generateConfigurationsByAttributeCode" after="fillConfigurableProductValues"> <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> </actionGroup> @@ -77,25 +77,25 @@ <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="fillCategory" after="fillConfigurableProductValues"/> <!-- Add child product to configurations grid --> - <actionGroup ref="addProductToConfigurationsGrid" stepKey="addSimpleProduct"> + <actionGroup ref="AddProductToConfigurationsGridActionGroup" stepKey="addSimpleProduct"> <argument name="sku" value="$$createSimpleProduct.sku$$"/> <argument name="name" value="$$createConfigProductAttributeOption.option[store_labels][1][label]$$"/> </actionGroup> <!-- Save configurable product --> - <actionGroup ref="saveProductForm" stepKey="saveConfigurableProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigurableProduct"/> <!-- Find configurable product in grid --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> <waitForPageLoad stepKey="waitForAdminProductPageLoad"/> - <actionGroup ref="filterProductGridBySku" stepKey="findCreatedConfigurableProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedConfigurableProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!-- Assert configurable product on admin product page --> <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductPage"/> <waitForPageLoad stepKey="waitForProductPageLoad"/> - <actionGroup ref="assertConfigurableProductOnAdminProductPage" stepKey="assertConfigurableProductOnAdminProductPage"> + <actionGroup ref="AssertConfigurableProductOnAdminProductPageActionGroup" stepKey="assertConfigurableProductOnAdminProductPage"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> @@ -105,7 +105,7 @@ <see userInput="$$createSimpleProduct.name$$" selector="{{AdminProductFormConfigurationsSection.currentVariationsNameCells}}" stepKey="seeProductNameInConfigurations"/> <!-- Display out of stock product --> - <actionGroup ref="displayOutOfStockProduct" stepKey="displayOutOfStockProduct"/> + <actionGroup ref="DisplayOutOfStockProductActionGroup" stepKey="displayOutOfStockProduct"/> <!-- Flash cache --> <magentoCLI command="cache:flush" stepKey="flushCache"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithImagesTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithImagesTest.xml index 9796c14f5519a..b448131e75f15 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithImagesTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithImagesTest.xml @@ -46,7 +46,7 @@ </before> <after> <!-- Delete configurable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> @@ -62,12 +62,12 @@ <!--Create configurable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="goToCreateProductPage" stepKey="createConfigurableProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="createConfigurableProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!-- Fill configurable product values --> - <actionGroup ref="fillMainProductForm" stepKey="fillConfigurableProductValues"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillConfigurableProductValues"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> @@ -75,7 +75,7 @@ <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="fillCategory" after="fillConfigurableProductValues"/> <!-- Add image to product --> - <actionGroup ref="addProductImage" stepKey="addImageForProduct"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImageForProduct"> <argument name="image" value="MagentoLogo"/> </actionGroup> @@ -84,7 +84,7 @@ <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewAttribute}}" time="30" stepKey="waitForConfigurationModalOpen" after="clickCreateConfigurations"/> <!-- Show 100 attributes per page --> - <actionGroup ref="adminDataGridSelectPerPage" stepKey="selectNumberOfAttributesPerPage"> + <actionGroup ref="AdminDataGridSelectPerPageActionGroup" stepKey="selectNumberOfAttributesPerPage"> <argument name="perPage" value="100"/> </actionGroup> @@ -97,24 +97,24 @@ <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> <!-- Add images to first product attribute options --> - <actionGroup ref="addUniqueImageToConfigurableProductOption" stepKey="addImageToConfigurableProductOptionOne"> + <actionGroup ref="AddUniqueImageToConfigurableProductOptionActionGroup" stepKey="addImageToConfigurableProductOptionOne"> <argument name="image" value="MagentoLogo"/> <argument name="frontend_label" value="$$createFirstConfigProductAttribute.default_frontend_label$$"/> <argument name="label" value="$$createConfigProductAttributeOptionOne.option[store_labels][1][label]$$"/> </actionGroup> - <actionGroup ref="addUniqueImageToConfigurableProductOption" stepKey="addImageToConfigurableProductOptionTwo"> + <actionGroup ref="AddUniqueImageToConfigurableProductOptionActionGroup" stepKey="addImageToConfigurableProductOptionTwo"> <argument name="image" value="TestImageNew"/> <argument name="frontend_label" value="$$createFirstConfigProductAttribute.default_frontend_label$$"/> <argument name="label" value="$$createConfigProductAttributeOptionTwo.option[store_labels][1][label]$$"/> </actionGroup> <!-- Add price to second product attribute options --> - <actionGroup ref="addUniquePriceToConfigurableProductOption" stepKey="addPriceToConfigurableProductOptionThree"> + <actionGroup ref="AddUniquePriceToConfigurableProductOptionActionGroup" stepKey="addPriceToConfigurableProductOptionThree"> <argument name="frontend_label" value="$$createSecondConfigProductAttribute.default_frontend_label$$"/> <argument name="label" value="$$createConfigProductAttributeOptionThree.option[store_labels][1][label]$$"/> <argument name="price" value="{{virtualProductWithRequiredFields.price}}"/> </actionGroup> - <actionGroup ref="addUniquePriceToConfigurableProductOption" stepKey="addPriceToConfigurableProductOptionFour"> + <actionGroup ref="AddUniquePriceToConfigurableProductOptionActionGroup" stepKey="addPriceToConfigurableProductOptionFour"> <argument name="frontend_label" value="$$createSecondConfigProductAttribute.default_frontend_label$$"/> <argument name="label" value="$$createConfigProductAttributeOptionFour.option[store_labels][1][label]$$"/> <argument name="price" value="{{virtualProductWithRequiredFields.price}}"/> @@ -127,7 +127,7 @@ <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/> <!-- Save product --> - <actionGroup ref="saveConfigurableProductAddToCurrentAttributeSet" stepKey="saveProduct"/> + <actionGroup ref="SaveConfigurableProductAddToCurrentAttributeSetActionGroup" stepKey="saveProduct"/> <!--Run re-index task--> <magentoCLI command="indexer:reindex" stepKey="reindex"/> @@ -135,25 +135,25 @@ <!-- Assert configurable product in category --> <amOnPage url="$$createCategory.name$$.html" stepKey="amOnCategoryPage"/> <waitForPageLoad stepKey="waitForCategoryPageLoad"/> - <actionGroup ref="StorefrontCheckCategoryConfigurableProduct" stepKey="assertConfigurableProductInCategory"> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="assertConfigurableProductInCategory"> <argument name="product" value="ApiConfigurableProduct"/> <argument name="optionProduct" value="virtualProductWithRequiredFields"/> </actionGroup> <!-- Assert product image in storefront product page --> <amOnPage url="{{ApiConfigurableProduct.urlKey}}.html" stepKey="amOnProductPage"/> - <actionGroup ref="assertProductImageStorefrontProductPage" stepKey="assertProductImageStorefrontProductPage"> + <actionGroup ref="AssertProductImageStorefrontProductPageActionGroup" stepKey="assertProductImageStorefrontProductPage"> <argument name="product" value="ApiConfigurableProduct"/> <argument name="image" value="MagentoLogo"/> </actionGroup> <!-- Assert product options images in storefront product page --> - <actionGroup ref="assertOptionImageInStorefrontProductPage" stepKey="assertFirstOptionImageInStorefrontProductPage"> + <actionGroup ref="AssertOptionImageInStorefrontProductPageActionGroup" stepKey="assertFirstOptionImageInStorefrontProductPage"> <argument name="product" value="ApiConfigurableProduct"/> <argument name="label" value="$$createConfigProductAttributeOptionOne.option[store_labels][1][label]$$"/> <argument name="image" value="MagentoLogo"/> </actionGroup> - <actionGroup ref="assertOptionImageInStorefrontProductPage" stepKey="assertSecondOptionImageInStorefrontProductPage"> + <actionGroup ref="AssertOptionImageInStorefrontProductPageActionGroup" stepKey="assertSecondOptionImageInStorefrontProductPage"> <argument name="product" value="ApiConfigurableProduct"/> <argument name="label" value="$$createConfigProductAttributeOptionTwo.option[store_labels][1][label]$$"/> <argument name="image" value="TestImageNew"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDisplayOutOfStockProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDisplayOutOfStockProductsTest.xml index 308e256543736..1c6908818b4da 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDisplayOutOfStockProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDisplayOutOfStockProductsTest.xml @@ -61,10 +61,10 @@ </before> <after> <!-- Don't display out of stock product --> - <actionGroup ref="noDisplayOutOfStockProduct" stepKey="revertDisplayOutOfStockProduct"/> + <actionGroup ref="NoDisplayOutOfStockProductActionGroup" stepKey="revertDisplayOutOfStockProduct"/> <!-- Delete configurable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> @@ -82,17 +82,17 @@ <!--Create configurable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="goToCreateProductPage" stepKey="createConfigurableProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="createConfigurableProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!--Fill configurable product values --> - <actionGroup ref="fillMainProductForm" stepKey="fillConfigurableProductValues"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillConfigurableProductValues"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!-- Create product configurations and add attribute and select all options --> - <actionGroup ref="generateConfigurationsByAttributeCode" stepKey="generateConfigurationsByAttributeCode" after="fillConfigurableProductValues"> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="generateConfigurationsByAttributeCode" after="fillConfigurableProductValues"> <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> </actionGroup> @@ -100,26 +100,26 @@ <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="fillCategory" after="fillConfigurableProductValues"/> <!-- Add child products to configurations grid --> - <actionGroup ref="addProductToConfigurationsGrid" stepKey="addFirstSimpleProduct"> + <actionGroup ref="AddProductToConfigurationsGridActionGroup" stepKey="addFirstSimpleProduct"> <argument name="sku" value="$$createFirstSimpleProduct.sku$$"/> <argument name="name" value="$$createConfigProductAttributeOptionOne.option[store_labels][1][label]$$"/> </actionGroup> - <actionGroup ref="addProductToConfigurationsGrid" stepKey="addSecondSimpleProduct"> + <actionGroup ref="AddProductToConfigurationsGridActionGroup" stepKey="addSecondSimpleProduct"> <argument name="sku" value="$$createSecondSimpleProduct.sku$$"/> <argument name="name" value="$$createConfigProductAttributeOptionTwo.option[store_labels][1][label]$$"/> </actionGroup> - <actionGroup ref="addProductToConfigurationsGrid" stepKey="addOutOfStockProduct"> + <actionGroup ref="AddProductToConfigurationsGridActionGroup" stepKey="addOutOfStockProduct"> <argument name="sku" value="$$createSimpleOutOfStockProduct.sku$$"/> <argument name="name" value="$$createConfigProductAttributeOptionThree.option[store_labels][1][label]$$"/> </actionGroup> <!-- Save configurable product --> - <actionGroup ref="saveProductForm" stepKey="saveConfigurableProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigurableProduct"/> <!-- Display out of stock product --> - <actionGroup ref="displayOutOfStockProduct" stepKey="displayOutOfStockProduct"/> + <actionGroup ref="DisplayOutOfStockProductActionGroup" stepKey="displayOutOfStockProduct"/> <!-- Flash cache --> <magentoCLI command="cache:flush" stepKey="flushCache"/> @@ -127,7 +127,7 @@ <!--Assert configurable product in category --> <amOnPage url="$$createCategory.name$$.html" stepKey="amOnCategoryPage"/> <waitForPageLoad stepKey="waitForCategoryPageLoad"/> - <actionGroup ref="StorefrontCheckCategoryConfigurableProduct" stepKey="assertConfigurableProductInCategory"> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="assertConfigurableProductInCategory"> <argument name="product" value="ApiConfigurableProduct"/> <argument name="optionProduct" value="$$createFirstSimpleProduct$$"/> </actionGroup> @@ -137,4 +137,4 @@ <waitForPageLoad stepKey="waitForProductPageLoad"/> <see userInput="$$createConfigProductAttributeOptionThree.option[store_labels][1][label]$$" selector="{{StorefrontProductInfoMainSection.optionByAttributeId($$createConfigProductAttribute.attribute_id$$)}}" stepKey="assertOptionNotAvailable" /> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDontDisplayOutOfStockProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDontDisplayOutOfStockProductsTest.xml index e24ac07c30d1e..9b4cea72882eb 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDontDisplayOutOfStockProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDontDisplayOutOfStockProductsTest.xml @@ -61,7 +61,7 @@ </before> <after> <!-- Delete configurable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> @@ -79,17 +79,17 @@ <!--Create configurable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="goToCreateProductPage" stepKey="createConfigurableProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="createConfigurableProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!--Fill configurable product values --> - <actionGroup ref="fillMainProductForm" stepKey="fillConfigurableProductValues"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillConfigurableProductValues"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!-- Create product configurations and add attribute and select all options --> - <actionGroup ref="generateConfigurationsByAttributeCode" stepKey="generateConfigurationsByAttributeCode" after="fillConfigurableProductValues"> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="generateConfigurationsByAttributeCode" after="fillConfigurableProductValues"> <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> </actionGroup> @@ -97,23 +97,23 @@ <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="fillCategory" after="fillConfigurableProductValues"/> <!-- Add child products to configurations grid --> - <actionGroup ref="addProductToConfigurationsGrid" stepKey="addFirstSimpleProduct"> + <actionGroup ref="AddProductToConfigurationsGridActionGroup" stepKey="addFirstSimpleProduct"> <argument name="sku" value="$$createFirstSimpleProduct.sku$$"/> <argument name="name" value="$$createConfigProductAttributeOptionOne.option[store_labels][1][label]$$"/> </actionGroup> - <actionGroup ref="addProductToConfigurationsGrid" stepKey="addSecondSimpleProduct"> + <actionGroup ref="AddProductToConfigurationsGridActionGroup" stepKey="addSecondSimpleProduct"> <argument name="sku" value="$$createSecondSimpleProduct.sku$$"/> <argument name="name" value="$$createConfigProductAttributeOptionTwo.option[store_labels][1][label]$$"/> </actionGroup> - <actionGroup ref="addProductToConfigurationsGrid" stepKey="addOutOfStockProduct"> + <actionGroup ref="AddProductToConfigurationsGridActionGroup" stepKey="addOutOfStockProduct"> <argument name="sku" value="$$createSimpleOutOfStockProduct.sku$$"/> <argument name="name" value="$createConfigProductAttributeOptionThree.option[store_labels][1][label]$$"/> </actionGroup> <!-- Save configurable product --> - <actionGroup ref="saveProductForm" stepKey="saveConfigurableProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigurableProduct"/> <!-- Flash cache --> <magentoCLI command="cache:flush" stepKey="flushCache"/> @@ -121,7 +121,7 @@ <!--Assert configurable product in category --> <amOnPage url="$$createCategory.name$$.html" stepKey="amOnCategoryPage"/> <waitForPageLoad stepKey="waitForCategoryPageLoad"/> - <actionGroup ref="StorefrontCheckCategoryConfigurableProduct" stepKey="assertConfigurableProductInCategory"> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="assertConfigurableProductInCategory"> <argument name="product" value="ApiConfigurableProduct"/> <argument name="optionProduct" value="$$createFirstSimpleProduct$$"/> </actionGroup> @@ -131,4 +131,4 @@ <waitForPageLoad stepKey="waitForPageLoad"/> <dontSee userInput="$$createConfigProductAttributeOptionThree.option[store_labels][1][label]$$" selector="{{StorefrontProductInfoMainSection.optionByAttributeId($$createConfigProductAttribute.attribute_id$$)}}" stepKey="assertOptionNotAvailable"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTierPriceForOneItemTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTierPriceForOneItemTest.xml index 51f4bf0279942..e67788d5e7bb0 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTierPriceForOneItemTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTierPriceForOneItemTest.xml @@ -54,7 +54,7 @@ </before> <after> <!-- Delete configurable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> @@ -70,33 +70,33 @@ <!--Create configurable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitProductGridPageLoad"/> - <actionGroup ref="goToCreateProductPage" stepKey="createConfigurableProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="createConfigurableProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!--Fill configurable product values --> - <actionGroup ref="fillMainProductForm" stepKey="fillConfigurableProductValues"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillConfigurableProductValues"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!-- Create product configurations and add attribute and select all options --> - <actionGroup ref="generateConfigurationsByAttributeCode" stepKey="generateConfigurationsByAttributeCode" after="fillConfigurableProductValues"> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="generateConfigurationsByAttributeCode" after="fillConfigurableProductValues"> <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> </actionGroup> <!-- Add associated products to configurations grid --> - <actionGroup ref="addProductToConfigurationsGrid" stepKey="addFirstSimpleProduct"> + <actionGroup ref="AddProductToConfigurationsGridActionGroup" stepKey="addFirstSimpleProduct"> <argument name="sku" value="$$createFirstSimpleProduct.sku$$"/> <argument name="name" value="$$createConfigProductAttributeOptionOne.option[store_labels][1][label]$$"/> </actionGroup> - <actionGroup ref="addProductToConfigurationsGrid" stepKey="addSecondSimpleProduct"> + <actionGroup ref="AddProductToConfigurationsGridActionGroup" stepKey="addSecondSimpleProduct"> <argument name="sku" value="$$createSecondSimpleProduct.sku$$"/> <argument name="name" value="$$createConfigProductAttributeOptionTwo.option[store_labels][1][label]$$"/> </actionGroup> <!-- Save configurable product --> - <actionGroup ref="saveProductForm" stepKey="saveConfigurableProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveConfigurableProduct"/> <!-- Assert product tier price on product page --> <amOnPage url="{{ApiConfigurableProduct.urlKey}}.html" stepKey="amOnProductPage"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml index 1db9b3e5b79b2..6ab4734a074a5 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsAssignedToCategoryTest.xml @@ -26,25 +26,25 @@ </before> <after> <!-- Delete configurable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!-- Delete children products --> - <actionGroup ref="deleteProductBySku" stepKey="deleteFirstChildProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteFirstChildProduct"> <argument name="sku" value="{{colorConfigurableProductAttribute1.sku}}"/> </actionGroup> - <actionGroup ref="deleteProductBySku" stepKey="deleteSecondChildProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteSecondChildProduct"> <argument name="sku" value="{{colorConfigurableProductAttribute2.sku}}"/> </actionGroup> <!-- Delete product attribute --> - <actionGroup ref="deleteProductAttributeByLabel" stepKey="deleteProductAttribute"> + <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> <argument name="ProductAttribute" value="colorProductAttribute"/> </actionGroup> <!-- Delete attribute set --> - <actionGroup ref="deleteAttributeSetByLabel" stepKey="deleteAttributeSet"> + <actionGroup ref="DeleteAttributeSetByLabelActionGroup" stepKey="deleteAttributeSet"> <argument name="label" value="{{ProductAttributeFrontendLabel.label}}"/> </actionGroup> @@ -58,12 +58,12 @@ <!-- Create configurable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="goToCreateProductPage" stepKey="createConfigurableProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="createConfigurableProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!-- Fill configurable product values --> - <actionGroup ref="fillMainProductForm" stepKey="fillConfigurableProductValues"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillConfigurableProductValues"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> @@ -72,14 +72,14 @@ <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewAttribute}}" time="30" stepKey="waitForConfigurationModalOpen" after="clickCreateConfigurations"/> <!--Create new attribute with two options --> - <actionGroup ref="addNewProductConfigurationAttribute" stepKey="createProductConfigurationAttribute"> + <actionGroup ref="AddNewProductConfigurationAttributeActionGroup" stepKey="createProductConfigurationAttribute"> <argument name="attribute" value="colorProductAttribute"/> <argument name="firstOption" value="colorConfigurableProductAttribute1"/> <argument name="secondOption" value="colorConfigurableProductAttribute2"/> </actionGroup> <!-- Change product configurations in grid --> - <actionGroup ref="changeProductConfigurationsInGrid" stepKey="changeProductConfigurationsInGrid"> + <actionGroup ref="ChangeProductConfigurationsInGridActionGroup" stepKey="changeProductConfigurationsInGrid"> <argument name="firstOption" value="colorConfigurableProductAttribute1"/> <argument name="secondOption" value="colorConfigurableProductAttribute2"/> </actionGroup> @@ -88,18 +88,18 @@ <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="fillCategory" after="fillConfigurableProductValues"/> <!-- Save configurable product; add product to new attribute set --> - <actionGroup ref="saveConfigurableProductWithNewAttributeSet" stepKey="saveConfigurableProduct"/> + <actionGroup ref="SaveConfigurableProductWithNewAttributeSetActionGroup" stepKey="saveConfigurableProduct"/> <!-- Assert child products in grid --> - <actionGroup ref="viewProductInAdminGrid" stepKey="viewFirstChildProductInAdminGrid"> + <actionGroup ref="ViewProductInAdminGridActionGroup" stepKey="viewFirstChildProductInAdminGrid"> <argument name="product" value="colorConfigurableProductAttribute1"/> </actionGroup> - <actionGroup ref="viewProductInAdminGrid" stepKey="viewSecondChildProductInAdminGrid"> + <actionGroup ref="ViewProductInAdminGridActionGroup" stepKey="viewSecondChildProductInAdminGrid"> <argument name="product" value="colorConfigurableProductAttribute2"/> </actionGroup> <!-- Assert configurable product in grid --> - <actionGroup ref="filterProductGridBySkuAndName" stepKey="findCreatedConfigurableProduct"> + <actionGroup ref="FilterProductGridBySkuAndNameActionGroup" stepKey="findCreatedConfigurableProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="{{ApiConfigurableProduct.type_id}}" stepKey="seeProductTypeInGrid"/> @@ -111,7 +111,7 @@ <!--Assert configurable product in category --> <amOnPage url="$$createCategory.name$$.html" stepKey="amOnCategoryPage"/> <waitForPageLoad stepKey="waitForCategoryPageLoad"/> - <actionGroup ref="StorefrontCheckCategoryConfigurableProduct" stepKey="assertConfigurableProductInCategory"> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="assertConfigurableProductInCategory"> <argument name="product" value="ApiConfigurableProduct"/> <argument name="optionProduct" value="colorConfigurableProductAttribute1"/> </actionGroup> @@ -119,7 +119,7 @@ <!--Assert configurable product on product page --> <amOnPage url="{{ApiConfigurableProduct.urlKey}}.html" stepKey="amOnProductPage" after="assertConfigurableProductInCategory"/> <waitForPageLoad stepKey="waitForProductPageLoad"/> - <actionGroup ref="storefrontCheckConfigurableProductOptions" stepKey="checkConfigurableProductOptions"> + <actionGroup ref="StorefrontCheckConfigurableProductOptionsActionGroup" stepKey="checkConfigurableProductOptions"> <argument name="product" value="ApiConfigurableProduct"/> <argument name="firstOption" value="colorConfigurableProductAttribute1"/> <argument name="secondOption" value="colorConfigurableProductAttribute2"/> @@ -131,7 +131,7 @@ <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" stepKey="waitForSuccessMessage"/> <!-- Assert configurable product in cart --> - <amOnPage url="/checkout/cart/" stepKey="amOnShoppingCartPage"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnShoppingCartPage"/> <waitForPageLoad stepKey="waitForShoppingCartPageLoad"/> <actionGroup ref="StorefrontCheckCartConfigurableProductActionGroup" stepKey="storefrontCheckCartConfigurableProductActionGroup"> <argument name="product" value="ApiConfigurableProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml index 934a410d58a8a..14303aa9b650b 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTwoOptionsWithoutAssignedToCategoryTest.xml @@ -22,25 +22,25 @@ </before> <after> <!-- Delete configurable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!-- Delete children products --> - <actionGroup ref="deleteProductBySku" stepKey="deleteFirstChildProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteFirstChildProduct"> <argument name="sku" value="{{colorConfigurableProductAttribute1.sku}}"/> </actionGroup> - <actionGroup ref="deleteProductBySku" stepKey="deleteSecondChildProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteSecondChildProduct"> <argument name="sku" value="{{colorConfigurableProductAttribute2.sku}}"/> </actionGroup> <!-- Delete product attribute --> - <actionGroup ref="deleteProductAttributeByLabel" stepKey="deleteProductAttribute"> + <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> <argument name="ProductAttribute" value="colorProductAttribute"/> </actionGroup> <!-- Delete attribute set --> - <actionGroup ref="deleteAttributeSetByLabel" stepKey="deleteAttributeSet"> + <actionGroup ref="DeleteAttributeSetByLabelActionGroup" stepKey="deleteAttributeSet"> <argument name="label" value="{{ProductAttributeFrontendLabel.label}}"/> </actionGroup> @@ -51,12 +51,12 @@ <!-- Create configurable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="goToCreateProductPage" stepKey="createConfigurableProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="createConfigurableProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!-- Fill configurable product values --> - <actionGroup ref="fillMainProductForm" stepKey="fillConfigurableProductValues"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillConfigurableProductValues"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> @@ -65,31 +65,31 @@ <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewAttribute}}" time="30" stepKey="waitForConfigurationModalOpen" after="clickCreateConfigurations"/> <!--Create new attribute with two option --> - <actionGroup ref="addNewProductConfigurationAttribute" stepKey="createProductConfigurationAttribute"> + <actionGroup ref="AddNewProductConfigurationAttributeActionGroup" stepKey="createProductConfigurationAttribute"> <argument name="attribute" value="colorProductAttribute"/> <argument name="firstOption" value="colorConfigurableProductAttribute1"/> <argument name="secondOption" value="colorConfigurableProductAttribute2"/> </actionGroup> <!-- Change product configurations in grid --> - <actionGroup ref="changeProductConfigurationsInGrid" stepKey="changeProductConfigurationsInGrid"> + <actionGroup ref="ChangeProductConfigurationsInGridActionGroup" stepKey="changeProductConfigurationsInGrid"> <argument name="firstOption" value="colorConfigurableProductAttribute1"/> <argument name="secondOption" value="colorConfigurableProductAttribute2"/> </actionGroup> <!-- Save configurable product; add product to new attribute set --> - <actionGroup ref="saveConfigurableProductWithNewAttributeSet" stepKey="saveConfigurableProduct"/> + <actionGroup ref="SaveConfigurableProductWithNewAttributeSetActionGroup" stepKey="saveConfigurableProduct"/> <!-- Assert Child Products in grid --> - <actionGroup ref="viewProductInAdminGrid" stepKey="viewFirstChildProductInAdminGrid"> + <actionGroup ref="ViewProductInAdminGridActionGroup" stepKey="viewFirstChildProductInAdminGrid"> <argument name="product" value="colorConfigurableProductAttribute1"/> </actionGroup> - <actionGroup ref="viewProductInAdminGrid" stepKey="viewSecondChildProductInAdminGrid"> + <actionGroup ref="ViewProductInAdminGridActionGroup" stepKey="viewSecondChildProductInAdminGrid"> <argument name="product" value="colorConfigurableProductAttribute2"/> </actionGroup> <!-- Assert Configurable Product in grid --> - <actionGroup ref="filterProductGridBySkuAndName" stepKey="findCreatedConfigurableProduct"> + <actionGroup ref="FilterProductGridBySkuAndNameActionGroup" stepKey="findCreatedConfigurableProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="{{ApiConfigurableProduct.type_id}}" stepKey="seeProductTypeInGrid"/> @@ -101,7 +101,7 @@ <!-- Assert configurable product on product page --> <amOnPage url="{{ApiConfigurableProduct.urlKey}}.html" stepKey="amOnProductPage"/> <waitForPageLoad stepKey="waitForProductPageLoad"/> - <actionGroup ref="storefrontCheckConfigurableProductOptions" stepKey="checkConfigurableProductOptions"> + <actionGroup ref="StorefrontCheckConfigurableProductOptionsActionGroup" stepKey="checkConfigurableProductOptions"> <argument name="product" value="ApiConfigurableProduct"/> <argument name="firstOption" value="colorConfigurableProductAttribute1"/> <argument name="secondOption" value="colorConfigurableProductAttribute2"/> @@ -113,7 +113,7 @@ <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" stepKey="waitForSuccessMessage"/> <!-- Assert configurable product in cart --> - <amOnPage url="/checkout/cart/" stepKey="amOnShoppingCartPage"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnShoppingCartPage"/> <waitForPageLoad stepKey="waitForShoppingCartPageLoad"/> <actionGroup ref="StorefrontCheckCartConfigurableProductActionGroup" stepKey="storefrontCheckCartConfigurableProductActionGroup"> <argument name="product" value="ApiConfigurableProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminDeleteConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminDeleteConfigurableProductTest.xml index fb2920be528b6..c2edbaa4e6e87 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminDeleteConfigurableProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminDeleteConfigurableProductTest.xml @@ -28,7 +28,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteConfigurableProductFilteredBySkuAndName"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteConfigurableProductFilteredBySkuAndName"> <argument name="product" value="$$createConfigurableProduct$$"/> </actionGroup> <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="A total of 1 record(s) have been deleted." stepKey="deleteMessage"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml index fa21d20eb4456..f1535d62861ac 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml @@ -39,7 +39,7 @@ <comment userInput="Delete product" stepKey="commentDeleteProduct"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> - <actionGroup ref="deleteAllDuplicateProductUsingProductGrid" stepKey="deleteAllDuplicateProducts"> + <actionGroup ref="DeleteAllDuplicateProductUsingProductGridActionGroup" stepKey="deleteAllDuplicateProducts"> <argument name="product" value="$$createProduct$$"/> </actionGroup> <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> @@ -49,14 +49,14 @@ <comment userInput="Add configurations to product" stepKey="commentAddConfigs"/> <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="gotToSimpleProductPage"/> <waitForPageLoad stepKey="waitForSimpleProductPageLoad"/> - <actionGroup ref="generateConfigurationsByAttributeCode" stepKey="setupConfigurations"> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="setupConfigurations"> <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> </actionGroup> - <actionGroup ref="saveConfiguredProduct" stepKey="saveConfigProductForm"/> + <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveConfigProductForm"/> <!--Assert configurable product on Admin product page grid--> <comment userInput="Assert configurable product in Admin product page grid" stepKey="commentAssertConfigProductOnAdmin"/> <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPage"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGridBySku"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySku"> <argument name="sku" value="$$createProduct.sku$$"/> </actionGroup> <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeProductNameInGrid"/> @@ -97,11 +97,11 @@ <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{SimpleProduct2.quantity}}" stepKey="fillProductQty"/> <clearField selector="{{AdminProductFormSection.productWeight}}" stepKey="clearWeightField"/> <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has no weight" stepKey="selectNoWeight"/> - <actionGroup ref="saveProductForm" stepKey="saveVirtualProductForm"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveVirtualProductForm"/> <!--Assert virtual product on Admin product page grid--> <comment userInput="Assert virtual product on Admin product page grid" stepKey="commentAssertVirtualProductOnAdmin"/> <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPageForVirtual"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGridBySkuForVirtual"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySkuForVirtual"> <argument name="sku" value="$$createProduct.sku$$"/> </actionGroup> <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeVirtualProductNameInGrid"/> @@ -143,7 +143,7 @@ <comment userInput="Delete product" stepKey="commentDeleteProduct"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> - <actionGroup ref="deleteAllDuplicateProductUsingProductGrid" stepKey="deleteAllDuplicateProducts"> + <actionGroup ref="DeleteAllDuplicateProductUsingProductGridActionGroup" stepKey="deleteAllDuplicateProducts"> <argument name="product" value="$$createProduct$$"/> </actionGroup> <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> @@ -154,14 +154,14 @@ <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="gotToConfigProductPage"/> <waitForPageLoad stepKey="waitForConfigurableProductPageLoad"/> <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeightForConfigurableProduct"/> - <actionGroup ref="generateConfigurationsByAttributeCode" stepKey="setupConfigurationsForProduct"> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="setupConfigurationsForProduct"> <argument name="attributeCode" value="$$createConfigProductAttribute.attribute_code$$"/> </actionGroup> - <actionGroup ref="saveConfiguredProduct" stepKey="saveNewConfigurableProductForm"/> + <actionGroup ref="SaveConfiguredProductActionGroup" stepKey="saveNewConfigurableProductForm"/> <!--Assert configurable product on Admin product page grid--> <comment userInput="Assert configurable product in Admin product page grid" stepKey="commentAssertConfigurableProductOnAdmin"/> <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPageForConfigurable"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGridBySkuForConfigurable"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySkuForConfigurable"> <argument name="sku" value="$$createProduct.sku$$"/> </actionGroup> <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeConfigurableProductNameInGrid"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRelatedProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRelatedProductsTest.xml index aa34693ed82f0..710430cf123dc 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRelatedProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRelatedProductsTest.xml @@ -94,18 +94,18 @@ <comment userInput="Filter and edit simple product 1" stepKey="filterAndEditComment1"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="productIndexPage"/> <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridSimple"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridSimple"> <argument name="product" value="$$simple1Handle$$"/> </actionGroup> - <actionGroup ref="openProducForEditByClickingRowXColumnYInProductGrid" stepKey="openProducForEditByClickingRow1Column2InProductGrid"/> + <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProducForEditByClickingRow1Column2InProductGrid"/> <conditionalClick selector="{{AdminProductFormSection.productFormTab('Related Products')}}" dependentSelector="{{AdminProductFormSection.productFormTabState('Related Products', 'closed')}}" visible="true" stepKey="openRelatedProductTab"/> <waitForPageLoad time="30" stepKey="waitForPageLoad3"/> <!-- TODO: move adding related product to a action group when nested action group is allowed (ref#: MQE-539)--> <comment userInput="Add related simple product to simple product" stepKey="addSimpleToSimpleComment"/> <click selector="{{AdminProductFormRelatedUpSellCrossSellSection.AddRelatedProductsButton}}" stepKey="clickAddRelatedProductButton"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridSimple1"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridSimple1"> <argument name="product" value="$$simple2Handle$$"/> </actionGroup> <click selector="{{AdminProductModalSlideGridSection.productGridXRowYColumnButton('1', '1')}}" stepKey="selectSimpleTwo"/> @@ -114,7 +114,7 @@ <comment userInput="Add related config product to simple product" stepKey="addConfigToSimpleComment"/> <click selector="{{AdminProductFormRelatedUpSellCrossSellSection.AddRelatedProductsButton}}" stepKey="clickAddRelatedProductButton2"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridSimpleForRelatedConfig1"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridSimpleForRelatedConfig1"> <argument name="product" value="$$baseConfigProductHandle$$"/> </actionGroup> <click selector="{{AdminProductModalSlideGridSection.productGridXRowYColumnButton('1', '1')}}" stepKey="selectConfigProduct"/> @@ -122,16 +122,16 @@ <click selector="{{AdminAddRelatedProductsModalSection.AddSelectedProductsButton}}" stepKey="addRelatedProductSelected2"/> <comment userInput="Save simple product" stepKey="saveSimpleProductComment"/> - <actionGroup ref="saveProductForm" stepKey="saveRelatedProduct1"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveRelatedProduct1"/> <comment userInput="Assert related simple products for simple product in Admin Product Form" stepKey="assertRelated1Comment"/> - <actionGroup ref="AssertTextInAdminProductRelatedUpSellCrossSellSection" stepKey="assertRelated1"> + <actionGroup ref="AssertTextInAdminProductRelatedUpSellCrossSellSectionActionGroup" stepKey="assertRelated1"> <argument name="element" value="AdminProductFormRelatedUpSellCrossSellSection.relatedProductSectionText"/> <argument name="expectedText" value="$$simple2Handle.name$$"/> </actionGroup> <comment userInput="Assert related config products for simple product in Admin Product Form" stepKey="assertRelated2Comment"/> - <actionGroup ref="AssertTextInAdminProductRelatedUpSellCrossSellSection" stepKey="assertRelated2"> + <actionGroup ref="AssertTextInAdminProductRelatedUpSellCrossSellSectionActionGroup" stepKey="assertRelated2"> <argument name="element" value="AdminProductFormRelatedUpSellCrossSellSection.relatedProductSectionText"/> <argument name="expectedText" value="$$baseConfigProductHandle.name$$"/> </actionGroup> @@ -139,17 +139,17 @@ <comment userInput="Filter and edit config product" stepKey="filterAndEditComment2"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="productIndexPage2"/> <waitForPageLoad time="30" stepKey="waitForPageLoad5"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial2"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridConfig"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial2"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridConfig"> <argument name="product" value="$$baseConfigProductHandle$$"/> </actionGroup> - <actionGroup ref="openProducForEditByClickingRowXColumnYInProductGrid" stepKey="openProducForEditByClickingRow1Column2InProductGrid2"/> + <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProducForEditByClickingRow1Column2InProductGrid2"/> <conditionalClick selector="{{AdminProductFormSection.productFormTab('Related Products')}}" dependentSelector="{{AdminProductFormSection.productFormTabState('Related Products', 'closed')}}" visible="true" stepKey="openRelatedProductTab2"/> <waitForPageLoad time="30" stepKey="waitForPageLoad7"/> <comment userInput="Add related simple product to config product" stepKey="addSimpleToConfigComment"/> <click selector="{{AdminProductFormRelatedUpSellCrossSellSection.AddRelatedProductsButton}}" stepKey="clickAddRelatedProductButton3"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridForConfig3"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridForConfig3"> <argument name="product" value="$$simple2Handle$$"/> </actionGroup> <click selector="{{AdminProductModalSlideGridSection.productGridXRowYColumnButton('1', '1')}}" stepKey="selectSimpleTwo2"/> @@ -157,10 +157,10 @@ <click selector="{{AdminAddRelatedProductsModalSection.AddSelectedProductsButton}}" stepKey="addRelatedProductSelected3"/> <comment userInput="Save config product" stepKey="saveConfigProductComment"/> - <actionGroup ref="saveProductForm" stepKey="saveRelatedProduct2"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveRelatedProduct2"/> <comment userInput="Assert related simple product for config product in Admin Product Form" stepKey="assertRelated3Comment"/> - <actionGroup ref="AssertTextInAdminProductRelatedUpSellCrossSellSection" stepKey="assertRelated3"> + <actionGroup ref="AssertTextInAdminProductRelatedUpSellCrossSellSectionActionGroup" stepKey="assertRelated3"> <argument name="element" value="AdminProductFormRelatedUpSellCrossSellSection.relatedProductSectionText"/> <argument name="expectedText" value="$$simple2Handle.name$$"/> </actionGroup> @@ -187,17 +187,17 @@ <comment userInput="Check related product in product page" stepKey="checkRelatedProductInProductPageComment"/> <click selector="{{StorefrontProductRelatedProductsSection.relatedProductCheckBoxButton('$$simple2Handle.name$$')}}" stepKey="checkRelatedProcut"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> <argument name="productName" value="$$baseConfigProductHandle.name$$"/> </actionGroup> <see selector="{{StorefrontMinicartSection.quantity}}" userInput="2" stepKey="seeItemCounterInMiniCart"/> - <actionGroup ref="assertOneProductNameInMiniCart" stepKey="assertOneProductNameInMiniCart"> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="assertOneProductNameInMiniCart"> <argument name="productName" value="$$baseConfigProductHandle.name$$"/> </actionGroup> - <actionGroup ref="assertOneProductNameInMiniCart" stepKey="assertOneProductNameInMiniCart2"> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="assertOneProductNameInMiniCart2"> <argument name="productName" value="$$simple2Handle.name$$"/> </actionGroup> </test> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml index e7492f4eeaecf..0cc73f117aaad 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml @@ -93,33 +93,33 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="productIndexPage"/> <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGrid"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGrid"> <argument name="product" value="$$baseConfigProductHandle$$"/> </actionGroup> - <actionGroup ref="openProducForEditByClickingRowXColumnYInProductGrid" stepKey="openProducForEditByClickingRow1Column2InProductGrid1"/> + <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProducForEditByClickingRow1Column2InProductGrid1"/> <!-- Add image to product --> - <actionGroup ref="addProductImage" stepKey="addImageForProduct"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImageForProduct"> <argument name="image" value="MagentoLogo"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Remove image from product --> - <actionGroup ref="removeProductImage" stepKey="removeProductImage"/> + <actionGroup ref="RemoveProductImageActionGroup" stepKey="removeProductImage"/> - <actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductFormAfterRemove"/> <!-- Assert product image not in admin product form --> - <actionGroup ref="assertProductImageNotInAdminProductPage" stepKey="assertProductImageNotInAdminProductPage"/> + <actionGroup ref="AssertProductImageNotInAdminProductPageActionGroup" stepKey="assertProductImageNotInAdminProductPage"/> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="AssertProductInStorefrontProductPageAfterRemove"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="AssertProductInStorefrontProductPageAfterRemove"> <argument name="product" value="$$baseConfigProductHandle$$"/> </actionGroup> <!-- Assert product image not in storefront product page --> - <actionGroup ref="assertProductImageNotInStorefrontProductPage2" stepKey="assertProductImageNotInStorefrontProductPage2"> + <actionGroup ref="AssertProductImageNotInStorefrontProductPage2ActionGroup" stepKey="assertProductImageNotInStorefrontProductPage2"> <argument name="product" value="$$baseConfigProductHandle$$"/> </actionGroup> </test> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml index 7fbff5eac2583..bb16d04dfc94a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductAttributeNameDesignTest.xml @@ -26,16 +26,16 @@ <!-- Navigate to Catalog-> Products --> - <actionGroup ref="GotoCatalogProductsPage" stepKey="goToCatalogProductsPage"/> + <actionGroup ref="GotoCatalogProductsPageActionGroup" stepKey="goToCatalogProductsPage"/> <!-- Fill the fields on Configurable Product--> - <actionGroup ref="GotoConfigurableProductPage" stepKey="goToConfigurableProductPage"/> - <actionGroup ref="FillAllRequiredFields" stepKey="fillInAllRequiredFields"/> + <actionGroup ref="GotoConfigurableProductPageActionGroup" stepKey="goToConfigurableProductPage"/> + <actionGroup ref="FillAllRequiredFieldsActionGroup" stepKey="fillInAllRequiredFields"/> <!-- Create New Attribute (Default Label= design) --> - <actionGroup ref="CreateNewAttribute" stepKey="createNewAttribute"/> + <actionGroup ref="CreateNewAttributeActionGroup" stepKey="createNewAttribute"/> <after> <!-- Delete Created Attribute --> - <actionGroup ref="DeleteCreatedAttribute" stepKey="deleteCreatedAttribute"/> + <actionGroup ref="DeleteCreatedAttributeActionGroup" stepKey="deleteCreatedAttribute"/> </after> </test> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml index 83d9bbe8c270a..0b078bc7d2a98 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml @@ -19,7 +19,7 @@ <group value="ConfigurableProduct"/> </annotations> <before> - + <createData entity="ApiCategory" stepKey="createCategory"/> <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> <requiredEntity createDataKey="createCategory"/> @@ -73,14 +73,14 @@ <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="ResetWebUrlOptions" stepKey="resetUrlOption"/> + <actionGroup ref="ResetWebUrlOptionsActionGroup" stepKey="resetUrlOption"/> <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteSecondWebsite"> <argument name="websiteName" value="Second Website"/> </actionGroup> <actionGroup ref="logout" stepKey="adminLogout"/> </after> - <actionGroup ref="EnableWebUrlOptions" stepKey="addStoreCodeToUrls"/> + <actionGroup ref="EnableWebUrlOptionsActionGroup" stepKey="addStoreCodeToUrls"/> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="addNewWebsite"> <argument name="newWebsiteName" value="Second Website"/> <argument name="websiteCode" value="second_website"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CAdminTest.xml index 6bbb97c66cdd8..b2cc4ddf8f6e0 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -11,11 +11,11 @@ <!--Create configurable product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPageConfigurable" after="seeSimpleProductInGrid"/> <waitForPageLoad stepKey="waitForProductPageLoadConfigurable" after="visitAdminProductPageConfigurable"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateConfigurableProduct" after="waitForProductPageLoadConfigurable"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateConfigurableProduct" after="waitForProductPageLoadConfigurable"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> - <actionGroup ref="checkRequiredFieldsInProductForm" stepKey="checkRequiredFieldsProductConfigurable" after="goToCreateConfigurableProduct"/> - <actionGroup ref="fillMainProductForm" stepKey="fillConfigurableProductMain" after="checkRequiredFieldsProductConfigurable"> + <actionGroup ref="CheckRequiredFieldsInProductFormActionGroup" stepKey="checkRequiredFieldsProductConfigurable" after="goToCreateConfigurableProduct"/> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillConfigurableProductMain" after="checkRequiredFieldsProductConfigurable"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> <!--Create product configurations--> @@ -63,14 +63,14 @@ <waitForElementVisible selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" time="30" stepKey="waitForAttributeSetConfirmation" after="clickSaveConfigurableProduct"/> <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickConfirmAttributeSet" after="waitForAttributeSetConfirmation"/> <see selector="You saved the product" stepKey="seeConfigurableSaveConfirmation" after="clickConfirmAttributeSet"/> - <actionGroup ref="viewConfigurableProductInAdminGrid" stepKey="viewConfigurableProductInGrid" after="seeConfigurableSaveConfirmation"> + <actionGroup ref="ViewConfigurableProductInAdminGridActionGroup" stepKey="viewConfigurableProductInGrid" after="seeConfigurableSaveConfirmation"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> <!--@TODO Move cleanup to "after" when MQE-830 is resolved--> <comment userInput="Clean up configurable product" stepKey="cleanUpConfigurableProduct" after="deleteSimpleProduct"/> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteConfigurableProduct" after="cleanUpConfigurableProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteConfigurableProduct" after="cleanUpConfigurableProduct"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index 04687a2314dc6..59bb7f53f0aa8 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -85,7 +85,7 @@ <!-- Check configurable product in category --> <comment userInput="Verify Configurable Product in category" stepKey="commentVerifyConfigurableProductInCategory" after="browseAssertSimpleProduct2ImageNotDefault" /> - <actionGroup ref="StorefrontCheckCategoryConfigurableProduct" stepKey="browseAssertCategoryConfigProduct" after="commentVerifyConfigurableProductInCategory"> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="browseAssertCategoryConfigProduct" after="commentVerifyConfigurableProductInCategory"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -98,7 +98,7 @@ <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory2" after="commentViewConfigurableProduct"/> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="browseClickCategoryConfigProductView" after="clickCategory2"/> <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductViewloaded" after="browseClickCategoryConfigProductView"/> - <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="browseAssertConfigProductPage" after="waitForConfigurableProductViewloaded"> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="browseAssertConfigProductPage" after="waitForConfigurableProductViewloaded"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -114,7 +114,7 @@ <argument name="category" value="$$createCategory$$"/> <argument name="productCount" value="3"/> </actionGroup> - <actionGroup ref="StorefrontCheckCategoryConfigurableProduct" stepKey="cartAssertConfigProduct" after="cartAssertCategory1ForConfigurableProduct"> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="cartAssertConfigProduct" after="cartAssertCategory1ForConfigurableProduct"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -123,7 +123,7 @@ <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabConfigProductImageSrc" stepKey="cartAssertConfigProductImageNotDefault" after="cartGrabConfigProductImageSrc"/> <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$createConfigProduct.name$$)}}" stepKey="cartClickCategoryConfigProductAddToCart" after="cartAssertConfigProductImageNotDefault"/> <waitForElement selector="{{StorefrontMessagesSection.message('You need to choose options for your item.')}}" time="30" stepKey="cartWaitForConfigProductPageLoad" after="cartClickCategoryConfigProductAddToCart"/> - <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="cartAssertConfigProductPage" after="cartWaitForConfigProductPageLoad"> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductPage" after="cartWaitForConfigProductPageLoad"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -132,7 +132,7 @@ <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabConfigProductPageImageSrc1" stepKey="cartAssertConfigProductPageImageNotDefault1" after="cartGrabConfigProductPageImageSrc1"/> <selectOption userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontProductInfoMainSection.optionByAttributeId($$createConfigProductAttribute.attribute_id$$)}}" stepKey="cartConfigProductFillOption" after="cartAssertConfigProductPageImageNotDefault1"/> <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductOptionloaded" after="cartConfigProductFillOption"/> - <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="cartAssertConfigProductWithOptionPage" after="waitForConfigurableProductOptionloaded"> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductWithOptionPage" after="waitForConfigurableProductOptionloaded"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> </actionGroup> @@ -157,7 +157,7 @@ <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontMinicartSection.productOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartMinicartCheckConfigProductOption" after="cartMinicartClickConfigProductDetails"/> <click selector="{{StorefrontMinicartSection.productLinkByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProduct" after="cartMinicartCheckConfigProductOption"/> <waitForLoadingMaskToDisappear stepKey="waitForMinicartConfigProductloaded" after="cartMinicartClickConfigProduct"/> - <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="cartAssertMinicartConfigProductPage" after="waitForMinicartConfigProductloaded"> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertMinicartConfigProductPage" after="waitForMinicartConfigProductloaded"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -180,7 +180,7 @@ <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{CheckoutCartProductSection.ProductOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartCheckConfigProductOption" after="cartCartAssertConfigProduct2ImageNotDefault"/> <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createConfigProduct.name$$)}}" stepKey="cartClickCartConfigProduct" after="cartCheckConfigProductOption"/> <waitForLoadingMaskToDisappear stepKey="waitForCartConfigProductloaded" after="cartClickCartConfigProduct"/> - <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="cartAssertCartConfigProductPage" after="waitForCartConfigProductloaded"> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertCartConfigProductPage" after="waitForCartConfigProductloaded"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -190,7 +190,7 @@ <!-- Add Configurable Product to comparison --> <comment userInput="Add Configurable Product to comparison" stepKey="commentAddConfigurableProductToComparison" after="compareAddSimpleProduct2ToCompare" /> - <actionGroup ref="StorefrontCheckCategoryConfigurableProduct" stepKey="compareAssertConfigProduct" after="commentAddConfigurableProductToComparison"> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="compareAssertConfigProduct" after="commentAddConfigurableProductToComparison"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -294,7 +294,7 @@ <!-- Check configurable product in category --> <comment userInput="Verify Configurable Product in category" stepKey="commentVerifyConfigurableProductInCategory" after="browseAssertSimpleProduct2ImageNotDefault" /> - <actionGroup ref="StorefrontCheckCategoryConfigurableProduct" stepKey="browseAssertCategoryConfigProduct" after="commentVerifyConfigurableProductInCategory"> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="browseAssertCategoryConfigProduct" after="commentVerifyConfigurableProductInCategory"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -307,7 +307,7 @@ <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory2" after="commentViewConfigurableProduct"/> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="browseClickCategoryConfigProductView" after="clickCategory2"/> <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductViewloaded" after="browseClickCategoryConfigProductView"/> - <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="browseAssertConfigProductPage" after="waitForConfigurableProductViewloaded"> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="browseAssertConfigProductPage" after="waitForConfigurableProductViewloaded"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -323,7 +323,7 @@ <argument name="category" value="$$createCategory$$"/> <argument name="productCount" value="3"/> </actionGroup> - <actionGroup ref="StorefrontCheckCategoryConfigurableProduct" stepKey="cartAssertConfigProduct" after="cartAssertCategory1ForConfigurableProduct"> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="cartAssertConfigProduct" after="cartAssertCategory1ForConfigurableProduct"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -332,7 +332,7 @@ <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabConfigProductImageSrc" stepKey="cartAssertConfigProductImageNotDefault" after="cartGrabConfigProductImageSrc"/> <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$createConfigProduct.name$$)}}" stepKey="cartClickCategoryConfigProductAddToCart" after="cartAssertConfigProductImageNotDefault"/> <waitForElement selector="{{StorefrontMessagesSection.message('You need to choose options for your item.')}}" time="30" stepKey="cartWaitForConfigProductPageLoad" after="cartClickCategoryConfigProductAddToCart"/> - <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="cartAssertConfigProductPage" after="cartWaitForConfigProductPageLoad"> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductPage" after="cartWaitForConfigProductPageLoad"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -341,7 +341,7 @@ <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabConfigProductPageImageSrc1" stepKey="cartAssertConfigProductPageImageNotDefault1" after="cartGrabConfigProductPageImageSrc1"/> <selectOption userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontProductInfoMainSection.optionByAttributeId($$createConfigProductAttribute.attribute_id$$)}}" stepKey="cartConfigProductFillOption" after="cartAssertConfigProductPageImageNotDefault1"/> <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductOptionloaded" after="cartConfigProductFillOption"/> - <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="cartAssertConfigProductWithOptionPage" after="waitForConfigurableProductOptionloaded"> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductWithOptionPage" after="waitForConfigurableProductOptionloaded"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> </actionGroup> @@ -366,7 +366,7 @@ <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontMinicartSection.productOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartMinicartCheckConfigProductOption" after="cartMinicartClickConfigProductDetails"/> <click selector="{{StorefrontMinicartSection.productLinkByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProduct" after="cartMinicartCheckConfigProductOption"/> <waitForLoadingMaskToDisappear stepKey="waitForMinicartConfigProductloaded" after="cartMinicartClickConfigProduct"/> - <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="cartAssertMinicartConfigProductPage" after="waitForMinicartConfigProductloaded"> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertMinicartConfigProductPage" after="waitForMinicartConfigProductloaded"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -389,7 +389,7 @@ <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{CheckoutCartProductSection.ProductOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartCheckConfigProductOption" after="cartCartAssertConfigProduct2ImageNotDefault"/> <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createConfigProduct.name$$)}}" stepKey="cartClickCartConfigProduct" after="cartCheckConfigProductOption"/> <waitForLoadingMaskToDisappear stepKey="waitForCartConfigProductloaded" after="cartClickCartConfigProduct"/> - <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="cartAssertCartConfigProductPage" after="waitForCartConfigProductloaded"> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertCartConfigProductPage" after="waitForCartConfigProductloaded"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -399,7 +399,7 @@ <!-- Add Configurable Product to comparison --> <comment userInput="Add Configurable Product to comparison" stepKey="commentAddConfigurableProductToComparison" after="compareAddSimpleProduct2ToCompare" /> - <actionGroup ref="StorefrontCheckCategoryConfigurableProduct" stepKey="compareAssertConfigProduct" after="commentAddConfigurableProductToComparison"> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="compareAssertConfigProduct" after="commentAddConfigurableProductToComparison"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index 6f9ad93a56dc5..dd0673563838e 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -85,7 +85,7 @@ <!-- Check configurable product in category --> <comment userInput="Verify Configurable Product in category" stepKey="commentVerifyConfigurableProductInCategory" after="browseAssertSimpleProduct2ImageNotDefault" /> - <actionGroup ref="StorefrontCheckCategoryConfigurableProduct" stepKey="browseAssertCategoryConfigProduct" after="commentVerifyConfigurableProductInCategory"> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="browseAssertCategoryConfigProduct" after="commentVerifyConfigurableProductInCategory"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -98,7 +98,7 @@ <click selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="clickCategory2" after="commentViewConfigurableProduct"/> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="browseClickCategoryConfigProductView" after="clickCategory2"/> <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductViewloaded" after="browseClickCategoryConfigProductView"/> - <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="browseAssertConfigProductPage" after="waitForConfigurableProductViewloaded"> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="browseAssertConfigProductPage" after="waitForConfigurableProductViewloaded"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -114,7 +114,7 @@ <argument name="category" value="$$createCategory$$"/> <argument name="productCount" value="3"/> </actionGroup> - <actionGroup ref="StorefrontCheckCategoryConfigurableProduct" stepKey="cartAssertConfigProduct" after="cartAssertCategory1ForConfigurableProduct"> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="cartAssertConfigProduct" after="cartAssertCategory1ForConfigurableProduct"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -123,7 +123,7 @@ <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$cartGrabConfigProductImageSrc" stepKey="cartAssertConfigProductImageNotDefault" after="cartGrabConfigProductImageSrc"/> <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName($$createConfigProduct.name$$)}}" stepKey="cartClickCategoryConfigProductAddToCart" after="cartAssertConfigProductImageNotDefault"/> <waitForElement selector="{{StorefrontMessagesSection.message('You need to choose options for your item.')}}" time="30" stepKey="cartWaitForConfigProductPageLoad" after="cartClickCategoryConfigProductAddToCart"/> - <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="cartAssertConfigProductPage" after="cartWaitForConfigProductPageLoad"> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductPage" after="cartWaitForConfigProductPageLoad"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -132,7 +132,7 @@ <assertNotRegExp expected="'/placeholder\/image\.jpg/'" actual="$cartGrabConfigProductPageImageSrc1" stepKey="cartAssertConfigProductPageImageNotDefault1" after="cartGrabConfigProductPageImageSrc1"/> <selectOption userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontProductInfoMainSection.optionByAttributeId($$createConfigProductAttribute.attribute_id$$)}}" stepKey="cartConfigProductFillOption" after="cartAssertConfigProductPageImageNotDefault1"/> <waitForLoadingMaskToDisappear stepKey="waitForConfigurableProductOptionloaded" after="cartConfigProductFillOption"/> - <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="cartAssertConfigProductWithOptionPage" after="waitForConfigurableProductOptionloaded"> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertConfigProductWithOptionPage" after="waitForConfigurableProductOptionloaded"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct2$$"/> </actionGroup> @@ -157,7 +157,7 @@ <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{StorefrontMinicartSection.productOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartMinicartCheckConfigProductOption" after="cartMinicartClickConfigProductDetails"/> <click selector="{{StorefrontMinicartSection.productLinkByName($$createConfigProduct.name$$)}}" stepKey="cartMinicartClickConfigProduct" after="cartMinicartCheckConfigProductOption"/> <waitForLoadingMaskToDisappear stepKey="waitForMinicartConfigProductloaded" after="cartMinicartClickConfigProduct"/> - <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="cartAssertMinicartConfigProductPage" after="waitForMinicartConfigProductloaded"> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertMinicartConfigProductPage" after="waitForMinicartConfigProductloaded"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -180,7 +180,7 @@ <see userInput="$$createConfigProductAttributeOption2.option[store_labels][1][label]$$" selector="{{CheckoutCartProductSection.ProductOptionByNameAndAttribute($$createConfigProduct.name$$, $$createConfigProductAttribute.attribute[frontend_labels][0][label]$$)}}" stepKey="cartCheckConfigProductOption" after="cartCartAssertConfigProduct2ImageNotDefault"/> <click selector="{{CheckoutCartProductSection.ProductLinkByName($$createConfigProduct.name$$)}}" stepKey="cartClickCartConfigProduct" after="cartCheckConfigProductOption"/> <waitForLoadingMaskToDisappear stepKey="waitForCartConfigProductloaded" after="cartClickCartConfigProduct"/> - <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="cartAssertCartConfigProductPage" after="waitForCartConfigProductloaded"> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="cartAssertCartConfigProductPage" after="waitForCartConfigProductloaded"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -190,7 +190,7 @@ <!-- Add Configurable Product to comparison --> <comment userInput="Add Configurable Product to comparison" stepKey="commentAddConfigurableProductToComparison" after="compareAddSimpleProduct2ToCompare" /> - <actionGroup ref="StorefrontCheckCategoryConfigurableProduct" stepKey="compareAssertConfigProduct" after="commentAddConfigurableProductToComparison"> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="compareAssertConfigProduct" after="commentAddConfigurableProductToComparison"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml new file mode 100644 index 0000000000000..42bad3e4bb8bf --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoErrorForMiniCartItemEditTest.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="NoErrorForMiniCartItemEditTest"> + <annotations> + <features value="ConfigurableProduct"/> + <title value="No error for minicart item edit test"/> + <description value="Already selected configurable option should be selected when configurable product is edited from minicart"/> + <severity value="MAJOR"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + <createData entity="ApiCategory" stepKey="createCategory"/> + <!-- Create Configurable product --> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> + <argument name="product" value="_defaultProduct"/> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <!-- Delete the first simple product --> + <actionGroup stepKey="deleteProduct1" ref="DeleteProductBySkuActionGroup"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" + dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" + stepKey="clickClearFilters"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Go To Created Product Page --> + <amOnPage stepKey="goToCreatedProductPage" url="{{_defaultProduct.urlKey}}.html"/> + <waitForPageLoad stepKey="waitForProductPageLoad2"/> + + <!-- Add Product to Cart --> + <seeElement selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" + stepKey="checkDropDownProductOption"/> + <selectOption userInput="{{colorProductAttribute1.name}}" + selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" + stepKey="selectOption1"/> + <selectOption userInput="{{colorProductAttribute2.name}}" + selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" + stepKey="selectOption2"/> + <click selector="{{StorefrontProductInfoMainSection.productAttributeOptions1}}" + stepKey="clickDropDownProductOption"/> + <selectOption userInput="{{colorProductAttribute1.name}}" + selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" + stepKey="selectOptionForAddingToCart"/> + <click selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="clickAddToCart"/> + <waitForPageLoad stepKey="waitForMiniCart"/> + + <!-- Edit Item in Cart --> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniCart"/> + <click selector="{{StorefrontMinicartSection.editMiniCartItem}}" stepKey="clickEditCartItem"/> + + <!-- Check if Product Configuration is still selected --> + <see selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" + userInput="{{colorProductAttribute1.name}}" stepKey="seeConfigurationSelected"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml new file mode 100644 index 0000000000000..5224cc6a9cced --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml @@ -0,0 +1,160 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="NoOptionAvailableToConfigureDisabledProductTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Admin order configurable product"/> + <title value="Disabled variation of configurable product can't be added to shopping cart via admin"/> + <description value="Disabled variation of configurable product can't be added to shopping cart via admin"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-17373"/> + <useCaseId value="MAGETWO-72172"/> + <group value="ConfigurableProduct"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <!--Create category--> + <comment userInput="Create category" stepKey="commentCreateCategory"/> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <!-- Create the configurable product based on the data in the data folder --> + <comment userInput="Create the configurable product based on the data in the data folder" stepKey="createConfigurableProduct"/> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- Create the configurable product with two options based on the default attribute set --> + <comment userInput="Create the configurable product with two options based on the default attribute set" stepKey="configurableProductWithTwoOptions"/> + <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> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="3" stepKey="getConfigAttributeOption3"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <!-- Create the 3 children that will be a part of the configurable product --> + <comment userInput="Create the 3 children that will be a part of the configurable product" stepKey="createTwoChildrenProducts"/> + <createData entity="ApiSimpleProductWithPrice50" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleProductWithPrice60" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ApiSimpleProductWithPrice70" stepKey="createConfigChildProduct3"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption3"/> + </createData> + <!-- Assign 3 products to the configurable product --> + <comment userInput="Assign 3 products to the configurable product" stepKey="assignToConfigurableProduct"/> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + <requiredEntity createDataKey="getConfigAttributeOption3"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild3"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct3"/> + </createData> + <!-- Create Customer --> + <comment userInput="Create customer" stepKey="commentCreateCustomer"/> + <createData entity="Simple_US_Customer_CA" stepKey="createCustomer"/> + </before> + <after> + <!-- Delete created data --> + <comment userInput="Delete created data" stepKey="deleteData"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory2"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigChildProduct3" stepKey="deleteConfigChildProduct3"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCreatedCustomer"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Disable child product --> + <comment userInput="Disable child product" stepKey="disableChildProduct"/> + <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct1.id$$)}}" stepKey="goToEditPage"/> + <waitForPageLoad stepKey="waitForChildProductPageLoad"/> + <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="disableProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> + <!-- Set the second product out of stock --> + <comment userInput="Set the second product out of stock" stepKey="outOfStockChildProduct"/> + <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct2.id$$)}}" stepKey="goToSecondProductEditPage"/> + <waitForPageLoad stepKey="waitForSecondChildProductPageLoad"/> + <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="Out of Stock" stepKey="outOfStockStatus"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSecondProductForm"/> + <!-- Go to created customer page --> + <comment userInput="Go to created customer page" stepKey="goToCreatedCustomerPage"/> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="createNewOrder"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickToAddProduct"/> + <waitForPageLoad stepKey="waitForProductsOpened"/> + <!-- Find created configurable product and click on "Configure" link --> + <comment userInput="Find created configurable product and click on Configure link" stepKey="goToConfigurableLink"/> + <click selector="{{AdminOrderFormConfigureProductSection.configure($$createConfigProduct.id$$)}}" stepKey="clickOnConfigure"/> + <!-- Click on attribute drop-down and check no option 1 is available --> + <comment userInput="Click on attribute drop-down and check no option 1 is available" stepKey="commentNoOptionIsAvailable"/> + <waitForElement selector="{{AdminOrderFormConfigureProductSection.selectOption}}" stepKey="waitForShippingSectionLoaded"/> + <click selector="{{AdminOrderFormConfigureProductSection.selectOption}}" stepKey="clickToSelectOption"/> + <dontSee userInput="$$createConfigProductAttributeOption1.option[store_labels][1][label]$$" stepKey="dontSeeOption1"/> + <!-- Go to created customer page again --> + <comment userInput="Go to created customer page again" stepKey="goToCreatedCustomerPageAgain"/> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="createNewOrderAgain"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickToAddProductAgain"/> + <waitForPageLoad stepKey="waitForProductsOpenedAgain"/> + <fillField selector="{{AdminOrderFormItemsSection.idFilter}}" userInput="$$createConfigChildProduct2.id$$" stepKey="idFilter"/> + <click selector="{{AdminOrderFormItemsSection.search}}" stepKey="clickSearch"/> + <checkOption selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" stepKey="selectConfigurableProduct"/> + <!-- Add product to order --> + <comment userInput="Add product to order" stepKey="addProductToOrder"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="clickToAddProductToOrder"/> + <waitForPageLoad stepKey="waitForNewOrderPageLoad"/> + <see userInput="This product is out of stock." stepKey="seeTheErrorMessageDisplayed"/> + <!-- Select shipping method --> + <comment userInput="Select shipping method" stepKey="selectShippingMethod"/> + <click selector="{{AdminInvoicePaymentShippingSection.getShippingMethodAndRates}}" stepKey="openShippingMethod"/> + <waitForPageLoad stepKey="waitForShippingMethods"/> + <click selector="{{AdminInvoicePaymentShippingSection.shippingMethod}}" stepKey="chooseShippingMethod"/> + <waitForPageLoad stepKey="waitForShippingMethodLoad"/> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="clickSubmitOrder"/> + <waitForPageLoad stepKey="waitForSuccess"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeSuccessMessage"/> + </test> +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml index c3459aec34492..6045ca5567b45 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml @@ -49,7 +49,7 @@ <waitForPageLoad stepKey="waitForProductPage"/> <fillField selector="{{StorefrontProductInfoMainSection.qty}}" userInput="4" stepKey="fillQuantity"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> <argument name="productName" value="$$createConfigProduct.name$$"/> </actionGroup> @@ -66,7 +66,7 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> @@ -83,7 +83,7 @@ <fillField selector="{{AdminShipmentItemsSection.itemQtyToShip('1')}}" userInput="1" stepKey="changeItemQtyToShip"/> <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="clickSubmitShipment"/> <waitForPageLoad stepKey="waitShipmentSectionToLoad"/> - <actionGroup ref="cancelPendingOrder" stepKey="cancelPendingOption"> + <actionGroup ref="CancelPendingOrderActionGroup" stepKey="cancelPendingOption"> <argument name="orderStatus" value="Complete"/> </actionGroup> @@ -91,7 +91,7 @@ <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="GoToCatalogProductPage"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGridBySku"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySku"> <argument name="sku" value="$$createConfigProduct.sku$$"/> </actionGroup> <see selector="{{AdminProductGridSection.productGridCell('1', 'Quantity')}}" userInput="99" stepKey="seeProductSkuInGrid"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductCategoryViewChildOnlyTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductCategoryViewChildOnlyTest.xml index 805727e29a17a..9d7807c543def 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductCategoryViewChildOnlyTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductCategoryViewChildOnlyTest.xml @@ -102,10 +102,10 @@ <!-- Go to the product page for the first product --> <amOnPage stepKey="goToProductGrid" url="{{ProductCatalogPage.url}}"/> <waitForPageLoad stepKey="waitForProductGridLoad"/> - <actionGroup stepKey="searchForSimpleProduct" ref="filterProductGridBySku2"> + <actionGroup stepKey="searchForSimpleProduct" ref="FilterProductGridBySku2ActionGroup"> <argument name="sku" value="$$createConfigChildProduct1.sku$$"/> </actionGroup> - <actionGroup stepKey="openProductEditPage" ref="openProducForEditByClickingRowXColumnYInProductGrid"/> + <actionGroup stepKey="openProductEditPage" ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup"/> <!-- Edit the visibility the first simple product --> <selectOption selector="{{AdminProductFormSection.visibility}}" userInput="Catalog, Search" stepKey="selectVisibilityCatalogSearch"/> <!--Add to category--> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml index 16400fa837b1c..294ab9fd0664d 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml @@ -119,10 +119,10 @@ <!-- Go to the product page for the first product --> <amOnPage stepKey="goToProductGrid" url="{{ProductCatalogPage.url}}"/> <waitForPageLoad stepKey="waitForProductGridLoad"/> - <actionGroup stepKey="searchForSimpleProduct" ref="filterProductGridBySku2"> + <actionGroup stepKey="searchForSimpleProduct" ref="FilterProductGridBySku2ActionGroup"> <argument name="sku" value="$$createConfigChildProduct1.sku$$"/> </actionGroup> - <actionGroup stepKey="openProductEditPage" ref="openProducForEditByClickingRowXColumnYInProductGrid"/> + <actionGroup stepKey="openProductEditPage" ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup"/> <!-- Edit the attribute for the first simple product --> <selectOption stepKey="editSelectAttribute" selector="{{ModifyAttributes.nthExistingAttribute($$createConfigProductAttributeSelect.default_frontend_label$$)}}" userInput="$$createConfigProductAttributeSelectOption1.option[store_labels][0][label]$$"/> @@ -156,4 +156,4 @@ <submitForm selector="#search_mini_form" parameterArray="['q' => $$createConfigChildProduct1.custom_attributes[short_description]$$]" stepKey="searchStorefront3" /> <seeElement stepKey="seeProduct3" selector="{{StorefrontCategoryProductSection.ProductTitleByName('$$createConfigProduct.name$$')}}"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml index f75e30907a1f4..42b9dfc92760a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml @@ -24,13 +24,17 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!-- Create a configurable product via the UI --> - <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> <argument name="product" value="_defaultProduct"/> <argument name="category" value="$$createCategory$$"/> </actionGroup> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> <actionGroup ref="logout" stepKey="adminLogout"/> </after> @@ -65,13 +69,17 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!-- Create a configurable product via the UI --> - <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> <argument name="product" value="_defaultProduct"/> <argument name="category" value="$$createCategory$$"/> </actionGroup> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> <actionGroup ref="logout" stepKey="adminLogout"/> </after> @@ -106,13 +114,17 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!-- Create a configurable product via the UI --> - <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> <argument name="product" value="_defaultProduct"/> <argument name="category" value="$$createCategory$$"/> </actionGroup> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> <actionGroup ref="logout" stepKey="adminLogout"/> </after> @@ -144,13 +156,17 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!-- Create a configurable product via the UI --> - <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> <argument name="product" value="_defaultProduct"/> <argument name="category" value="$$createCategory$$"/> </actionGroup> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> <actionGroup ref="logout" stepKey="adminLogout"/> </after> @@ -162,4 +178,108 @@ <click selector="{{StorefrontProductInfoMainSection.AddToCart}}" stepKey="clickAddToCart" /> <see userInput="This is a required field" selector="{{StorefrontProductInfoMainSection.productAttributeOptionsError}}" stepKey="seeError"/> </test> + + <test name="StorefrontConfigurableProductVariationsTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="Configurable Product"/> + <title value="Customer should get the right options list"/> + <description value="Customer should get the right options list for each variation of configurable product"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-23027"/> + <useCaseId value="MC-22732"/> + <group value="configurable_product"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <!-- Add first attribute with options --> + <createData entity="productAttributeWithTwoOptions" stepKey="createFirstAttribute"/> + <createData entity="productAttributeOption1" stepKey="createFirstAttributeFirstOption"> + <requiredEntity createDataKey="createFirstAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createFirstAttributeSecondOption"> + <requiredEntity createDataKey="createFirstAttribute"/> + </createData> + <!-- Add second attribute with options --> + <createData entity="productAttributeWithTwoOptions" stepKey="createSecondAttribute"/> + <createData entity="productAttributeOption1" stepKey="createSecondAttributeFirstOption"> + <requiredEntity createDataKey="createSecondAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createSecondAttributeSecondOption"> + <requiredEntity createDataKey="createSecondAttribute"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> + <argument name="sku" value="{{BaseConfigurableProduct.sku}}"/> + </actionGroup> + <deleteData createDataKey="createFirstAttribute" stepKey="deleteFirstAttribute"/> + <deleteData createDataKey="createSecondAttribute" stepKey="deleteSecondAttribute"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="logout" stepKey="adminLogout"/> + </after> + + <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPage"/> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="navigateToCreateProductPage"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <actionGroup ref="SetCategoryByNameActionGroup" stepKey="addCategoryToProduct"> + <argument name="categoryName" value="$createCategory.name$"/> + </actionGroup> + <actionGroup ref="SetProductUrlKeyByStringActionGroup" stepKey="fillUrlKey"> + <argument name="urlKey" value="{{BaseConfigurableProduct.urlKey}}"/> + </actionGroup> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickOnCreateConfigurations"/> + <actionGroup ref="AdminSelectAttributeInConfigurableAttributesGrid" stepKey="checkFirstAttribute"> + <argument name="attributeCode" value="$createFirstAttribute.attribute_code$"/> + </actionGroup> + <actionGroup ref="AdminSelectAttributeInConfigurableAttributesGrid" stepKey="checkSecondAttribute"> + <argument name="attributeCode" value="$createSecondAttribute.attribute_code$"/> + </actionGroup> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton"/> + <waitForPageLoad stepKey="waitForStepLoad"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute($createFirstAttribute.default_frontend_label$)}}" stepKey="clickFirstAttributeSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute($createSecondAttribute.default_frontend_label$)}}" stepKey="clickSecondAttributeSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickSecondNextStep"/> + <waitForElement selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="waitThirdNextButton"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickThirdStep"/> + <waitForElement selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="waitGenerateConfigurationsButton"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickToGenerateConfigurations"/> + + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.variationsGrid}}" stepKey="waitForVariationsGrid"/> + <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setFirstVariationQuantity"> + <argument name="rowIndex" value="0"/> + <argument name="quantity" value="0"/> + </actionGroup> + <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setSecondVariationQuantity"> + <argument name="rowIndex" value="1"/> + <argument name="quantity" value="1"/> + </actionGroup> + <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setThirdVariationQuantity"> + <argument name="rowIndex" value="2"/> + <argument name="quantity" value="1"/> + </actionGroup> + <actionGroup ref="AdminChangeConfigurableProductVariationQty" stepKey="setFourthVariationQuantity"> + <argument name="rowIndex" value="3"/> + <argument name="quantity" value="1"/> + </actionGroup> + <actionGroup ref="SaveConfigurableProductActionGroup" stepKey="saveConfigurableProduct"> + <argument name="product" value="BaseConfigurableProduct"/> + </actionGroup> + <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToAdminProductSEOSection"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSectionHeader"/> + <grabValueFrom selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="grabUrlKey"/> + <amOnPage url="{$grabUrlKey}.html" stepKey="amOnConfigurableProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.productOptionSelect($createFirstAttribute.default_frontend_label$)}}" stepKey="waitForFirstSelect"/> + <selectOption userInput="$createFirstAttributeFirstOption.option[store_labels][0][label]$" selector="{{StorefrontProductInfoMainSection.productOptionSelect($createFirstAttribute.default_frontend_label$)}}" stepKey="selectFirstAttributeFirstOption"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.productOptionSelect($createSecondAttribute.default_frontend_label$)}}" stepKey="waitForSecondSelect"/> + <selectOption userInput="$createSecondAttributeSecondOption.option[store_labels][0][label]$" selector="{{StorefrontProductInfoMainSection.productOptionSelect($createSecondAttribute.default_frontend_label$)}}" stepKey="selectSecondAttributeSecondOption"/> + </test> </tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest.xml index 0ade410714a25..bfdadd54b872c 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest.xml @@ -22,16 +22,21 @@ <before> <createData entity="ApiCategory" stepKey="createCategory"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <!-- Create a configurable product via the UI --> - <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> <argument name="product" value="_defaultProduct"/> <argument name="category" value="$$createCategory$$"/> </actionGroup> + <!-- TODO: REMOVE AFTER FIX MC-21717 --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush eav" stepKey="flushCache"/> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup stepKey="deleteProduct" ref="DeleteProductBySkuActionGroup"> + <argument name="sku" value="{{_defaultProduct.sku}}"/> + </actionGroup> <actionGroup ref="logout" stepKey="adminLogout"/> </after> @@ -60,7 +65,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> <!-- Create a configurable product via the UI --> - <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> <argument name="product" value="_defaultProduct"/> <argument name="category" value="$$createCategory$$"/> </actionGroup> @@ -98,7 +103,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> <!-- Create a configurable product via the UI --> - <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> <argument name="product" value="_defaultProduct"/> <argument name="category" value="$$createCategory$$"/> </actionGroup> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml index 4c955f3385643..e24c8e4d94916 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductWithFileCustomOptionTest.xml @@ -31,14 +31,14 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!-- Create a configurable product via the UI --> - <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> <argument name="product" value="BaseConfigurableProduct"/> <argument name="category" value="$$createCategory$$"/> </actionGroup> <!--Add custom option to configurable product--> - <actionGroup ref="AddProductCustomOptionFile" stepKey="addCustomOptionToProduct"/> + <actionGroup ref="AddProductCustomOptionFileActionGroup" stepKey="addCustomOptionToProduct"/> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> - + <!--Go to storefront--> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> <waitForPageLoad stepKey="waitForHomePageLoad"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml index bc8f3e49272b7..7828478bc963e 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml @@ -89,10 +89,10 @@ </createData> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!--SKU Product Attribute is enabled for Promo Rule Conditions--> - <actionGroup ref="navigateToEditProductAttribute" stepKey="navigateToSkuProductAttribute"> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="navigateToSkuProductAttribute"> <argument name="ProductAttribute" value="sku"/> </actionGroup> - <actionGroup ref="changeUseForPromoRuleConditionsProductAttribute" stepKey="changeUseForPromoRuleConditionsProductAttributeToYes"> + <actionGroup ref="ChangeUseForPromoRuleConditionsProductAttributeActionGroup" stepKey="changeUseForPromoRuleConditionsProductAttributeToYes"> <argument name="option" value="Yes"/> </actionGroup> <magentoCLI command="indexer:reindex" stepKey="reindex1"/> @@ -110,17 +110,17 @@ <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <!--SKU Product Attribute is disable for Promo Rule Conditions--> - <actionGroup ref="navigateToEditProductAttribute" stepKey="navigateToSkuProductAttribute"> + <actionGroup ref="NavigateToEditProductAttributeActionGroup" stepKey="navigateToSkuProductAttribute"> <argument name="ProductAttribute" value="sku"/> </actionGroup> - <actionGroup ref="changeUseForPromoRuleConditionsProductAttribute" stepKey="changeUseForPromoRuleConditionsProductAttributeToNo"> + <actionGroup ref="ChangeUseForPromoRuleConditionsProductAttributeActionGroup" stepKey="changeUseForPromoRuleConditionsProductAttributeToNo"> <argument name="option" value="No"/> </actionGroup> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> <!--Open category with products and Sort by price desc--> - <actionGroup ref="GoToStorefrontCategoryPageByParameters" stepKey="goToStorefrontCategoryPage"> + <actionGroup ref="GoToStorefrontCategoryPageByParametersActionGroup" stepKey="goToStorefrontCategoryPage"> <argument name="category" value="$$createCategory.custom_attributes[url_key]$$"/> <argument name="mode" value="grid"/> <argument name="numOfProductsPerPage" value="25"/> @@ -132,18 +132,18 @@ <see selector="{{StorefrontCategoryMainSection.lineProductName('3')}}" userInput="$$createSimpleProduct.name$$" stepKey="seeSimpleProduct"/> <!--Create and apply catalog price rule--> - <actionGroup ref="newCatalogPriceRuleByUIWithConditionIsSKU" stepKey="createCatalogPriceRule"> + <actionGroup ref="NewCatalogPriceRuleByUIWithConditionIsSKUActionGroup" stepKey="createCatalogPriceRule"> <argument name="catalogRule" value="CatalogRuleByPercentWith96Amount" /> <argument name="productSku" value="$$createConfigChildProduct3.sku$$" /> </actionGroup> - <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="clickSaveAndApplyRules"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> <!--Reopen category with products and Sort by price desc--> - <actionGroup ref="GoToStorefrontCategoryPageByParameters" stepKey="goToStorefrontCategoryPage2"> + <actionGroup ref="GoToStorefrontCategoryPageByParametersActionGroup" stepKey="goToStorefrontCategoryPage2"> <argument name="category" value="$$createCategory.custom_attributes[url_key]$$"/> <argument name="mode" value="grid"/> <argument name="numOfProductsPerPage" value="9"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml index 182c8c069ab23..3ebd9d6ef5367 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml @@ -122,7 +122,7 @@ <!-- Open Product Index Page and Filter First Child product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProduct"> <argument name="product" value="ApiSimpleOne"/> </actionGroup> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml index 749d1bee0661a..2cdad86aa0b2c 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVisibilityOfDuplicateProductTest.xml @@ -30,17 +30,17 @@ <after> <!--Delete created data--> <comment userInput="Delete created data" stepKey="commentDeleteCreatedData"/> - <actionGroup ref="deleteAllDuplicateProductUsingProductGrid" stepKey="deleteDuplicatedProduct"> + <actionGroup ref="DeleteAllDuplicateProductUsingProductGridActionGroup" stepKey="deleteDuplicatedProduct"> <argument name="product" value="$$createConfigProduct$$"/> </actionGroup> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetGridToDefaultKeywordSearch"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> <!--Delete product attributes--> <comment userInput="Delete product attributes" stepKey="deleteCommentAttributes"/> - <actionGroup ref="deleteProductAttributeByLabel" stepKey="deleteProductAttribute"> + <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"> <argument name="ProductAttribute" value="colorProductAttribute"/> </actionGroup> <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGridFirst"/> - <actionGroup ref="deleteProductAttributeByLabel" stepKey="deleteProductSecondAttribute"> + <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteProductSecondAttribute"> <argument name="ProductAttribute" value="productAttributeColor"/> </actionGroup> <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGridSecond"/> @@ -51,11 +51,11 @@ <comment userInput="Create attribute and options for product" stepKey="commentCreateAttributesAndOptions"/> <amOnPage url="{{AdminProductEditPage.url($$createConfigProduct.id$$)}}" stepKey="navigateToConfigProductPage"/> <waitForPageLoad stepKey="waitForProductPageLoad"/> - <actionGroup ref="addProductImage" stepKey="addImageForProduct1"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImageForProduct1"> <argument name="image" value="MagentoLogo"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProductForm"/> - <actionGroup ref="AdminCreateAttributeFromProductPageWithScope" stepKey="createAttributeForProduct"> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> + <actionGroup ref="AdminCreateAttributeFromProductPageWithScopeActionGroup" stepKey="createAttributeForProduct"> <argument name="attributeName" value="{{colorProductAttribute.default_label}}"/> <argument name="attributeType" value="{{colorProductAttribute.input_type}}"/> <argument name="scope" value="Global"/> @@ -64,7 +64,7 @@ <waitForPageLoad stepKey="waitForProductPageReload"/> <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickOnCreateConfigurations"/> <waitForPageLoad stepKey="waitForFilters"/> - <actionGroup ref="createOptionsForAttribute" stepKey="createOptions"> + <actionGroup ref="CreateOptionsForAttributeActionGroup" stepKey="createOptions"> <argument name="attributeName" value="{{colorProductAttribute.default_label}}"/> <argument name="firstOptionName" value="{{colorConfigurableProductAttribute1.name}}"/> <argument name="secondOptionName" value="{{colorConfigurableProductAttribute2.name}}"/> @@ -73,36 +73,36 @@ <waitForPageLoad stepKey="waitForBulkImagesPriceQuantityPageLoad"/> <!--Add images to configurable product attribute options--> <comment userInput="Add images to configurable product attribute options" stepKey="commentAddImages"/> - <actionGroup ref="addUniqueImageToConfigurableProductOption" stepKey="addImageToConfigurableProductOptionOne"> + <actionGroup ref="AddUniqueImageToConfigurableProductOptionActionGroup" stepKey="addImageToConfigurableProductOptionOne"> <argument name="image" value="ImageUpload"/> <argument name="frontend_label" value="{{colorProductAttribute.default_label}}"/> <argument name="label" value="{{colorConfigurableProductAttribute1.name}}"/> </actionGroup> - <actionGroup ref="addUniqueImageToConfigurableProductOption" stepKey="addImageToConfigurableProductOptionTwo"> + <actionGroup ref="AddUniqueImageToConfigurableProductOptionActionGroup" stepKey="addImageToConfigurableProductOptionTwo"> <argument name="image" value="ImageUpload_1"/> <argument name="frontend_label" value="{{colorProductAttribute.default_label}}"/> <argument name="label" value="{{colorConfigurableProductAttribute2.name}}"/> </actionGroup> <!--Add price to product attribute options--> <comment userInput="Add price to product attribute options" stepKey="commentAddPrice"/> - <actionGroup ref="addUniquePriceToConfigurableProductOption" stepKey="addPriceToConfigurableProductOptionFirst"> + <actionGroup ref="AddUniquePriceToConfigurableProductOptionActionGroup" stepKey="addPriceToConfigurableProductOptionFirst"> <argument name="frontend_label" value="{{colorProductAttribute.default_label}}"/> <argument name="label" value="{{colorConfigurableProductAttribute1.name}}"/> <argument name="price" value="{{virtualProductWithRequiredFields.price}}"/> </actionGroup> - <actionGroup ref="addUniquePriceToConfigurableProductOption" stepKey="addPriceToConfigurableProductOptionSecond"> + <actionGroup ref="AddUniquePriceToConfigurableProductOptionActionGroup" stepKey="addPriceToConfigurableProductOptionSecond"> <argument name="frontend_label" value="{{colorProductAttribute.default_label}}"/> <argument name="label" value="{{colorConfigurableProductAttribute2.name}}"/> <argument name="price" value="{{virtualProductWithRequiredFields.price}}"/> </actionGroup> <!--Add quantity to product attribute options--> <comment userInput="Add quantity to product attribute options" stepKey="commentAddQuantity"/> - <actionGroup ref="addUniqueQuantityToConfigurableProductOption" stepKey="addUniqueQtyForFirstOption"> + <actionGroup ref="AddUniqueQuantityToConfigurableProductOptionActionGroup" stepKey="addUniqueQtyForFirstOption"> <argument name="frontend_label" value="{{colorProductAttribute.default_label}}"/> <argument name="label" value="{{colorConfigurableProductAttribute1.name}}"/> <argument name="quantity" value="{{virtualProductBigQty.quantity}}"/> </actionGroup> - <actionGroup ref="addUniqueQuantityToConfigurableProductOption" stepKey="addUniqueQtyForSecondOption"> + <actionGroup ref="AddUniqueQuantityToConfigurableProductOptionActionGroup" stepKey="addUniqueQtyForSecondOption"> <argument name="frontend_label" value="{{colorProductAttribute.default_label}}"/> <argument name="label" value="{{colorConfigurableProductAttribute2.name}}"/> <argument name="quantity" value="{{virtualProductBigQty.quantity}}"/> @@ -111,7 +111,7 @@ <waitForPageLoad stepKey="waitForSummaryPageLoad"/> <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButtonToGenerateConfigs"/> <waitForPageLoad stepKey="waitForConfigurableProductPageLoad"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!--Duplicate the product--> <comment userInput="Duplicate the product" stepKey="commentDuplicateProduct"/> <actionGroup ref="AdminFormSaveAndDuplicate" stepKey="saveAndDuplicateProductForm"/> @@ -120,8 +120,8 @@ <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="1" stepKey="selectInStock"/> <!--Change product image--> <comment userInput="Change product image" stepKey="commentChangeProductImage"/> - <actionGroup ref="removeProductImage" stepKey="removeProductImage"/> - <actionGroup ref="addProductImage" stepKey="addImageForProduct"> + <actionGroup ref="RemoveProductImageActionGroup" stepKey="removeProductImage"/> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImageForProduct"> <argument name="image" value="TestImageAdobe"/> </actionGroup> <!--Disable configurations--> @@ -132,10 +132,10 @@ <actionGroup ref="AdminConfigurableProductDisableConfigurationsActionGroup" stepKey="disableSecondConfig"> <argument name="productName" value="{{colorConfigurableProductAttribute2.name}}"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveDuplicatedProductForm"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveDuplicatedProductForm"/> <!--Create new configurations with another attribute--> <comment userInput="Create new configurations with another attribute" stepKey="commentCreateNewConfigurations"/> - <actionGroup ref="AdminCreateAttributeFromProductPageWithScope" stepKey="createAttributeForDuplicatedProduct"> + <actionGroup ref="AdminCreateAttributeFromProductPageWithScopeActionGroup" stepKey="createAttributeForDuplicatedProduct"> <argument name="attributeName" value="{{productAttributeColor.default_label}}"/> <argument name="attributeType" value="{{productAttributeColor.input_type}}"/> <argument name="scope" value="Global"/> @@ -146,39 +146,39 @@ <waitForElementVisible selector="{{AdminGridSelectRows.multicheckDropdown}}" stepKey="waitForCreateConfigurationsPageLoad"/> <click selector="{{AdminGridSelectRows.multicheckDropdown}}" stepKey="openMulticheckDropdown"/> <click selector="{{AdminGridSelectRows.multicheckOption('Deselect All')}}" stepKey="DeselectAllAttributes"/> - <actionGroup ref="createOptionsForAttribute" stepKey="createOptionsForDuplicatedProduct"> + <actionGroup ref="CreateOptionsForAttributeActionGroup" stepKey="createOptionsForDuplicatedProduct"> <argument name="attributeName" value="{{productAttributeColor.default_label}}"/> <argument name="firstOptionName" value="{{colorConfigurableProductAttribute1.name}}"/> <argument name="secondOptionName" value="{{colorConfigurableProductAttribute2.name}}"/> </actionGroup> <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnFirstNextButtonForDuplicatedProduct"/> <waitForPageLoad stepKey="waitForBulkImagesPriceQuantityPageLoadForDuplicatedProduct"/> - <actionGroup ref="addUniqueImageToConfigurableProductOption" stepKey="addImgConfigProductOption1DuplicatedProduct"> + <actionGroup ref="AddUniqueImageToConfigurableProductOptionActionGroup" stepKey="addImgConfigProductOption1DuplicatedProduct"> <argument name="image" value="MagentoLogo"/> <argument name="frontend_label" value="{{productAttributeColor.default_label}}"/> <argument name="label" value="{{colorConfigurableProductAttribute1.name}}"/> </actionGroup> - <actionGroup ref="addUniqueImageToConfigurableProductOption" stepKey="addImgConfigProductOption2DuplicatedProduct"> + <actionGroup ref="AddUniqueImageToConfigurableProductOptionActionGroup" stepKey="addImgConfigProductOption2DuplicatedProduct"> <argument name="image" value="MagentoLogo"/> <argument name="frontend_label" value="{{productAttributeColor.default_label}}"/> <argument name="label" value="{{colorConfigurableProductAttribute2.name}}"/> </actionGroup> - <actionGroup ref="addUniquePriceToConfigurableProductOption" stepKey="addPriceConfigProductOption1DuplicatedProduct"> + <actionGroup ref="AddUniquePriceToConfigurableProductOptionActionGroup" stepKey="addPriceConfigProductOption1DuplicatedProduct"> <argument name="frontend_label" value="{{productAttributeColor.default_label}}"/> <argument name="label" value="{{colorConfigurableProductAttribute1.name}}"/> <argument name="price" value="{{virtualProductWithRequiredFields.price}}"/> </actionGroup> - <actionGroup ref="addUniquePriceToConfigurableProductOption" stepKey="addPriceConfigProductOption2DuplicatedProduct"> + <actionGroup ref="AddUniquePriceToConfigurableProductOptionActionGroup" stepKey="addPriceConfigProductOption2DuplicatedProduct"> <argument name="frontend_label" value="{{productAttributeColor.default_label}}"/> <argument name="label" value="{{colorConfigurableProductAttribute2.name}}"/> <argument name="price" value="{{virtualProductWithRequiredFields.price}}"/> </actionGroup> - <actionGroup ref="addUniqueQuantityToConfigurableProductOption" stepKey="addUniqueQtyOption1DuplicatedProduct"> + <actionGroup ref="AddUniqueQuantityToConfigurableProductOptionActionGroup" stepKey="addUniqueQtyOption1DuplicatedProduct"> <argument name="frontend_label" value="{{productAttributeColor.default_label}}"/> <argument name="label" value="{{colorConfigurableProductAttribute1.name}}"/> <argument name="quantity" value="{{virtualProductBigQty.quantity}}"/> </actionGroup> - <actionGroup ref="addUniqueQuantityToConfigurableProductOption" stepKey="addUniqueQtyOption2DuplicatedProduct"> + <actionGroup ref="AddUniqueQuantityToConfigurableProductOptionActionGroup" stepKey="addUniqueQtyOption2DuplicatedProduct"> <argument name="frontend_label" value="{{productAttributeColor.default_label}}"/> <argument name="label" value="{{colorConfigurableProductAttribute2.name}}"/> <argument name="quantity" value="{{virtualProductBigQty.quantity}}"/> @@ -187,7 +187,7 @@ <waitForPageLoad stepKey="waitForSummaryPageLoadForDuplicatedProduct"/> <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="generateConfigsForDuplicatedProduct"/> <waitForPageLoad stepKey="waitForDuplicatedProductPageLoad"/> - <actionGroup ref="saveProductForm" stepKey="saveDuplicatedProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveDuplicatedProduct"/> <magentoCLI command="indexer:reindex" stepKey="reindex"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> <!--Assert configurable product in category--> diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php index c5c2368720b98..36fda4ef3245c 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php @@ -233,9 +233,7 @@ public function cacheKeyProvider(): array public function testGetCacheKeyInfo(array $expected, string $priceCurrency = null, string $customerGroupId = null) { $storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->setMethods([ - 'getCurrentCurrency', - ]) + ->setMethods(['getCurrentCurrency']) ->getMockForAbstractClass(); $storeMock->expects($this->any()) ->method('getCode') @@ -270,9 +268,7 @@ public function testGetJsonConfig() $amountMock = $this->getAmountMock($amount); $priceMock = $this->getMockBuilder(\Magento\Framework\Pricing\Price\PriceInterface::class) - ->setMethods([ - 'getAmount', - ]) + ->setMethods(['getAmount']) ->getMockForAbstractClass(); $priceMock->expects($this->any())->method('getAmount')->willReturn($amountMock); $tierPriceMock = $this->getTierPriceMock($amountMock, $priceQty, $percentage); @@ -287,22 +283,25 @@ public function testGetJsonConfig() ->getMock(); $priceInfoMock->expects($this->any()) ->method('getPrice') - ->willReturnMap([ - ['regular_price', $priceMock], - ['final_price', $priceMock], - ['tier_price', $tierPriceMock], - ]); + ->willReturnMap( + [ + ['regular_price', $priceMock], + ['final_price', $priceMock], + ['tier_price', $tierPriceMock], + ] + ); $productMock->expects($this->any())->method('getTypeInstance')->willReturn($productTypeMock); $productMock->expects($this->any())->method('getPriceInfo')->willReturn($priceInfoMock); $productMock->expects($this->any())->method('isSaleable')->willReturn(true); $productMock->expects($this->any())->method('getId')->willReturn($productId); + $productMock->expects($this->any())->method('getStatus') + ->willReturn(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED); $this->helper->expects($this->any()) ->method('getOptions') ->with($productMock, [$productMock]) ->willReturn([]); - $this->product->expects($this->any())->method('getSkipSaleableCheck')->willReturn(true); $attributesData = [ 'attributes' => [], @@ -421,9 +420,7 @@ private function getProductTypeMock(\PHPUnit_Framework_MockObject_MockObject $pr ->willReturn('%s'); $storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->setMethods([ - 'getCurrentCurrency', - ]) + ->setMethods(['getCurrentCurrency']) ->getMockForAbstractClass(); $storeMock->expects($this->any()) ->method('getCurrentCurrency') @@ -475,10 +472,7 @@ protected function mockContextObject() protected function getAmountMock($amount): \PHPUnit_Framework_MockObject_MockObject { $amountMock = $this->getMockBuilder(\Magento\Framework\Pricing\Amount\AmountInterface::class) - ->setMethods([ - 'getValue', - 'getBaseAmount', - ]) + ->setMethods(['getValue', 'getBaseAmount']) ->getMockForAbstractClass(); $amountMock->expects($this->any()) ->method('getValue') @@ -506,10 +500,7 @@ protected function getTierPriceMock(\PHPUnit_Framework_MockObject_MockObject $am ]; $tierPriceMock = $this->getMockBuilder(\Magento\Catalog\Pricing\Price\TierPriceInterface::class) - ->setMethods([ - 'getTierPriceList', - 'getSavePercent', - ]) + ->setMethods(['getTierPriceList', 'getSavePercent']) ->getMockForAbstractClass(); $tierPriceMock->expects($this->any()) ->method('getTierPriceList') diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/SalesRule/Model/Rule/Condition/ProductTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/SalesRule/Model/Rule/Condition/ProductTest.php index 80979148c4959..bebbb04405c4b 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/SalesRule/Model/Rule/Condition/ProductTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/SalesRule/Model/Rule/Condition/ProductTest.php @@ -176,14 +176,16 @@ private function createProductMock(): \PHPUnit_Framework_MockObject_MockObject { $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) ->disableOriginalConstructor() - ->setMethods([ - 'getAttribute', - 'getId', - 'setQuoteItemQty', - 'setQuoteItemPrice', - 'getTypeId', - 'hasData', - ]) + ->setMethods( + [ + 'getAttribute', + 'getId', + 'setQuoteItemQty', + 'setQuoteItemPrice', + 'getTypeId', + 'hasData', + ] + ) ->getMock(); $productMock ->expects($this->any()) @@ -223,4 +225,38 @@ public function testChildIsNotUsedForValidation() $this->validatorPlugin->beforeValidate($this->validator, $item); } + + /** + * Test for Configurable product in invalid state with no children does not raise error + */ + public function testChildIsNotUsedForValidationWhenConfigurableProductIsMissingChildren() + { + $configurableProductMock = $this->createProductMock(); + $configurableProductMock + ->expects($this->any()) + ->method('getTypeId') + ->willReturn(Configurable::TYPE_CODE); + + $configurableProductMock + ->expects($this->any()) + ->method('hasData') + ->with($this->equalTo('special_price')) + ->willReturn(false); + + /* @var AbstractItem|\PHPUnit_Framework_MockObject_MockObject $item */ + $item = $this->getMockBuilder(AbstractItem::class) + ->disableOriginalConstructor() + ->setMethods(['setProduct', 'getProduct', 'getChildren']) + ->getMockForAbstractClass(); + $item->expects($this->any()) + ->method('getProduct') + ->willReturn($configurableProductMock); + $item->expects($this->any()) + ->method('getChildren') + ->willReturn([]); + + $this->validator->setAttribute('special_price'); + + $this->validatorPlugin->beforeValidate($this->validator, $item); + } } diff --git a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/ConfigurableQty.php b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/ConfigurableQty.php index 055891ff79c69..ade56edeb3dfc 100644 --- a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/ConfigurableQty.php +++ b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Product/Form/Modifier/ConfigurableQty.php @@ -8,7 +8,6 @@ namespace Magento\ConfigurableProduct\Ui\DataProvider\Product\Form\Modifier; use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier; -use Magento\Catalog\Model\Locator\LocatorInterface; /** * Data provider for quantity in the Configurable products @@ -18,21 +17,6 @@ class ConfigurableQty extends AbstractModifier const CODE_QUANTITY = 'qty'; const CODE_QTY_CONTAINER = 'quantity_and_stock_status_qty'; - /** - * @var LocatorInterface - */ - private $locator; - - /** - * ConfigurableQty constructor - * - * @param LocatorInterface $locator - */ - public function __construct(LocatorInterface $locator) - { - $this->locator = $locator; - } - /** * @inheritdoc */ @@ -48,8 +32,7 @@ public function modifyMeta(array $meta) { if ($groupCode = $this->getGroupCodeByField($meta, self::CODE_QTY_CONTAINER)) { $parentChildren = &$meta[$groupCode]['children']; - $isConfigurable = $this->locator->getProduct()->getTypeId() === 'configurable'; - if (!empty($parentChildren[self::CODE_QTY_CONTAINER]) && $isConfigurable) { + if (!empty($parentChildren[self::CODE_QTY_CONTAINER])) { $parentChildren[self::CODE_QTY_CONTAINER] = array_replace_recursive( $parentChildren[self::CODE_QTY_CONTAINER], [ diff --git a/app/code/Magento/ConfigurableProduct/registration.php b/app/code/Magento/ConfigurableProduct/registration.php index fdb3fba0c9912..80572b8609ec6 100644 --- a/app/code/Magento/ConfigurableProduct/registration.php +++ b/app/code/Magento/ConfigurableProduct/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_ConfigurableProduct', __DIR__); diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml index 1166adca97255..844422b2a2d7a 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml @@ -17,7 +17,7 @@ </legend> <div class="product-options fieldset admin__fieldset"> <?php foreach ($_attributes as $_attribute) : ?> - <div class="field admin__field _required required"> + <div class="field admin__field required"> <label class="label admin__field-label"><?= $block->escapeHtml($_attribute->getProductAttribute()->getStoreLabel($_product->getStoreId())); ?></label> diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/attributes_values.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/attributes_values.js index 6c790c634ee93..16dbf9ec23cd6 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/attributes_values.js +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/attributes_values.js @@ -106,6 +106,8 @@ define([ errorOption, allOptions = []; + newOption.label = $.trim(newOption.label); + if (_.isEmpty(newOption.label)) { return false; } diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/bulk.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/bulk.js index 00bf1feff7fb5..eed887037bc96 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/bulk.js +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/steps/bulk.js @@ -237,12 +237,21 @@ define([ getImageProperty: function (node) { var types = node.find('[data-role=gallery]').productGallery('option').types, images = _.map(node.find('[data-role=image]'), function (image) { - var imageData = $(image).data('imageData'); + var imageData = $(image).data('imageData'), + positionElement; imageData.galleryTypes = _.pluck(_.filter(types, function (type) { return type.value === imageData.file; }), 'code'); + //jscs:disable requireCamelCaseOrUpperCaseIdentifiers + positionElement = + $(image).find('[name="product[media_gallery][images][' + imageData.file_id + '][position]"]'); + //jscs:enable requireCamelCaseOrUpperCaseIdentifiers + if (!_.isEmpty(positionElement.val())) { + imageData.position = positionElement.val(); + } + return imageData; }); diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js index ae564610e4b0b..faacbd03f20b0 100644 --- a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js +++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js @@ -443,6 +443,10 @@ define([ } for (i = 0; i < options.length; i++) { + if (prevConfig && typeof allowedProductsByOption[i] === 'undefined') { + continue; //jscs:ignore disallowKeywords + } + allowedProducts = prevConfig ? allowedProductsByOption[i] : options[i].products.slice(0); optionPriceDiff = 0; diff --git a/app/code/Magento/ConfigurableProductGraphQl/Model/Resolver/ConfigurableVariant.php b/app/code/Magento/ConfigurableProductGraphQl/Model/Resolver/ConfigurableVariant.php index 3e07fecb2ebe7..f28bf97adf930 100644 --- a/app/code/Magento/ConfigurableProductGraphQl/Model/Resolver/ConfigurableVariant.php +++ b/app/code/Magento/ConfigurableProductGraphQl/Model/Resolver/ConfigurableVariant.php @@ -18,6 +18,7 @@ use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\CatalogGraphQl\Model\Resolver\Products\Query\FieldSelection; /** * @inheritdoc @@ -49,25 +50,33 @@ class ConfigurableVariant implements ResolverInterface */ private $metadataPool; + /** + * @var FieldSelection + */ + private $fieldSelection; + /** * @param Collection $variantCollection * @param OptionCollection $optionCollection * @param ValueFactory $valueFactory * @param AttributeCollection $attributeCollection * @param MetadataPool $metadataPool + * @param FieldSelection $fieldSelection */ public function __construct( Collection $variantCollection, OptionCollection $optionCollection, ValueFactory $valueFactory, AttributeCollection $attributeCollection, - MetadataPool $metadataPool + MetadataPool $metadataPool, + FieldSelection $fieldSelection ) { $this->variantCollection = $variantCollection; $this->optionCollection = $optionCollection; $this->valueFactory = $valueFactory; $this->attributeCollection = $attributeCollection; $this->metadataPool = $metadataPool; + $this->fieldSelection = $fieldSelection; } /** @@ -84,9 +93,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value } $this->variantCollection->addParentProduct($value['model']); - $fields = $this->getProductFields($info); - $matchedFields = $this->attributeCollection->getRequestAttributes($fields); - $this->variantCollection->addEavAttributes($matchedFields); + $fields = $this->fieldSelection->getProductsFieldSelection($info); + $this->variantCollection->addEavAttributes($fields); $this->optionCollection->addProductId((int)$value[$linkField]); $result = function () use ($value, $linkField) { @@ -103,26 +111,4 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value return $this->valueFactory->create($result); } - - /** - * Return field names for all requested product fields. - * - * @param ResolveInfo $info - * @return string[] - */ - private function getProductFields(ResolveInfo $info) - { - $fieldNames = []; - foreach ($info->fieldNodes as $node) { - if ($node->name->value !== 'product') { - continue; - } - - foreach ($node->selectionSet->selections as $selectionNode) { - $fieldNames[] = $selectionNode->name->value; - } - } - - return $fieldNames; - } } diff --git a/app/code/Magento/ConfigurableProductGraphQl/Model/Resolver/Variant/Attributes.php b/app/code/Magento/ConfigurableProductGraphQl/Model/Resolver/Variant/Attributes.php index dd2b84e1da539..faf666144422c 100644 --- a/app/code/Magento/ConfigurableProductGraphQl/Model/Resolver/Variant/Attributes.php +++ b/app/code/Magento/ConfigurableProductGraphQl/Model/Resolver/Variant/Attributes.php @@ -44,7 +44,10 @@ public function resolve( } $data = []; - foreach ($value['options'] as $option) { + foreach ($value['options'] as $optionId => $option) { + if (!isset($option['attribute_code'])) { + continue; + } $code = $option['attribute_code']; /** @var Product|null $model */ $model = $value['product']['model'] ?? null; @@ -52,18 +55,54 @@ public function resolve( continue; } - foreach ($option['values'] as $optionValue) { - if ($optionValue['value_index'] != $model->getData($code)) { - continue; + if (isset($option['options_map'])) { + $optionsFromMap = $this->getOptionsFromMap( + $option['options_map'] ?? [], + $code, + (int) $optionId, + (int) $model->getData($code) + ); + if (!empty($optionsFromMap)) { + $data[] = $optionsFromMap; } - $data[] = [ - 'label' => $optionValue['label'], - 'code' => $code, - 'use_default_value' => $optionValue['use_default_value'], - 'value_index' => $optionValue['value_index'] - ]; } } return $data; } + + /** + * Get options by index mapping + * + * @param array $optionMap + * @param string $code + * @param int $optionId + * @param int $attributeCodeId + * @return array + */ + private function getOptionsFromMap(array $optionMap, string $code, int $optionId, int $attributeCodeId): array + { + $data = []; + if (isset($optionMap[$optionId . ':' . $attributeCodeId])) { + $optionValue = $optionMap[$optionId . ':' . $attributeCodeId]; + $data = $this->getOptionsArray($optionValue, $code); + } + return $data; + } + + /** + * Get options formatted as array + * + * @param array $optionValue + * @param string $code + * @return array + */ + private function getOptionsArray(array $optionValue, string $code): array + { + return [ + 'label' => $optionValue['label'] ?? null, + 'code' => $code, + 'use_default_value' => $optionValue['use_default_value'] ?? null, + 'value_index' => $optionValue['value_index'] ?? null, + ]; + } } diff --git a/app/code/Magento/ConfigurableProductGraphQl/Model/Variant/Collection.php b/app/code/Magento/ConfigurableProductGraphQl/Model/Variant/Collection.php index d517c9aa29bd3..6c4371b23927e 100644 --- a/app/code/Magento/ConfigurableProductGraphQl/Model/Variant/Collection.php +++ b/app/code/Magento/ConfigurableProductGraphQl/Model/Variant/Collection.php @@ -14,6 +14,7 @@ use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionProcessorInterface; +use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product\CollectionPostProcessor; /** * Collection for fetching configurable child product data. @@ -55,22 +56,30 @@ class Collection */ private $collectionProcessor; + /** + * @var CollectionPostProcessor + */ + private $collectionPostProcessor; + /** * @param CollectionFactory $childCollectionFactory * @param SearchCriteriaBuilder $searchCriteriaBuilder * @param MetadataPool $metadataPool * @param CollectionProcessorInterface $collectionProcessor + * @param CollectionPostProcessor $collectionPostProcessor */ public function __construct( CollectionFactory $childCollectionFactory, SearchCriteriaBuilder $searchCriteriaBuilder, MetadataPool $metadataPool, - CollectionProcessorInterface $collectionProcessor + CollectionProcessorInterface $collectionProcessor, + CollectionPostProcessor $collectionPostProcessor ) { $this->childCollectionFactory = $childCollectionFactory; $this->searchCriteriaBuilder = $searchCriteriaBuilder; $this->metadataPool = $metadataPool; $this->collectionProcessor = $collectionProcessor; + $this->collectionPostProcessor = $collectionPostProcessor; } /** @@ -126,7 +135,6 @@ public function getChildProductsByParentId(int $id) : array * Fetch all children products from parent id's. * * @return array - * @throws \Exception */ private function fetch() : array { @@ -144,9 +152,11 @@ private function fetch() : array $this->searchCriteriaBuilder->create(), $attributeData ); + $childCollection->load(); + $this->collectionPostProcessor->process($childCollection, $attributeData); /** @var Product $childProduct */ - foreach ($childCollection->getItems() as $childProduct) { + foreach ($childCollection as $childProduct) { $formattedChild = ['model' => $childProduct, 'sku' => $childProduct->getSku()]; $parentId = (int)$childProduct->getParentId(); if (!isset($this->childrenMap[$parentId])) { @@ -168,7 +178,7 @@ private function fetch() : array */ private function getAttributesCodes(Product $currentProduct): array { - $attributeCodes = []; + $attributeCodes = $this->attributeCodes; $allowAttributes = $currentProduct->getTypeInstance()->getConfigurableAttributes($currentProduct); foreach ($allowAttributes as $attribute) { $productAttribute = $attribute->getProductAttribute(); diff --git a/app/code/Magento/ConfigurableProductSales/registration.php b/app/code/Magento/ConfigurableProductSales/registration.php index 99affe39c15c1..a6d88cff482e4 100644 --- a/app/code/Magento/ConfigurableProductSales/registration.php +++ b/app/code/Magento/ConfigurableProductSales/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_ConfigurableProductSales', __DIR__); diff --git a/app/code/Magento/Contact/Test/Unit/Model/ConfigTest.php b/app/code/Magento/Contact/Test/Unit/Model/ConfigTest.php new file mode 100644 index 0000000000000..9456ea4b48105 --- /dev/null +++ b/app/code/Magento/Contact/Test/Unit/Model/ConfigTest.php @@ -0,0 +1,126 @@ +<?php + +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Contact\Test\Unit\Model; + +use Magento\Contact\Model\Config; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * Unit test for Magento\Contact\Model\Config + */ +class ConfigTest extends TestCase +{ + /** + * @var Config + */ + private $model; + + /** + * @var ScopeConfigInterface|MockObject + */ + private $scopeConfigMock; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) + ->setMethods(['getValue', 'isSetFlag']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $objectManager = new ObjectManagerHelper($this); + $this->model = $objectManager->getObject( + Config::class, + [ + 'scopeConfig' => $this->scopeConfigMock + ] + ); + } + + /** + * Test isEnabled() + * + * @return void + * @dataProvider isEnabledDataProvider + */ + public function testIsEnabled($isSetFlag, $result): void + { + $this->scopeConfigMock->expects($this->once()) + ->method('isSetFlag') + ->with(config::XML_PATH_ENABLED, ScopeInterface::SCOPE_STORE) + ->willReturn($isSetFlag); + + $this->assertEquals($result, $this->model->isEnabled()); + } + + /** + * Data provider for isEnabled() + * + * @return array + */ + public function isEnabledDataProvider(): array + { + return [ + [true, true], + [false, false] + ]; + } + + /** + * Test emailTemplate() + * + * @return void + */ + public function testEmailTemplate(): void + { + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->with(config::XML_PATH_EMAIL_TEMPLATE, ScopeInterface::SCOPE_STORE) + ->willReturn('contact_email_email_template'); + + $this->assertEquals('contact_email_email_template', $this->model->emailTemplate()); + } + + /** + * Test emailSender() + * + * @return void + */ + public function testEmailSender(): void + { + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->with(config::XML_PATH_EMAIL_SENDER, ScopeInterface::SCOPE_STORE) + ->willReturn('custom2'); + + $this->assertEquals('custom2', $this->model->emailSender()); + } + + /** + * Test emailRecipient() + * + * @return void + */ + public function testEmailRecipient(): void + { + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->with(config::XML_PATH_EMAIL_RECIPIENT, ScopeInterface::SCOPE_STORE) + ->willReturn('hello@example.com'); + + $this->assertEquals('hello@example.com', $this->model->emailRecipient()); + } +} diff --git a/app/code/Magento/Contact/ViewModel/UserDataProvider.php b/app/code/Magento/Contact/ViewModel/UserDataProvider.php new file mode 100644 index 0000000000000..678f4349b0e88 --- /dev/null +++ b/app/code/Magento/Contact/ViewModel/UserDataProvider.php @@ -0,0 +1,72 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Contact\ViewModel; + +use Magento\Contact\Helper\Data; +use Magento\Framework\View\Element\Block\ArgumentInterface; + +/** + * Provides the user data to fill the form. + */ +class UserDataProvider implements ArgumentInterface +{ + + /** + * @var Data + */ + private $helper; + + /** + * UserDataProvider constructor. + * @param Data $helper + */ + public function __construct( + Data $helper + ) { + $this->helper = $helper; + } + + /** + * Get user name + * + * @return string + */ + public function getUserName() + { + return $this->helper->getPostValue('name') ?: $this->helper->getUserName(); + } + + /** + * Get user email + * + * @return string + */ + public function getUserEmail() + { + return $this->helper->getPostValue('email') ?: $this->helper->getUserEmail(); + } + + /** + * Get user telephone + * + * @return string + */ + public function getUserTelephone() + { + return $this->helper->getPostValue('telephone'); + } + + /** + * Get user comment + * + * @return string + */ + public function getUserComment() + { + return $this->helper->getPostValue('comment'); + } +} diff --git a/app/code/Magento/Contact/registration.php b/app/code/Magento/Contact/registration.php index b280b2e0b81a2..0d8b36f51eda5 100644 --- a/app/code/Magento/Contact/registration.php +++ b/app/code/Magento/Contact/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Contact', __DIR__); diff --git a/app/code/Magento/Contact/view/frontend/layout/contact_index_index.xml b/app/code/Magento/Contact/view/frontend/layout/contact_index_index.xml index 078c1a4ff5621..c7009831d4642 100644 --- a/app/code/Magento/Contact/view/frontend/layout/contact_index_index.xml +++ b/app/code/Magento/Contact/view/frontend/layout/contact_index_index.xml @@ -13,6 +13,9 @@ <referenceContainer name="content"> <block class="Magento\Contact\Block\ContactForm" name="contactForm" template="Magento_Contact::form.phtml"> <container name="form.additional.info" label="Form Additional Info"/> + <arguments> + <argument name="view_model" xsi:type="object">Magento\Contact\ViewModel\UserDataProvider</argument> + </arguments> </block> </referenceContainer> </body> diff --git a/app/code/Magento/Contact/view/frontend/templates/form.phtml b/app/code/Magento/Contact/view/frontend/templates/form.phtml index 673bdc73840dc..3a7c4c8b6d865 100644 --- a/app/code/Magento/Contact/view/frontend/templates/form.phtml +++ b/app/code/Magento/Contact/view/frontend/templates/form.phtml @@ -5,6 +5,9 @@ */ /** @var \Magento\Contact\Block\ContactForm $block */ +/** @var \Magento\Contact\ViewModel\UserDataProvider $viewModel */ + +$viewModel = $block->getViewModel(); ?> <form class="form contact" action="<?= $block->escapeUrl($block->getFormAction()) ?>" @@ -14,29 +17,57 @@ data-mage-init='{"validation":{}}'> <fieldset class="fieldset"> <legend class="legend"><span><?= $block->escapeHtml(__('Write Us')) ?></span></legend><br /> - <div class="field note no-label"><?= $block->escapeHtml(__('Jot us a note and we’ll get back to you as quickly as possible.')) ?></div> + <div class="field note no-label"> + <?= $block->escapeHtml(__('Jot us a note and we’ll get back to you as quickly as possible.')) ?> + </div> <div class="field name required"> <label class="label" for="name"><span><?= $block->escapeHtml(__('Name')) ?></span></label> <div class="control"> - <input name="name" id="name" title="<?= $block->escapeHtmlAttr(__('Name')) ?>" value="<?= $block->escapeHtmlAttr($this->helper(\Magento\Contact\Helper\Data::class)->getPostValue('name') ?: $this->helper(\Magento\Contact\Helper\Data::class)->getUserName()) ?>" class="input-text" type="text" data-validate="{required:true}"/> + <input name="name" + id="name" + title="<?= $block->escapeHtmlAttr(__('Name')) ?>" + value="<?= $block->escapeHtmlAttr($viewModel->getUserName()) ?>" + class="input-text" + type="text" + data-validate="{required:true}"/> </div> </div> <div class="field email required"> <label class="label" for="email"><span><?= $block->escapeHtml(__('Email')) ?></span></label> <div class="control"> - <input name="email" id="email" title="<?= $block->escapeHtmlAttr(__('Email')) ?>" value="<?= $block->escapeHtmlAttr($this->helper(\Magento\Contact\Helper\Data::class)->getPostValue('email') ?: $this->helper(\Magento\Contact\Helper\Data::class)->getUserEmail()) ?>" class="input-text" type="email" data-validate="{required:true, 'validate-email':true}"/> + <input name="email" + id="email" + title="<?= $block->escapeHtmlAttr(__('Email')) ?>" + value="<?= $block->escapeHtmlAttr($viewModel->getUserEmail()) ?>" + class="input-text" + type="email" + data-validate="{required:true, 'validate-email':true}"/> </div> </div> <div class="field telephone"> <label class="label" for="telephone"><span><?= $block->escapeHtml(__('Phone Number')) ?></span></label> <div class="control"> - <input name="telephone" id="telephone" title="<?= $block->escapeHtmlAttr(__('Phone Number')) ?>" value="<?= $block->escapeHtmlAttr($this->helper(\Magento\Contact\Helper\Data::class)->getPostValue('telephone')) ?>" class="input-text" type="text" /> + <input name="telephone" + id="telephone" + title="<?= $block->escapeHtmlAttr(__('Phone Number')) ?>" + value="<?= $block->escapeHtmlAttr($viewModel->getUserTelephone()) ?>" + class="input-text" + type="text" /> </div> </div> <div class="field comment required"> - <label class="label" for="comment"><span><?= $block->escapeHtml(__('What’s on your mind?')) ?></span></label> + <label class="label" for="comment"> + <span><?= $block->escapeHtml(__('What’s on your mind?')) ?></span> + </label> <div class="control"> - <textarea name="comment" id="comment" title="<?= $block->escapeHtmlAttr(__('What’s on your mind?')) ?>" class="input-text" cols="5" rows="3" data-validate="{required:true}"><?= $block->escapeHtml($this->helper(\Magento\Contact\Helper\Data::class)->getPostValue('comment')) ?></textarea> + <textarea name="comment" + id="comment" + title="<?= $block->escapeHtmlAttr(__('What’s on your mind?')) ?>" + class="input-text" + cols="5" + rows="3" + data-validate="{required:true}"><?= $block->escapeHtml($viewModel->getUserComment()) ?> + </textarea> </div> </div> <?= $block->getChildHtml('form.additional.info') ?> diff --git a/app/code/Magento/Cookie/Block/RequireCookie.php b/app/code/Magento/Cookie/Block/RequireCookie.php index 0a836e5441540..a9c2310b2dfc1 100644 --- a/app/code/Magento/Cookie/Block/RequireCookie.php +++ b/app/code/Magento/Cookie/Block/RequireCookie.php @@ -3,15 +3,22 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - +declare(strict_types=1); /** * Frontend form key content block */ namespace Magento\Cookie\Block; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\Config\ScopeConfigInterface; + /** + * Block Require Cookie + * * @api * @since 100.0.2 + * + * Class \Magento\Cookie\Block\RequireCookie */ class RequireCookie extends \Magento\Framework\View\Element\Template { @@ -22,9 +29,11 @@ class RequireCookie extends \Magento\Framework\View\Element\Template */ public function getScriptOptions() { + $isRedirectCmsPage = (boolean)$this->_scopeConfig->getValue('web/browser_capabilities/cookies'); $params = [ 'noCookieUrl' => $this->escapeUrl($this->getUrl('cookie/index/noCookies/')), - 'triggers' => $this->escapeHtml($this->getTriggers()) + 'triggers' => $this->escapeHtml($this->getTriggers()), + 'isRedirectCmsPage' => $isRedirectCmsPage ]; return json_encode($params); } diff --git a/app/code/Magento/Cookie/Test/Unit/Block/RequireCookieTest.php b/app/code/Magento/Cookie/Test/Unit/Block/RequireCookieTest.php new file mode 100644 index 0000000000000..5208f0740a610 --- /dev/null +++ b/app/code/Magento/Cookie/Test/Unit/Block/RequireCookieTest.php @@ -0,0 +1,113 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Cookie\Test\Unit\Block; + +use Magento\Cookie\Block\RequireCookie; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\View\Element\Template\Context; + +/** + * Class \Magento\Cookie\Test\Unit\Block\RequireCookieTest + */ +class RequireCookieTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject|RequireCookie + */ + private $block; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|Context + */ + private $context; + + /** + * Setup Environment + */ + protected function setUp() + { + $this->scopeConfig = $this->getMockBuilder(ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getValue']) + ->getMockForAbstractClass(); + $this->context = $this->getMockBuilder(Context::class) + ->disableOriginalConstructor() + ->getMock(); + $this->context->expects($this->any())->method('getScopeConfig') + ->willReturn($this->scopeConfig); + $this->block = $this->getMockBuilder(RequireCookie::class) + ->setMethods(['escapeHtml', 'escapeUrl', 'getUrl', 'getTriggers']) + ->setConstructorArgs( + [ + 'context' => $this->context + ] + )->getMock(); + } + + /** + * Test getScriptOptions() when the settings "Redirect to CMS-page if Cookies are Disabled" is "Yes" + */ + public function testGetScriptOptionsWhenRedirectToCmsIsYes() + { + $this->scopeConfig->expects($this->any())->method('getValue') + ->with('web/browser_capabilities/cookies') + ->willReturn('1'); + + $this->block->expects($this->any())->method('getUrl') + ->with('cookie/index/noCookies/') + ->willReturn('http://magento.com/cookie/index/noCookies/'); + $this->block->expects($this->any())->method('getTriggers') + ->willReturn('test'); + $this->block->expects($this->any())->method('escapeUrl') + ->with('http://magento.com/cookie/index/noCookies/') + ->willReturn('http://magento.com/cookie/index/noCookies/'); + $this->block->expects($this->any())->method('escapeHtml') + ->with('test') + ->willReturn('test'); + + $this->assertEquals( + '{"noCookieUrl":"http:\/\/magento.com\/cookie\/index\/noCookies\/",' . + '"triggers":"test","isRedirectCmsPage":true}', + $this->block->getScriptOptions() + ); + } + + /** + * Test getScriptOptions() when the settings "Redirect to CMS-page if Cookies are Disabled" is "No" + */ + public function testGetScriptOptionsWhenRedirectToCmsIsNo() + { + $this->scopeConfig->expects($this->any())->method('getValue') + ->with('web/browser_capabilities/cookies') + ->willReturn('0'); + + $this->block->expects($this->any())->method('getUrl') + ->with('cookie/index/noCookies/') + ->willReturn('http://magento.com/cookie/index/noCookies/'); + $this->block->expects($this->any())->method('getTriggers') + ->willReturn('test'); + $this->block->expects($this->any())->method('escapeUrl') + ->with('http://magento.com/cookie/index/noCookies/') + ->willReturn('http://magento.com/cookie/index/noCookies/'); + $this->block->expects($this->any())->method('escapeHtml') + ->with('test') + ->willReturn('test'); + + $this->assertEquals( + '{"noCookieUrl":"http:\/\/magento.com\/cookie\/index\/noCookies\/",' . + '"triggers":"test","isRedirectCmsPage":false}', + $this->block->getScriptOptions() + ); + } +} diff --git a/app/code/Magento/Cookie/i18n/en_US.csv b/app/code/Magento/Cookie/i18n/en_US.csv index 09424c22833fe..7fc98c0ad4c58 100644 --- a/app/code/Magento/Cookie/i18n/en_US.csv +++ b/app/code/Magento/Cookie/i18n/en_US.csv @@ -11,3 +11,5 @@ "Cookie Domain","Cookie Domain" "Use HTTP Only","Use HTTP Only" "Cookie Restriction Mode","Cookie Restriction Mode" +"Cookies are disabled in your browser.","Cookies are disabled in your browser." + diff --git a/app/code/Magento/Cookie/registration.php b/app/code/Magento/Cookie/registration.php index 64f13f19968ce..5b5b7b1f3c96f 100644 --- a/app/code/Magento/Cookie/registration.php +++ b/app/code/Magento/Cookie/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Cookie', __DIR__); diff --git a/app/code/Magento/Cookie/view/frontend/web/js/require-cookie.js b/app/code/Magento/Cookie/view/frontend/web/js/require-cookie.js index 0a175136f034e..e82d7f2af29ca 100644 --- a/app/code/Magento/Cookie/view/frontend/web/js/require-cookie.js +++ b/app/code/Magento/Cookie/view/frontend/web/js/require-cookie.js @@ -8,15 +8,19 @@ */ define([ 'jquery', - 'jquery-ui-modules/widget' -], function ($) { + 'Magento_Ui/js/modal/alert', + 'jquery-ui-modules/widget', + 'mage/mage', + 'mage/translate' +], function ($, alert) { 'use strict'; $.widget('mage.requireCookie', { options: { event: 'click', noCookieUrl: 'enable-cookies', - triggers: ['.action.login', '.action.submit'] + triggers: ['.action.login', '.action.submit'], + isRedirectCmsPage: true }, /** @@ -49,8 +53,16 @@ define([ if (navigator.cookieEnabled) { return; } + event.preventDefault(); - window.location = this.options.noCookieUrl; + + if (this.options.isRedirectCmsPage) { + window.location = this.options.noCookieUrl; + } else { + alert({ + content: $.mage.__('Cookies are disabled in your browser.') + }); + } } }); diff --git a/app/code/Magento/Cron/Model/Schedule.php b/app/code/Magento/Cron/Model/Schedule.php index 582c7c811b71f..365d110421664 100644 --- a/app/code/Magento/Cron/Model/Schedule.php +++ b/app/code/Magento/Cron/Model/Schedule.php @@ -97,7 +97,7 @@ public function _construct() public function setCronExpr($expr) { $e = preg_split('#\s+#', $expr, null, PREG_SPLIT_NO_EMPTY); - if (sizeof($e) < 5 || sizeof($e) > 6) { + if (count($e) < 5 || count($e) > 6) { throw new CronException(__('Invalid cron expression: %1', $expr)); } @@ -168,7 +168,7 @@ public function matchCronExpression($expr, $num) // handle modulus if (strpos($expr, '/') !== false) { $e = explode('/', $expr); - if (sizeof($e) !== 2) { + if (count($e) !== 2) { throw new CronException(__('Invalid cron expression, expecting \'match/modulus\': %1', $expr)); } if (!is_numeric($e[1])) { @@ -187,7 +187,7 @@ public function matchCronExpression($expr, $num) } elseif (strpos($expr, '-') !== false) { // handle range $e = explode('-', $expr); - if (sizeof($e) !== 2) { + if (count($e) !== 2) { throw new CronException(__('Invalid cron expression, expecting \'from-to\' structure: %1', $expr)); } diff --git a/app/code/Magento/Cron/Observer/ProcessCronQueueObserver.php b/app/code/Magento/Cron/Observer/ProcessCronQueueObserver.php index 5c8aa1dc78abd..053ba43c1c20e 100644 --- a/app/code/Magento/Cron/Observer/ProcessCronQueueObserver.php +++ b/app/code/Magento/Cron/Observer/ProcessCronQueueObserver.php @@ -3,9 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + /** * Handling cron jobs */ + namespace Magento\Cron\Observer; use Magento\Cron\Model\Schedule; @@ -69,6 +71,11 @@ class ProcessCronQueueObserver implements ObserverInterface */ const LOCK_PREFIX = 'CRON_GROUP_'; + /** + * Cron Job name pattern for Profiling + */ + const CRON_TIMERID = 'job %s'; + /** * @var \Magento\Cron\Model\ResourceModel\Schedule\Collection */ @@ -311,7 +318,7 @@ protected function _runJob($scheduledTime, $currentTime, $jobConfig, $schedule, $schedule->setExecutedAt(strftime('%Y-%m-%d %H:%M:%S', $this->dateTime->gmtTimestamp()))->save(); - $this->startProfiling(); + $this->startProfiling($jobCode); try { $this->logger->info(sprintf('Cron Job %s is run', $jobCode)); //phpcs:ignore Magento2.Functions.DiscouragedFunction @@ -323,7 +330,7 @@ protected function _runJob($scheduledTime, $currentTime, $jobConfig, $schedule, 'Cron Job %s has an error: %s. Statistics: %s', $jobCode, $e->getMessage(), - $this->getProfilingStat() + $this->getProfilingStat($jobCode) ) ); if (!$e instanceof \Exception) { @@ -335,7 +342,7 @@ protected function _runJob($scheduledTime, $currentTime, $jobConfig, $schedule, } throw $e; } finally { - $this->stopProfiling(); + $this->stopProfiling($jobCode); } $schedule->setStatus( @@ -351,7 +358,7 @@ protected function _runJob($scheduledTime, $currentTime, $jobConfig, $schedule, sprintf( 'Cron Job %s is successfully finished. Statistics: %s', $jobCode, - $this->getProfilingStat() + $this->getProfilingStat($jobCode) ) ); } @@ -359,32 +366,47 @@ protected function _runJob($scheduledTime, $currentTime, $jobConfig, $schedule, /** * Starts profiling * + * @param string $jobName * @return void */ - private function startProfiling() + private function startProfiling(string $jobName = '') { $this->statProfiler->clear(); - $this->statProfiler->start('job', microtime(true), memory_get_usage(true), memory_get_usage()); + $this->statProfiler->start( + sprintf(self::CRON_TIMERID, $jobName), + microtime(true), + memory_get_usage(true), + memory_get_usage() + ); } /** * Stops profiling * + * @param string $jobName * @return void */ - private function stopProfiling() + private function stopProfiling(string $jobName = '') { - $this->statProfiler->stop('job', microtime(true), memory_get_usage(true), memory_get_usage()); + $this->statProfiler->stop( + sprintf(self::CRON_TIMERID, $jobName), + microtime(true), + memory_get_usage(true), + memory_get_usage() + ); } /** * Retrieves statistics in the JSON format * + * @param string $jobName * @return string */ - private function getProfilingStat() + private function getProfilingStat(string $jobName): string { - $stat = $this->statProfiler->get('job'); + $stat = $this->statProfiler->get( + sprintf(self::CRON_TIMERID, $jobName) + ); unset($stat[Stat::START]); return json_encode($stat); } @@ -418,7 +440,9 @@ private function getNonExitedSchedules($groupId) 'status', [ 'in' => [ - Schedule::STATUS_PENDING, Schedule::STATUS_RUNNING, Schedule::STATUS_SUCCESS + Schedule::STATUS_PENDING, + Schedule::STATUS_RUNNING, + Schedule::STATUS_SUCCESS ] ] ); @@ -478,10 +502,10 @@ private function generateSchedules($groupId) /** * Generate jobs for config information * - * @param array $jobs - * @param array $exists - * @param string $groupId - * @return void + * @param array $jobs + * @param array $exists + * @param string $groupId + * @return void */ protected function _generateJobs($jobs, $exists, $groupId) { diff --git a/app/code/Magento/Cron/etc/adminhtml/system.xml b/app/code/Magento/Cron/etc/adminhtml/system.xml index c8753f1b0b56f..cef45ba386be2 100644 --- a/app/code/Magento/Cron/etc/adminhtml/system.xml +++ b/app/code/Magento/Cron/etc/adminhtml/system.xml @@ -12,7 +12,7 @@ <label>Cron (Scheduled Tasks)</label> <comment>For correct URLs generated during cron runs please make sure that Web > Secure and Unsecure Base URLs are explicitly set. All the times are in minutes.</comment> <group id="template" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> - <label>Cron configuration options for group: </label> + <label>Cron configuration options for group:</label> <field id="schedule_generate_every" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Generate Schedules Every</label> <validate>validate-zero-or-greater validate-digits</validate> diff --git a/app/code/Magento/Cron/i18n/en_US.csv b/app/code/Magento/Cron/i18n/en_US.csv index b1969d7723315..df9aef7f13747 100644 --- a/app/code/Magento/Cron/i18n/en_US.csv +++ b/app/code/Magento/Cron/i18n/en_US.csv @@ -11,7 +11,7 @@ Monthly,Monthly "Test exception","Test exception" "Cron (Scheduled Tasks)","Cron (Scheduled Tasks)" "For correct URLs generated during cron runs please make sure that Web > Secure and Unsecure Base URLs are explicitly set. All the times are in minutes.","For correct URLs generated during cron runs please make sure that Web > Secure and Unsecure Base URLs are explicitly set. All the times are in minutes." -"Cron configuration options for group: ","Cron configuration options for group: " +"Cron configuration options for group:","Cron configuration options for group:" "Generate Schedules Every","Generate Schedules Every" "Schedule Ahead for","Schedule Ahead for" "Missed if Not Run Within","Missed if Not Run Within" diff --git a/app/code/Magento/Cron/registration.php b/app/code/Magento/Cron/registration.php index d22298a2efc37..97f162d76ec52 100644 --- a/app/code/Magento/Cron/registration.php +++ b/app/code/Magento/Cron/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Cron', __DIR__); diff --git a/app/code/Magento/Csp/Api/CspRendererInterface.php b/app/code/Magento/Csp/Api/CspRendererInterface.php new file mode 100644 index 0000000000000..696d2e147f054 --- /dev/null +++ b/app/code/Magento/Csp/Api/CspRendererInterface.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Api; + +use Magento\Framework\App\Response\HttpInterface as HttpResponse; + +/** + * Renders configured CSPs + */ +interface CspRendererInterface +{ + /** + * Render configured CSP for the given HTTP response. + * + * @param HttpResponse $response + * @return void + */ + public function render(HttpResponse $response): void; +} diff --git a/app/code/Magento/Csp/Api/Data/ModeConfiguredInterface.php b/app/code/Magento/Csp/Api/Data/ModeConfiguredInterface.php new file mode 100644 index 0000000000000..5779bbd052792 --- /dev/null +++ b/app/code/Magento/Csp/Api/Data/ModeConfiguredInterface.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Api\Data; + +/** + * CSP mode. + */ +interface ModeConfiguredInterface +{ + /** + * Report only mode flag. + * + * In "report-only" mode browsers only report violation but do not restrict them. + * + * @return bool + */ + public function isReportOnly(): bool; + + /** + * URI of endpoint logging reported violations. + * + * Even in "restrict" mode violations can be logged. + * + * @return string|null + */ + public function getReportUri(): ?string; +} diff --git a/app/code/Magento/Csp/Api/Data/PolicyInterface.php b/app/code/Magento/Csp/Api/Data/PolicyInterface.php new file mode 100644 index 0000000000000..c713c4b61417f --- /dev/null +++ b/app/code/Magento/Csp/Api/Data/PolicyInterface.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Api\Data; + +/** + * Defined Content Security Policy. + * + * Different policies will have different types of data but they all will have identifiers and string representations. + */ +interface PolicyInterface +{ + /** + * Policy unique name (ID). + * + * @return string + */ + public function getId(): string; + + /** + * Value of the policy. + * + * @return string + */ + public function getValue(): string; +} diff --git a/app/code/Magento/Csp/Api/ModeConfigManagerInterface.php b/app/code/Magento/Csp/Api/ModeConfigManagerInterface.php new file mode 100644 index 0000000000000..79aff996bbf5c --- /dev/null +++ b/app/code/Magento/Csp/Api/ModeConfigManagerInterface.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Api; + +use Magento\Csp\Api\Data\ModeConfiguredInterface; + +/** + * CSP mode config manager. + * + * Responsible for CSP mode configurations like report-only/restrict modes, report URL etc. + */ +interface ModeConfigManagerInterface +{ + /** + * Load CSP mode config. + * + * @return ModeConfiguredInterface + * @throws \RuntimeException When failed to retrieve configurations. + */ + public function getConfigured(): ModeConfiguredInterface; +} diff --git a/app/code/Magento/Csp/Api/PolicyCollectorInterface.php b/app/code/Magento/Csp/Api/PolicyCollectorInterface.php new file mode 100644 index 0000000000000..139dec77e040b --- /dev/null +++ b/app/code/Magento/Csp/Api/PolicyCollectorInterface.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Api; + +use Magento\Csp\Api\Data\PolicyInterface; + +/** + * Collects CSPs from a source. + */ +interface PolicyCollectorInterface +{ + /** + * Collect all configured policies. + * + * Collector finds CSPs from configurations and returns a list. + * The resulting list will be used to render policies as is so it is a collector's responsibility to include + * previously found policies from $defaultPolicies or redefine them. + * + * @param PolicyInterface[] $defaultPolicies Default policies/policies found previously. + * @return PolicyInterface[] + */ + public function collect(array $defaultPolicies = []): array; +} diff --git a/app/code/Magento/Csp/Api/PolicyRendererInterface.php b/app/code/Magento/Csp/Api/PolicyRendererInterface.php new file mode 100644 index 0000000000000..db761598291d4 --- /dev/null +++ b/app/code/Magento/Csp/Api/PolicyRendererInterface.php @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Api; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Framework\App\Response\HttpInterface as HttpResponse; + +/** + * Renders one policy at a time. + * + * Different type of CSPs may require specific renderers due to being represented by different headers. + */ +interface PolicyRendererInterface +{ + /** + * Render a policy for a response. + * + * @param PolicyInterface $policy + * @param HttpResponse $response + * @return void + */ + public function render(PolicyInterface $policy, HttpResponse $response): void; + + /** + * Would this renderer work for given policy? + * + * @param PolicyInterface $policy + * @return bool + */ + public function canRender(PolicyInterface $policy): bool; +} diff --git a/app/code/Magento/Csp/LICENSE.txt b/app/code/Magento/Csp/LICENSE.txt new file mode 100644 index 0000000000000..49525fd99da9c --- /dev/null +++ b/app/code/Magento/Csp/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/Csp/LICENSE_AFL.txt b/app/code/Magento/Csp/LICENSE_AFL.txt new file mode 100644 index 0000000000000..f39d641b18a19 --- /dev/null +++ b/app/code/Magento/Csp/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/Csp/Model/Collector/Config/FetchPolicyReader.php b/app/code/Magento/Csp/Model/Collector/Config/FetchPolicyReader.php new file mode 100644 index 0000000000000..8699d9588b909 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/Config/FetchPolicyReader.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector\Config; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Model\Policy\FetchPolicy; + +/** + * Reads fetch directives. + */ +class FetchPolicyReader implements PolicyReaderInterface +{ + /** + * @inheritDoc + */ + public function read(string $id, $value): PolicyInterface + { + return new FetchPolicy( + $id, + !empty($value['none']), + !empty($value['hosts']) ? array_values($value['hosts']) : [], + !empty($value['schemes']) ? array_values($value['schemes']) : [], + !empty($value['self']), + !empty($value['inline']), + !empty($value['eval']), + [], + [], + !empty($value['dynamic']), + !empty($value['event_handlers']) + ); + } + + /** + * @inheritDoc + */ + public function canRead(string $id): bool + { + return in_array($id, FetchPolicy::POLICIES, true); + } +} diff --git a/app/code/Magento/Csp/Model/Collector/Config/FlagPolicyReader.php b/app/code/Magento/Csp/Model/Collector/Config/FlagPolicyReader.php new file mode 100644 index 0000000000000..f8d1ea962308f --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/Config/FlagPolicyReader.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector\Config; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Model\Policy\FlagPolicy; + +/** + * @inheritDoc + */ +class FlagPolicyReader implements PolicyReaderInterface +{ + /** + * @inheritDoc + */ + public function read(string $id, $value): PolicyInterface + { + return new FlagPolicy($id); + } + + /** + * @inheritDoc + */ + public function canRead(string $id): bool + { + return in_array($id, FlagPolicy::POLICIES, true); + } +} diff --git a/app/code/Magento/Csp/Model/Collector/Config/PluginTypesPolicyReader.php b/app/code/Magento/Csp/Model/Collector/Config/PluginTypesPolicyReader.php new file mode 100644 index 0000000000000..7214e6da62273 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/Config/PluginTypesPolicyReader.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector\Config; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Model\Policy\PluginTypesPolicy; + +/** + * @inheritDoc + */ +class PluginTypesPolicyReader implements PolicyReaderInterface +{ + /** + * @inheritDoc + */ + public function read(string $id, $value): PolicyInterface + { + return new PluginTypesPolicy(array_values($value['types'])); + } + + /** + * @inheritDoc + */ + public function canRead(string $id): bool + { + return $id === 'plugin-types'; + } +} diff --git a/app/code/Magento/Csp/Model/Collector/Config/PolicyReaderInterface.php b/app/code/Magento/Csp/Model/Collector/Config/PolicyReaderInterface.php new file mode 100644 index 0000000000000..2ebac992222d9 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/Config/PolicyReaderInterface.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector\Config; + +use Magento\Csp\Api\Data\PolicyInterface; + +/** + * Initiates a policy DTO based on a value found in Magento config. + */ +interface PolicyReaderInterface +{ + /** + * Read a policy from a config value. + * + * @param string $id + * @param string|array|bool $value + * @return PolicyInterface + */ + public function read(string $id, $value): PolicyInterface; + + /** + * Can given policy be read by this reader? + * + * @param string $id + * @return bool + */ + public function canRead(string $id): bool; +} diff --git a/app/code/Magento/Csp/Model/Collector/Config/PolicyReaderPool.php b/app/code/Magento/Csp/Model/Collector/Config/PolicyReaderPool.php new file mode 100644 index 0000000000000..0fc6fe03e05fa --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/Config/PolicyReaderPool.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector\Config; + +/** + * Pool of readers. + */ +class PolicyReaderPool +{ + /** + * @var PolicyReaderInterface[] + */ + private $readers; + + /** + * @param PolicyReaderInterface[] $readers + */ + public function __construct(array $readers) + { + $this->readers = $readers; + } + + /** + * Find a reader for the policy. + * + * @param string $id + * @return PolicyReaderInterface + * @throws \RuntimeException When failed to find a reader for given policy. + */ + public function getReader(string $id): PolicyReaderInterface + { + foreach ($this->readers as $reader) { + if ($reader->canRead($id)) { + return $reader; + } + } + + throw new \RuntimeException(sprintf('Failed to find a config reader for policy #%s', $id)); + } +} diff --git a/app/code/Magento/Csp/Model/Collector/Config/SandboxPolicyReader.php b/app/code/Magento/Csp/Model/Collector/Config/SandboxPolicyReader.php new file mode 100644 index 0000000000000..2699b8d57c037 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/Config/SandboxPolicyReader.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector\Config; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Model\Policy\SandboxPolicy; + +/** + * @inheritDoc + */ +class SandboxPolicyReader implements PolicyReaderInterface +{ + /** + * @inheritDoc + */ + public function read(string $id, $value): PolicyInterface + { + return new SandboxPolicy( + !empty($value['forms']), + !empty($value['modals']), + !empty($value['orientation']), + !empty($value['pointer']), + !empty($value['popup']), + !empty($value['popups_to_escape']), + !empty($value['presentation']), + !empty($value['same_origin']), + !empty($value['scripts']), + !empty($value['navigation']), + !empty($value['navigation_by_user']) + ); + } + + /** + * @inheritDoc + */ + public function canRead(string $id): bool + { + return $id === 'sandbox'; + } +} diff --git a/app/code/Magento/Csp/Model/Collector/ConfigCollector.php b/app/code/Magento/Csp/Model/Collector/ConfigCollector.php new file mode 100644 index 0000000000000..34711fe5d8a22 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/ConfigCollector.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector; + +use Magento\Csp\Api\PolicyCollectorInterface; +use Magento\Csp\Model\Collector\Config\PolicyReaderPool; +use Magento\Framework\App\Area; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\State; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Store\Model\ScopeInterface; + +/** + * Reads Magento config. + */ +class ConfigCollector implements PolicyCollectorInterface +{ + /** + * @var ScopeConfigInterface + */ + private $config; + + /** + * @var PolicyReaderPool + */ + private $readersPool; + + /** + * @var State + */ + private $state; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @param ScopeConfigInterface $config + * @param PolicyReaderPool $readersPool + * @param State $state + * @param StoreManagerInterface $storeManager + */ + public function __construct( + ScopeConfigInterface $config, + PolicyReaderPool $readersPool, + State $state, + StoreManagerInterface $storeManager + ) { + $this->config = $config; + $this->readersPool = $readersPool; + $this->state = $state; + $this->storeManager = $storeManager; + } + + /** + * @inheritDoc + */ + public function collect(array $defaultPolicies = []): array + { + $collected = $defaultPolicies; + + $configArea = null; + $area = $this->state->getAreaCode(); + if ($area === Area::AREA_ADMINHTML) { + $configArea = 'admin'; + } elseif ($area === Area::AREA_FRONTEND) { + $configArea = 'storefront'; + } + + if ($configArea) { + $policiesConfig = $this->config->getValue( + 'csp/policies/' . $configArea, + ScopeInterface::SCOPE_STORE, + $this->storeManager->getStore() + ); + if (is_array($policiesConfig) && $policiesConfig) { + foreach ($policiesConfig as $policyConfig) { + $collected[] = $this->readersPool->getReader($policyConfig['policy_id']) + ->read($policyConfig['policy_id'], $policyConfig); + } + } + } + + return $collected; + } +} diff --git a/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/Converter.php b/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/Converter.php new file mode 100644 index 0000000000000..ab1ee8bb0befe --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/Converter.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector\CspWhitelistXml; + +use Magento\Framework\Config\ConverterInterface; + +/** + * Converts csp_whitelist.xml files' content into config data. + */ +class Converter implements ConverterInterface +{ + /** + * @inheritDoc + */ + public function convert($source) + { + $policyConfig = []; + + /** @var \DOMNodeList $policies */ + $policies = $source->getElementsByTagName('policy'); + /** @var \DOMElement $policy */ + foreach ($policies as $policy) { + if ($policy->nodeType != XML_ELEMENT_NODE) { + continue; + } + $id = $policy->attributes->getNamedItem('id')->nodeValue; + if (!array_key_exists($id, $policyConfig)) { + $policyConfig[$id] = ['hosts' => [], 'hashes' => []]; + } + /** @var \DOMElement $value */ + foreach ($policy->getElementsByTagName('value') as $value) { + if ($value->attributes->getNamedItem('type')->nodeValue === 'host') { + $policyConfig[$id]['hosts'][] = $value->nodeValue; + } else { + $policyConfig[$id]['hashes'][$value->nodeValue] + = $value->attributes->getNamedItem('algorithm')->nodeValue; + } + } + $policyConfig[$id]['hosts'] = array_unique($policyConfig[$id]['hosts']); + $policyConfig[$id]['hashes'] = array_unique($policyConfig[$id]['hashes']); + } + + return $policyConfig; + } +} diff --git a/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/Reader.php b/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/Reader.php new file mode 100644 index 0000000000000..63f570ceb7a71 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/Reader.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector\CspWhitelistXml; + +use Magento\Framework\Config\Reader\Filesystem; + +/** + * Config reader for csp_whitelist.xml files. + */ +class Reader extends Filesystem +{ + /** + * List of id attributes for merge + * + * @var array + */ + protected $_idAttributes = [ + '/csp_whitelist/policies/policy' => ['id'], + '/csp_whitelist/policies/policy/values/value' => ['id'] + ]; +} diff --git a/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/SchemaLocator.php b/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/SchemaLocator.php new file mode 100644 index 0000000000000..285d37a1b6270 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/SchemaLocator.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector\CspWhitelistXml; + +use Magento\Framework\Module\Dir; +use Magento\Framework\Config\SchemaLocatorInterface; +use Magento\Framework\Module\Dir\Reader; + +/** + * CSP whitelist config schema locator. + */ +class SchemaLocator implements SchemaLocatorInterface +{ + /** + * Path to corresponding XSD file with validation rules for merged config and per file config. + * + * @var string + */ + private $schema ; + + /** + * @param Reader $moduleReader + */ + public function __construct(Reader $moduleReader) + { + $this->schema = $moduleReader->getModuleDir(Dir::MODULE_ETC_DIR, 'Magento_Csp') + . '/csp_whitelist.xsd'; + } + + /** + * @inheritDoc + */ + public function getSchema() + { + return $this->schema; + } + + /** + * @inheritDoc + */ + public function getPerFileSchema() + { + return $this->schema; + } +} diff --git a/app/code/Magento/Csp/Model/Collector/CspWhitelistXmlCollector.php b/app/code/Magento/Csp/Model/Collector/CspWhitelistXmlCollector.php new file mode 100644 index 0000000000000..9f19a5299c063 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/CspWhitelistXmlCollector.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector; + +use Magento\Csp\Api\PolicyCollectorInterface; +use Magento\Csp\Model\Collector\CspWhitelistXml\Reader as ConfigReader; +use Magento\Csp\Model\Policy\FetchPolicy; + +/** + * Collects policies defined in csp_whitelist.xml configs. + */ +class CspWhitelistXmlCollector implements PolicyCollectorInterface +{ + /** + * @var ConfigReader + */ + private $configReader; + + /** + * @param ConfigReader $configReader + */ + public function __construct(ConfigReader $configReader) + { + $this->configReader = $configReader; + } + + /** + * @inheritDoc + */ + public function collect(array $defaultPolicies = []): array + { + $policies = $defaultPolicies; + $config = $this->configReader->read(); + foreach ($config as $policyId => $values) { + $policies[] = new FetchPolicy( + $policyId, + false, + $values['hosts'], + [], + false, + false, + false, + [], + $values['hashes'], + false, + false + ); + } + + return $policies; + } +} diff --git a/app/code/Magento/Csp/Model/Collector/FetchPolicyMerger.php b/app/code/Magento/Csp/Model/Collector/FetchPolicyMerger.php new file mode 100644 index 0000000000000..2a8f6c278b078 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/FetchPolicyMerger.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Model\Policy\FetchPolicy; + +/** + * @inheritDoc + */ +class FetchPolicyMerger implements MergerInterface +{ + /** + * @inheritDoc + */ + public function merge(PolicyInterface $policy1, PolicyInterface $policy2): PolicyInterface + { + /** @var FetchPolicy $policy1 */ + /** @var FetchPolicy $policy2 */ + return new FetchPolicy( + $policy1->getId(), + $policy1->isNoneAllowed() || $policy2->isNoneAllowed(), + array_unique(array_merge($policy1->getHostSources(), $policy2->getHostSources())), + array_unique(array_merge($policy1->getSchemeSources(), $policy2->getSchemeSources())), + $policy1->isSelfAllowed() || $policy2->isSelfAllowed(), + $policy1->isInlineAllowed() || $policy2->isInlineAllowed(), + $policy1->isEvalAllowed() || $policy2->isEvalAllowed(), + array_unique(array_merge($policy1->getNonceValues(), $policy2->getNonceValues())), + array_merge($policy1->getHashes(), $policy2->getHashes()), + $policy1->isDynamicAllowed() || $policy2->isDynamicAllowed(), + $policy1->areEventHandlersAllowed() || $policy2->areEventHandlersAllowed() + ); + } + + /** + * @inheritDoc + */ + public function canMerge(PolicyInterface $policy1, PolicyInterface $policy2): bool + { + return ($policy1 instanceof FetchPolicy) && ($policy2 instanceof FetchPolicy); + } +} diff --git a/app/code/Magento/Csp/Model/Collector/FlagPolicyMerger.php b/app/code/Magento/Csp/Model/Collector/FlagPolicyMerger.php new file mode 100644 index 0000000000000..a734feeab1281 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/FlagPolicyMerger.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Model\Policy\FlagPolicy; + +/** + * @inheritDoc + */ +class FlagPolicyMerger implements MergerInterface +{ + /** + * @inheritDoc + */ + public function merge(PolicyInterface $policy1, PolicyInterface $policy2): PolicyInterface + { + return $policy1; + } + + /** + * @inheritDoc + */ + public function canMerge(PolicyInterface $policy1, PolicyInterface $policy2): bool + { + return ($policy1 instanceof FlagPolicy) && ($policy2 instanceof FlagPolicy); + } +} diff --git a/app/code/Magento/Csp/Model/Collector/MergerInterface.php b/app/code/Magento/Csp/Model/Collector/MergerInterface.php new file mode 100644 index 0000000000000..4a8d78e7b8f4b --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/MergerInterface.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector; + +use Magento\Csp\Api\Data\PolicyInterface; + +/** + * Merges policies with the same ID in order to have only 1 policy DTO-per-policy. + */ +interface MergerInterface +{ + /** + * Merges 2 found policies into 1. + * + * @param PolicyInterface $policy1 + * @param PolicyInterface $policy2 + * @return PolicyInterface + */ + public function merge(PolicyInterface $policy1, PolicyInterface $policy2): PolicyInterface; + + /** + * Whether current merger can merge given 2 policies. + * + * @param PolicyInterface $policy1 + * @param PolicyInterface $policy2 + * @return bool + */ + public function canMerge(PolicyInterface $policy1, PolicyInterface $policy2): bool; +} diff --git a/app/code/Magento/Csp/Model/Collector/PluginTypesPolicyMerger.php b/app/code/Magento/Csp/Model/Collector/PluginTypesPolicyMerger.php new file mode 100644 index 0000000000000..58f2128657788 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/PluginTypesPolicyMerger.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Model\Policy\PluginTypesPolicy; + +/** + * @inheritDoc + */ +class PluginTypesPolicyMerger implements MergerInterface +{ + /** + * @inheritDoc + */ + public function merge(PolicyInterface $policy1, PolicyInterface $policy2): PolicyInterface + { + /** @var PluginTypesPolicy $policy1 */ + /** @var PluginTypesPolicy $policy2 */ + return new PluginTypesPolicy(array_unique(array_merge($policy1->getTypes(), $policy2->getTypes()))); + } + + /** + * @inheritDoc + */ + public function canMerge(PolicyInterface $policy1, PolicyInterface $policy2): bool + { + return ($policy1 instanceof PluginTypesPolicy) && ($policy2 instanceof PluginTypesPolicy); + } +} diff --git a/app/code/Magento/Csp/Model/Collector/SandboxPolicyMerger.php b/app/code/Magento/Csp/Model/Collector/SandboxPolicyMerger.php new file mode 100644 index 0000000000000..3e3f05b1bc845 --- /dev/null +++ b/app/code/Magento/Csp/Model/Collector/SandboxPolicyMerger.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Collector; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Model\Policy\SandboxPolicy; + +/** + * @inheritDoc + */ +class SandboxPolicyMerger implements MergerInterface +{ + /** + * @inheritDoc + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + public function merge(PolicyInterface $policy1, PolicyInterface $policy2): PolicyInterface + { + /** @var SandboxPolicy $policy1 */ + /** @var SandboxPolicy $policy2 */ + return new SandboxPolicy( + $policy1->isFormAllowed() || $policy2->isFormAllowed(), + $policy1->isModalsAllowed() || $policy2->isModalsAllowed(), + $policy1->isOrientationLockAllowed() || $policy2->isOrientationLockAllowed(), + $policy1->isPointerLockAllowed() || $policy2->isPointerLockAllowed(), + $policy1->isPopupsAllowed() || $policy2->isPopupsAllowed(), + $policy1->isPopupsToEscapeSandboxAllowed() || $policy2->isPopupsToEscapeSandboxAllowed(), + $policy1->isPresentationAllowed() || $policy2->isPresentationAllowed(), + $policy1->isSameOriginAllowed() || $policy2->isSameOriginAllowed(), + $policy1->isScriptsAllowed() || $policy2->isScriptsAllowed(), + $policy1->isTopNavigationAllowed() || $policy2->isTopNavigationAllowed(), + $policy1->isTopNavigationByUserActivationAllowed() || $policy2->isTopNavigationByUserActivationAllowed() + ); + } + + /** + * @inheritDoc + */ + public function canMerge(PolicyInterface $policy1, PolicyInterface $policy2): bool + { + return ($policy1 instanceof SandboxPolicy) && ($policy2 instanceof SandboxPolicy); + } +} diff --git a/app/code/Magento/Csp/Model/CompositePolicyCollector.php b/app/code/Magento/Csp/Model/CompositePolicyCollector.php new file mode 100644 index 0000000000000..b775c91b4e1ef --- /dev/null +++ b/app/code/Magento/Csp/Model/CompositePolicyCollector.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Api\PolicyCollectorInterface; +use Magento\Csp\Model\Collector\MergerInterface; + +/** + * Delegates collecting to multiple collectors. + */ +class CompositePolicyCollector implements PolicyCollectorInterface +{ + /** + * @var PolicyCollectorInterface[] + */ + private $collectors; + + /** + * @var MergerInterface[] + */ + private $mergers; + + /** + * @param PolicyCollectorInterface[] $collectors + * @param MergerInterface[] $mergers + */ + public function __construct(array $collectors, array $mergers) + { + $this->collectors = $collectors; + $this->mergers = $mergers; + } + + /** + * Merge 2 policies with the same ID. + * + * @param PolicyInterface $policy1 + * @param PolicyInterface $policy2 + * @return PolicyInterface + * @throws \RuntimeException When failed to merge. + */ + private function merge(PolicyInterface $policy1, PolicyInterface $policy2): PolicyInterface + { + foreach ($this->mergers as $merger) { + if ($merger->canMerge($policy1, $policy2)) { + return $merger->merge($policy1, $policy2); + } + } + + throw new \RuntimeException(sprintf('Merge for policies #%s was not found', $policy1->getId())); + } + + /** + * @inheritDoc + */ + public function collect(array $defaultPolicies = []): array + { + $collected = $defaultPolicies; + foreach ($this->collectors as $collector) { + $collected = $collector->collect($collected); + } + //Merging policies. + /** @var PolicyInterface[] $result */ + $result = []; + foreach ($collected as $policy) { + if (array_key_exists($policy->getId(), $result)) { + $result[$policy->getId()] = $this->merge($result[$policy->getId()], $policy); + } else { + $result[$policy->getId()] = $policy; + } + } + + return array_values($result); + } +} diff --git a/app/code/Magento/Csp/Model/CspRenderer.php b/app/code/Magento/Csp/Model/CspRenderer.php new file mode 100644 index 0000000000000..a883820f6743a --- /dev/null +++ b/app/code/Magento/Csp/Model/CspRenderer.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model; + +use Magento\Csp\Api\CspRendererInterface; +use Magento\Csp\Api\PolicyCollectorInterface; +use Magento\Framework\App\Response\HttpInterface as HttpResponse; + +/** + * @inheritDoc + */ +class CspRenderer implements CspRendererInterface +{ + /** + * @var PolicyRendererPool + */ + private $rendererPool; + + /** + * @var PolicyCollectorInterface + */ + private $collector; + + /** + * @param PolicyRendererPool $rendererPool + * @param PolicyCollectorInterface $collector + */ + public function __construct(PolicyRendererPool $rendererPool, PolicyCollectorInterface $collector) + { + $this->rendererPool = $rendererPool; + $this->collector = $collector; + } + + /** + * @inheritDoc + */ + public function render(HttpResponse $response): void + { + $policies = $this->collector->collect(); + foreach ($policies as $policy) { + $this->rendererPool->getRenderer($policy)->render($policy, $response); + } + } +} diff --git a/app/code/Magento/Csp/Model/Mode/ConfigManager.php b/app/code/Magento/Csp/Model/Mode/ConfigManager.php new file mode 100644 index 0000000000000..9f10154604d5f --- /dev/null +++ b/app/code/Magento/Csp/Model/Mode/ConfigManager.php @@ -0,0 +1,78 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Mode; + +use Magento\Csp\Api\Data\ModeConfiguredInterface; +use Magento\Csp\Api\ModeConfigManagerInterface; +use Magento\Csp\Model\Mode\Data\ModeConfigured; +use Magento\Framework\App\Area; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\State; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\Store; + +/** + * @inheritDoc + */ +class ConfigManager implements ModeConfigManagerInterface +{ + /** + * @var ScopeConfigInterface + */ + private $config; + + /** + * @var Store + */ + private $storeModel; + + /** + * @var State + */ + private $state; + + /** + * @param ScopeConfigInterface $config + * @param Store $store + * @param State $state + */ + public function __construct(ScopeConfigInterface $config, Store $store, State $state) + { + $this->config = $config; + $this->storeModel = $store; + $this->state = $state; + } + + /** + * @inheritDoc + */ + public function getConfigured(): ModeConfiguredInterface + { + $area = $this->state->getAreaCode(); + if ($area === Area::AREA_ADMINHTML) { + $configArea = 'admin'; + } elseif ($area === Area::AREA_FRONTEND) { + $configArea = 'storefront'; + } else { + throw new \RuntimeException('CSP can only be configured for storefront or admin area'); + } + + $reportOnly = $this->config->isSetFlag( + 'csp/mode/' . $configArea .'/report_only', + ScopeInterface::SCOPE_STORE, + $this->storeModel->getStore() + ); + $reportUri = $this->config->getValue( + 'csp/mode/' . $configArea .'/report_uri', + ScopeInterface::SCOPE_STORE, + $this->storeModel->getStore() + ); + + return new ModeConfigured($reportOnly, !empty($reportUri) ? $reportUri : null); + } +} diff --git a/app/code/Magento/Csp/Model/Mode/Data/ModeConfigured.php b/app/code/Magento/Csp/Model/Mode/Data/ModeConfigured.php new file mode 100644 index 0000000000000..db5e362f560a7 --- /dev/null +++ b/app/code/Magento/Csp/Model/Mode/Data/ModeConfigured.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Mode\Data; + +use Magento\Csp\Api\Data\ModeConfiguredInterface; + +/** + * @inheritDoc + */ +class ModeConfigured implements ModeConfiguredInterface +{ + /** + * @var bool + */ + private $reportOnly; + + /** + * @var string|null + */ + private $reportUri; + + /** + * @param bool $reportOnly + * @param string|null $reportUri + */ + public function __construct(bool $reportOnly, ?string $reportUri) + { + $this->reportOnly = $reportOnly; + $this->reportUri = $reportUri; + } + + /** + * @inheritDoc + */ + public function isReportOnly(): bool + { + return $this->reportOnly; + } + + /** + * @inheritDoc + */ + public function getReportUri(): ?string + { + return $this->reportUri; + } +} diff --git a/app/code/Magento/Csp/Model/Policy/FetchPolicy.php b/app/code/Magento/Csp/Model/Policy/FetchPolicy.php new file mode 100644 index 0000000000000..7350cbe80aecb --- /dev/null +++ b/app/code/Magento/Csp/Model/Policy/FetchPolicy.php @@ -0,0 +1,283 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Policy; + +/** + * Represents a fetch directive. + */ +class FetchPolicy implements SimplePolicyInterface +{ + /** + * List of possible fetch directives. + */ + public const POLICIES = [ + 'default-src', + 'child-src', + 'connect-src', + 'font-src', + 'frame-src', + 'img-src', + 'manifest-src', + 'media-src', + 'object-src', + 'script-src', + 'style-src', + 'base-uri', + 'form-action', + 'frame-ancestors' + ]; + + /** + * @var string + */ + private $id; + + /** + * @var string[] + */ + private $hostSources; + + /** + * @var string[] + */ + private $schemeSources; + + /** + * @var bool + */ + private $selfAllowed; + + /** + * @var bool + */ + private $inlineAllowed; + + /** + * @var bool + */ + private $evalAllowed; + + /** + * @var bool + */ + private $noneAllowed; + + /** + * @var string[] + */ + private $nonceValues; + + /** + * @var string[] + */ + private $hashes; + + /** + * @var bool + */ + private $dynamicAllowed; + + /** + * @var bool + */ + private $eventHandlersAllowed; + + /** + * @param string $id + * @param bool $noneAllowed + * @param string[] $hostSources + * @param string[] $schemeSources + * @param bool $selfAllowed + * @param bool $inlineAllowed + * @param bool $evalAllowed + * @param string[] $nonceValues + * @param string[] $hashValues + * @param bool $dynamicAllowed + * @param bool $eventHandlersAllowed + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function __construct( + string $id, + bool $noneAllowed = true, + array $hostSources = [], + array $schemeSources = [], + bool $selfAllowed = false, + bool $inlineAllowed = false, + bool $evalAllowed = false, + array $nonceValues = [], + array $hashValues = [], + bool $dynamicAllowed = false, + bool $eventHandlersAllowed = false + ) { + $this->id = $id; + $this->noneAllowed = $noneAllowed; + $this->hostSources = array_unique($hostSources); + $this->schemeSources = array_unique($schemeSources); + $this->selfAllowed = $selfAllowed; + $this->inlineAllowed = $inlineAllowed; + $this->evalAllowed = $evalAllowed; + $this->nonceValues = array_unique($nonceValues); + $this->hashes = $hashValues; + $this->dynamicAllowed = $dynamicAllowed; + $this->eventHandlersAllowed = $eventHandlersAllowed; + } + + /** + * @inheritDoc + */ + public function getId(): string + { + return $this->id; + } + + /** + * Items can be loaded from given hosts. + * + * @return string[] + */ + public function getHostSources(): array + { + return $this->hostSources; + } + + /** + * Items can be loaded using following schemes. + * + * @return string[] + */ + public function getSchemeSources(): array + { + return $this->schemeSources; + } + + /** + * Items can be loaded from the same host/port as the HTML page. + * + * @return bool + */ + public function isSelfAllowed(): bool + { + return $this->selfAllowed; + } + + /** + * Items can be loaded from tags present on the original HTML page. + * + * @return bool + */ + public function isInlineAllowed(): bool + { + return $this->inlineAllowed; + } + + /** + * Allows creating items from strings. + * + * For example using "eval()" for JavaScript. + * + * @return bool + */ + public function isEvalAllowed(): bool + { + return $this->evalAllowed; + } + + /** + * Content type governed by this policy is disabled completely. + * + * @return bool + */ + public function isNoneAllowed(): bool + { + return $this->noneAllowed; + } + + /** + * @inheritDoc + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + public function getValue(): string + { + if ($this->isNoneAllowed()) { + return '\'none\''; + } else { + $sources = $this->getHostSources(); + foreach ($this->getSchemeSources() as $schemeSource) { + $sources[] = $schemeSource .':'; + } + if ($this->isSelfAllowed()) { + $sources[] = '\'self\''; + } + if ($this->isInlineAllowed()) { + $sources[] = '\'unsafe-inline\''; + } + if ($this->isEvalAllowed()) { + $sources[] = '\'unsafe-eval\''; + } + if ($this->isDynamicAllowed()) { + $sources[] = '\'strict-dynamic\''; + } + if ($this->areEventHandlersAllowed()) { + $sources[] = '\'unsafe-hashes\''; + } + foreach ($this->getNonceValues() as $nonce) { + $sources[] = '\'nonce-' .base64_encode($nonce) .'\''; + } + foreach ($this->getHashes() as $hash => $algorithm) { + $sources[]= "'$algorithm-$hash'"; + } + + return implode(' ', $sources); + } + } + + /** + * Unique cryptographically random numbers marking inline items as trusted. + * + * Contains only numbers, not encoded. + * + * @return string[] + */ + public function getNonceValues(): array + { + return $this->nonceValues; + } + + /** + * Unique hashes generated based on inline items marking them as trusted. + * + * Contains only hashes themselves, encoded into base64. Keys are the hashes, values are algorithms used. + * + * @return string[] + */ + public function getHashes(): array + { + return $this->hashes; + } + + /** + * Is trust to inline items propagated to items loaded by root items. + * + * @return bool + */ + public function isDynamicAllowed(): bool + { + return $this->dynamicAllowed; + } + + /** + * Allows to whitelist event handlers (but not javascript: URLs) with hashes. + * + * @return bool + */ + public function areEventHandlersAllowed(): bool + { + return $this->eventHandlersAllowed; + } +} diff --git a/app/code/Magento/Csp/Model/Policy/FlagPolicy.php b/app/code/Magento/Csp/Model/Policy/FlagPolicy.php new file mode 100644 index 0000000000000..041e1ca5d6229 --- /dev/null +++ b/app/code/Magento/Csp/Model/Policy/FlagPolicy.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Policy; + +/** + * Policies that are used as flags without a value. + */ +class FlagPolicy implements SimplePolicyInterface +{ + public const POLICIES = [ + 'upgrade-insecure-requests', + 'block-all-mixed-content' + ]; + + /** + * @var string + */ + private $id; + + /** + * @param string $id + */ + public function __construct(string $id) + { + $this->id = $id; + } + + /** + * @inheritDoc + */ + public function getId(): string + { + return $this->id; + } + + /** + * @inheritDoc + */ + public function getValue(): string + { + return ''; + } +} diff --git a/app/code/Magento/Csp/Model/Policy/PluginTypesPolicy.php b/app/code/Magento/Csp/Model/Policy/PluginTypesPolicy.php new file mode 100644 index 0000000000000..4f34f49bfffe7 --- /dev/null +++ b/app/code/Magento/Csp/Model/Policy/PluginTypesPolicy.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Policy; + +/** + * Governs allowed plugin mime-types. + */ +class PluginTypesPolicy implements SimplePolicyInterface +{ + /** + * @var string[] + */ + private $types; + + /** + * @param string[] $types + */ + public function __construct(array $types) + { + if (!$types) { + throw new \RuntimeException('PluginTypePolicy must be given at least 1 type.'); + } + $this->types = array_unique($types); + } + + /** + * @inheritDoc + */ + public function getId(): string + { + return 'plugin-types'; + } + + /** + * @inheritDoc + */ + public function getValue(): string + { + return implode(' ', $this->getTypes()); + } + + /** + * Mime types of allowed plugins. + * + * Types like "application/x-shockwave-flash", "application/x-java-applet". + * Will only work if object-src directive != "none". + * + * @return string[] + */ + public function getTypes(): array + { + return $this->types; + } +} diff --git a/app/code/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRenderer.php b/app/code/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRenderer.php new file mode 100644 index 0000000000000..14ae23eb3fe37 --- /dev/null +++ b/app/code/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRenderer.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Policy\Renderer; + +use Magento\Csp\Api\Data\ModeConfiguredInterface; +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Api\ModeConfigManagerInterface; +use Magento\Csp\Api\PolicyRendererInterface; +use Magento\Csp\Model\Policy\SimplePolicyInterface; +use Magento\Framework\App\Response\HttpInterface as HttpResponse; + +/** + * Renders a simple policy as a "Content-Security-Policy" header. + */ +class SimplePolicyHeaderRenderer implements PolicyRendererInterface +{ + /** + * @var ModeConfigManagerInterface + */ + private $modeConfig; + + /** + * @param ModeConfigManagerInterface $modeConfig + */ + public function __construct(ModeConfigManagerInterface $modeConfig) + { + $this->modeConfig = $modeConfig; + } + + /** + * @inheritDoc + */ + public function render(PolicyInterface $policy, HttpResponse $response): void + { + /** @var SimplePolicyInterface $policy */ + $config = $this->modeConfig->getConfigured(); + if ($config->isReportOnly()) { + $header = 'Content-Security-Policy-Report-Only'; + } else { + $header = 'Content-Security-Policy'; + } + $value = $policy->getId() .' ' .$policy->getValue() .';'; + if ($config->getReportUri()) { + $reportToData = [ + 'group' => 'report-endpoint', + 'max_age' => 10886400, + 'endpoints' => [ + ['url' => $config->getReportUri()] + ] + ]; + $value .= ' report-uri ' .$config->getReportUri() .';'; + $value .= ' report-to '. $reportToData['group'] .';'; + $response->setHeader('Report-To', json_encode($reportToData), true); + } + $response->setHeader($header, $value, false); + } + + /** + * @inheritDoc + */ + public function canRender(PolicyInterface $policy): bool + { + return true; + } +} diff --git a/app/code/Magento/Csp/Model/Policy/SandboxPolicy.php b/app/code/Magento/Csp/Model/Policy/SandboxPolicy.php new file mode 100644 index 0000000000000..33e3b06f56aec --- /dev/null +++ b/app/code/Magento/Csp/Model/Policy/SandboxPolicy.php @@ -0,0 +1,278 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Policy; + +/** + * "sandbox" directive enables sandbox mode for requested pages limiting their functionality. + * + * Works the same as "sandbox" attribute for iframes but for the main document. + */ +class SandboxPolicy implements SimplePolicyInterface +{ + /** + * @var bool + */ + private $formAllowed; + + /** + * @var bool + */ + private $modalsAllowed; + + /** + * @var bool + */ + private $orientationLockAllowed; + + /** + * @var bool + */ + private $pointerLockAllowed; + + /** + * @var bool + */ + private $popupsAllowed; + + /** + * @var bool + */ + private $popupsToEscapeSandboxAllowed; + + /** + * @var bool + */ + private $presentationAllowed; + + /** + * @var bool + */ + private $sameOriginAllowed; + + /** + * @var bool + */ + private $scriptsAllowed; + + /** + * @var bool + */ + private $topNavigationAllowed; + + /** + * @var bool + */ + private $topNavigationByUserActivationAllowed; + + /** + * @param bool $formAllowed + * @param bool $modalsAllowed + * @param bool $orientationLockAllowed + * @param bool $pointerLockAllowed + * @param bool $popupsAllowed + * @param bool $popupsToEscapeSandboxAllowed + * @param bool $presentationAllowed + * @param bool $sameOriginAllowed + * @param bool $scriptsAllowed + * @param bool $topNavigationAllowed + * @param bool $topNavigationByUserActivationAllowed + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function __construct( + bool $formAllowed, + bool $modalsAllowed, + bool $orientationLockAllowed, + bool $pointerLockAllowed, + bool $popupsAllowed, + bool $popupsToEscapeSandboxAllowed, + bool $presentationAllowed, + bool $sameOriginAllowed, + bool $scriptsAllowed, + bool $topNavigationAllowed, + bool $topNavigationByUserActivationAllowed + ) { + $this->formAllowed = $formAllowed; + $this->modalsAllowed = $modalsAllowed; + $this->orientationLockAllowed = $orientationLockAllowed; + $this->pointerLockAllowed = $pointerLockAllowed; + $this->popupsAllowed = $popupsAllowed; + $this->popupsToEscapeSandboxAllowed = $popupsToEscapeSandboxAllowed; + $this->presentationAllowed = $presentationAllowed; + $this->sameOriginAllowed = $sameOriginAllowed; + $this->scriptsAllowed = $scriptsAllowed; + $this->topNavigationAllowed = $topNavigationAllowed; + $this->topNavigationByUserActivationAllowed = $topNavigationByUserActivationAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isFormAllowed(): bool + { + return $this->formAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isModalsAllowed(): bool + { + return $this->modalsAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isOrientationLockAllowed(): bool + { + return $this->orientationLockAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isPointerLockAllowed(): bool + { + return $this->pointerLockAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isPopupsAllowed(): bool + { + return $this->popupsAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isPopupsToEscapeSandboxAllowed(): bool + { + return $this->popupsToEscapeSandboxAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isPresentationAllowed(): bool + { + return $this->presentationAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isSameOriginAllowed(): bool + { + return $this->sameOriginAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isScriptsAllowed(): bool + { + return $this->scriptsAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isTopNavigationAllowed(): bool + { + return $this->topNavigationAllowed; + } + + /** + * Sandbox option. + * + * @return bool + */ + public function isTopNavigationByUserActivationAllowed(): bool + { + return $this->topNavigationByUserActivationAllowed; + } + + /** + * @inheritDoc + */ + public function getId(): string + { + return 'sandbox'; + } + + /** + * @inheritDoc + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + public function getValue(): string + { + $allowed = []; + + if ($this->isFormAllowed()) { + $allowed[] = 'allow-forms'; + } + if ($this->isModalsAllowed()) { + $allowed[] = 'allow-modals'; + } + if ($this->isOrientationLockAllowed()) { + $allowed[] = 'allow-orientation-lock'; + } + if ($this->isPointerLockAllowed()) { + $allowed[] = 'allow-pointer-lock'; + } + if ($this->isPopupsAllowed()) { + $allowed[] = 'allow-popups'; + } + if ($this->isPopupsToEscapeSandboxAllowed()) { + $allowed[] = 'allow-popups-to-escape-sandbox'; + } + if ($this->isPresentationAllowed()) { + $allowed[] = 'allow-presentation'; + } + if ($this->isSameOriginAllowed()) { + $allowed[] = 'allow-same-origin'; + } + if ($this->isScriptsAllowed()) { + $allowed[] = 'allow-scripts'; + } + if ($this->isTopNavigationAllowed()) { + $allowed[] = 'allow-top-navigation'; + } + if ($this->isTopNavigationByUserActivationAllowed()) { + $allowed[] = 'allow-top-navigation-by-user-activation'; + } + + if (!$allowed) { + throw new \RuntimeException('At least 1 option must be selected'); + } + return implode(' ', $allowed); + } +} diff --git a/app/code/Magento/Csp/Model/Policy/SimplePolicyInterface.php b/app/code/Magento/Csp/Model/Policy/SimplePolicyInterface.php new file mode 100644 index 0000000000000..aea28a1469de5 --- /dev/null +++ b/app/code/Magento/Csp/Model/Policy/SimplePolicyInterface.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model\Policy; + +use Magento\Csp\Api\Data\PolicyInterface; + +/** + * Simple policy that is represented by the default prefix and an ID - string value combination. + */ +interface SimplePolicyInterface extends PolicyInterface +{ + +} diff --git a/app/code/Magento/Csp/Model/PolicyRendererPool.php b/app/code/Magento/Csp/Model/PolicyRendererPool.php new file mode 100644 index 0000000000000..5bce191e3a878 --- /dev/null +++ b/app/code/Magento/Csp/Model/PolicyRendererPool.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Model; + +use Magento\Csp\Api\Data\PolicyInterface; +use Magento\Csp\Api\PolicyRendererInterface; + +/** + * Pool of policy renderers. + */ +class PolicyRendererPool +{ + /** + * @var PolicyRendererInterface[] + */ + private $renderers; + + /** + * @param PolicyRendererInterface[] $renderers + */ + public function __construct(array $renderers) + { + $this->renderers = $renderers; + } + + /** + * Get renderer for the given policy. + * + * @param PolicyInterface $forPolicy + * @return PolicyRendererInterface + * @throws \RuntimeException When it's impossible to find a proper renderer. + */ + public function getRenderer(PolicyInterface $forPolicy): PolicyRendererInterface + { + foreach ($this->renderers as $renderer) { + if ($renderer->canRender($forPolicy)) { + return $renderer; + } + } + + throw new \RuntimeException(sprintf('Failed to find a renderer for policy #%s', $forPolicy->getId())); + } +} diff --git a/app/code/Magento/Csp/Observer/Render.php b/app/code/Magento/Csp/Observer/Render.php new file mode 100644 index 0000000000000..2b88d685f3fe4 --- /dev/null +++ b/app/code/Magento/Csp/Observer/Render.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Csp\Observer; + +use Magento\Csp\Api\CspRendererInterface; +use Magento\Framework\Event\Observer; +use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\App\Response\HttpInterface as HttpResponse; + +/** + * Adds CSP rendering after HTTP response is generated. + */ +class Render implements ObserverInterface +{ + /** + * @var CspRendererInterface + */ + private $cspRenderer; + + /** + * @param CspRendererInterface $cspRenderer + */ + public function __construct(CspRendererInterface $cspRenderer) + { + $this->cspRenderer = $cspRenderer; + } + + /** + * @inheritDoc + */ + public function execute(Observer $observer) + { + /** @var HttpResponse $response */ + $response = $observer->getEvent()->getData('response'); + + $this->cspRenderer->render($response); + } +} diff --git a/app/code/Magento/Csp/README.md b/app/code/Magento/Csp/README.md new file mode 100644 index 0000000000000..47f0f196becd5 --- /dev/null +++ b/app/code/Magento/Csp/README.md @@ -0,0 +1,2 @@ +Magento_Csp implements Content Security Policies for Magento. Allows CSP configuration for Merchants, +provides a way for extension and theme developers to configure CSP headers for their extensions. diff --git a/app/code/Magento/Csp/composer.json b/app/code/Magento/Csp/composer.json new file mode 100644 index 0000000000000..cd8a47a92159d --- /dev/null +++ b/app/code/Magento/Csp/composer.json @@ -0,0 +1,25 @@ +{ + "name": "magento/module-csp", + "description": "CSP module enables Content Security Policies for Magento", + "config": { + "sort-packages": true + }, + "require": { + "php": "~7.1.3||~7.2.0||~7.3.0", + "magento/framework": "*", + "magento/module-store": "*" + }, + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\Csp\\": "" + } + } +} diff --git a/app/code/Magento/Csp/etc/adminhtml/events.xml b/app/code/Magento/Csp/etc/adminhtml/events.xml new file mode 100644 index 0000000000000..f81031d2a779a --- /dev/null +++ b/app/code/Magento/Csp/etc/adminhtml/events.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> + <event name="controller_front_send_response_before"> + <observer name="csp_render" instance="Magento\Csp\Observer\Render" /> + </event> +</config> diff --git a/app/code/Magento/Csp/etc/config.xml b/app/code/Magento/Csp/etc/config.xml new file mode 100644 index 0000000000000..e45f6b223ed22 --- /dev/null +++ b/app/code/Magento/Csp/etc/config.xml @@ -0,0 +1,21 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> + <default> + <csp> + <mode> + <storefront> + <report_only>1</report_only> + </storefront> + <admin> + <report_only>1</report_only> + </admin> + </mode> + </csp> + </default> +</config> diff --git a/app/code/Magento/Csp/etc/csp_whitelist.xsd b/app/code/Magento/Csp/etc/csp_whitelist.xsd new file mode 100644 index 0000000000000..e68e596e21c79 --- /dev/null +++ b/app/code/Magento/Csp/etc/csp_whitelist.xsd @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Structure description for csp_whitelist.xml configuration files. + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> + <xs:element name="csp_whitelist" type="cspWhitelistType" /> + + <xs:complexType name="cspWhitelistType"> + <xs:sequence> + <xs:element name="policies" type="policiesType" minOccurs="1" maxOccurs="1"/> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="policiesType"> + <xs:sequence> + <xs:element name="policy" type="policyType" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="policyType"> + <xs:sequence> + <xs:element name="values" type="valuesType" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + <xs:attribute name="id" type="xs:string" use="required" /> + </xs:complexType> + + <xs:complexType name="valuesType"> + <xs:sequence> + <xs:element name="value" type="valueType" minOccurs="1" maxOccurs="unbounded" /> + </xs:sequence> + </xs:complexType> + <xs:complexType name="valueType"> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute type="xs:string" name="id" use="required" /> + <xs:attribute type="cspValueType" name="type" use="required" /> + <xs:attribute type="xs:string" name="algorithm" use="optional" /> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + <xs:simpleType name="cspValueType"> + <xs:restriction base="xs:string"> + <xs:enumeration value="host" /> + <xs:enumeration value="hash" /> + </xs:restriction> + </xs:simpleType> +</xs:schema> diff --git a/app/code/Magento/Csp/etc/di.xml b/app/code/Magento/Csp/etc/di.xml new file mode 100644 index 0000000000000..0804f6d579137 --- /dev/null +++ b/app/code/Magento/Csp/etc/di.xml @@ -0,0 +1,50 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <preference for="Magento\Csp\Api\CspRendererInterface" type="Magento\Csp\Model\CspRenderer" /> + <type name="Magento\Csp\Model\PolicyRendererPool"> + <arguments> + <argument name="renderers" xsi:type="array"> + <item name="header" xsi:type="object">Magento\Csp\Model\Policy\Renderer\SimplePolicyHeaderRenderer</item> + </argument> + </arguments> + </type> + <preference for="Magento\Csp\Api\PolicyCollectorInterface" type="Magento\Csp\Model\CompositePolicyCollector" /> + <type name="Magento\Csp\Model\CompositePolicyCollector"> + <arguments> + <argument name="collectors" xsi:type="array"> + <item name="config" xsi:type="object">Magento\Csp\Model\Collector\ConfigCollector</item> + <item name="csp_whitelist" xsi:type="object">Magento\Csp\Model\Collector\CspWhitelistXmlCollector</item> + </argument> + <argument name="mergers" xsi:type="array"> + <item name="fetch" xsi:type="object">Magento\Csp\Model\Collector\FetchPolicyMerger</item> + <item name="flag" xsi:type="object">Magento\Csp\Model\Collector\FlagPolicyMerger</item> + <item name="plugins" xsi:type="object">Magento\Csp\Model\Collector\PluginTypesPolicyMerger</item> + <item name="sandbox" xsi:type="object">Magento\Csp\Model\Collector\SandboxPolicyMerger</item> + </argument> + </arguments> + </type> + <type name="Magento\Csp\Model\Collector\Config\PolicyReaderPool"> + <arguments> + <argument name="readers" xsi:type="array"> + <item name="fetch" xsi:type="object">Magento\Csp\Model\Collector\Config\FetchPolicyReader</item> + <item name="plugin_types" xsi:type="object">Magento\Csp\Model\Collector\Config\PluginTypesPolicyReader</item> + <item name="sandbox" xsi:type="object">Magento\Csp\Model\Collector\Config\SandboxPolicyReader</item> + <item name="flag" xsi:type="object">Magento\Csp\Model\Collector\Config\FlagPolicyReader</item> + </argument> + </arguments> + </type> + <preference for="Magento\Csp\Api\ModeConfigManagerInterface" type="Magento\Csp\Model\Mode\ConfigManager" /> + <type name="Magento\Csp\Model\Collector\CspWhitelistXml\Reader"> + <arguments> + <argument name="converter" xsi:type="object">Magento\Csp\Model\Collector\CspWhitelistXml\Converter</argument> + <argument name="schemaLocator" xsi:type="object">Magento\Csp\Model\Collector\CspWhitelistXml\SchemaLocator</argument> + <argument name="fileName" xsi:type="string">csp_whitelist.xml</argument> + </arguments> + </type> +</config> diff --git a/app/code/Magento/Csp/etc/frontend/events.xml b/app/code/Magento/Csp/etc/frontend/events.xml new file mode 100644 index 0000000000000..f81031d2a779a --- /dev/null +++ b/app/code/Magento/Csp/etc/frontend/events.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> + <event name="controller_front_send_response_before"> + <observer name="csp_render" instance="Magento\Csp\Observer\Render" /> + </event> +</config> diff --git a/app/code/Magento/Csp/etc/module.xml b/app/code/Magento/Csp/etc/module.xml new file mode 100644 index 0000000000000..e99608ef13f15 --- /dev/null +++ b/app/code/Magento/Csp/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_Csp" /> +</config> diff --git a/app/code/Magento/Csp/registration.php b/app/code/Magento/Csp/registration.php new file mode 100644 index 0000000000000..337cfbc23f2de --- /dev/null +++ b/app/code/Magento/Csp/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Csp', __DIR__); diff --git a/app/code/Magento/CurrencySymbol/Block/Adminhtml/System/Currency/Rate/Matrix.php b/app/code/Magento/CurrencySymbol/Block/Adminhtml/System/Currency/Rate/Matrix.php index e20054a5a8084..ee94195a29cc7 100644 --- a/app/code/Magento/CurrencySymbol/Block/Adminhtml/System/Currency/Rate/Matrix.php +++ b/app/code/Magento/CurrencySymbol/Block/Adminhtml/System/Currency/Rate/Matrix.php @@ -11,6 +11,9 @@ */ namespace Magento\CurrencySymbol\Block\Adminhtml\System\Currency\Rate; +/** + * Manage currency block + */ class Matrix extends \Magento\Backend\Block\Template { /** @@ -105,7 +108,7 @@ protected function _prepareRates($array) foreach ($array as $key => $rate) { foreach ($rate as $code => $value) { $parts = explode('.', $value); - if (sizeof($parts) == 2) { + if (count($parts) == 2) { $parts[1] = str_pad(rtrim($parts[1], 0), 4, '0', STR_PAD_RIGHT); $array[$key][$code] = join('.', $parts); } elseif ($value > 0) { diff --git a/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/FetchRates.php b/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/FetchRates.php index a6e29db1e1c40..9140807121b83 100644 --- a/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/FetchRates.php +++ b/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currency/FetchRates.php @@ -3,29 +3,73 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\CurrencySymbol\Controller\Adminhtml\System\Currency; +use Magento\Backend\App\Action\Context; +use Magento\Backend\Model\View\Result\Redirect; +use Magento\Backend\Model\Session as BackendSession; +use Magento\CurrencySymbol\Controller\Adminhtml\System\Currency as CurrencyAction; +use Magento\Directory\Model\Currency\Import\Factory as CurrencyImportFactory; +use Magento\Directory\Model\Currency\Import\ImportInterface as CurrencyImport; use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Escaper; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Controller\ResultFactory; -use Magento\CurrencySymbol\Controller\Adminhtml\System\Currency as CurrencyAction; +use Magento\Framework\Registry; +use Exception; /** - * Class FetchRates + * Fetch rates controller. */ class FetchRates extends CurrencyAction implements HttpGetActionInterface, HttpPostActionInterface { + /** + * @var BackendSession + */ + private $backendSession; + + /** + * @var CurrencyImportFactory + */ + private $currencyImportFactory; + + /** + * @var Escaper + */ + private $escaper; + + /** + * @param Context $context + * @param Registry $coreRegistry + * @param BackendSession|null $backendSession + * @param CurrencyImportFactory|null $currencyImportFactory + * @param Escaper|null $escaper + */ + public function __construct( + Context $context, + Registry $coreRegistry, + ?BackendSession $backendSession = null, + ?CurrencyImportFactory $currencyImportFactory = null, + ?Escaper $escaper = null + ) { + parent::__construct($context, $coreRegistry); + $this->backendSession = $backendSession ?: ObjectManager::getInstance()->get(BackendSession::class); + $this->currencyImportFactory = $currencyImportFactory ?: ObjectManager::getInstance() + ->get(CurrencyImportFactory::class); + $this->escaper = $escaper ?: ObjectManager::getInstance()->get(Escaper::class); + } + /** * Fetch rates action * - * @return \Magento\Backend\Model\View\Result\Redirect + * @return Redirect */ public function execute() { - /** @var \Magento\Backend\Model\Session $backendSession */ - $backendSession = $this->_objectManager->get(\Magento\Backend\Model\Session::class); try { $service = $this->getRequest()->getParam('rate_services'); $this->_getSession()->setCurrencyRateService($service); @@ -33,10 +77,9 @@ public function execute() throw new LocalizedException(__('The Import Service is incorrect. Verify the service and try again.')); } try { - /** @var \Magento\Directory\Model\Currency\Import\ImportInterface $importModel */ - $importModel = $this->_objectManager->get(\Magento\Directory\Model\Currency\Import\Factory::class) - ->create($service); - } catch (\Exception $e) { + /** @var CurrencyImport $importModel */ + $importModel = $this->currencyImportFactory->create($service); + } catch (Exception $e) { throw new LocalizedException( __("The import model can't be initialized. Verify the model and try again.") ); @@ -45,7 +88,8 @@ public function execute() $errors = $importModel->getMessages(); if (count($errors) > 0) { foreach ($errors as $error) { - $this->messageManager->addWarningMessage($error); + $escapedError = $this->escaper->escapeHtml($error); + $this->messageManager->addWarningMessage($escapedError); } $this->messageManager->addWarningMessage( __('Click "Save" to apply the rates we found.') @@ -54,12 +98,12 @@ public function execute() $this->messageManager->addSuccessMessage(__('Click "Save" to apply the rates we found.')); } - $backendSession->setRates($rates); - } catch (\Exception $e) { + $this->backendSession->setRates($rates); + } catch (Exception $e) { $this->messageManager->addErrorMessage($e->getMessage()); } - /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ + /** @var Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); return $resultRedirect->setPath('adminhtml/*/'); } diff --git a/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/Save.php b/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/Save.php index f77976cc9e2f2..07c7c553ac792 100644 --- a/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/Save.php +++ b/app/code/Magento/CurrencySymbol/Controller/Adminhtml/System/Currencysymbol/Save.php @@ -5,37 +5,68 @@ */ namespace Magento\CurrencySymbol\Controller\Adminhtml\System\Currencysymbol; -use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; +use Magento\Backend\App\Action; +use Magento\Backend\Model\View\Result\Redirect; +use Magento\CurrencySymbol\Controller\Adminhtml\System\Currencysymbol as CurrencysymbolController; +use Magento\CurrencySymbol\Model\System\CurrencysymbolFactory; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\Filter\FilterManager; /** - * Class Save + * Controller to save currency symbol */ -class Save extends \Magento\CurrencySymbol\Controller\Adminhtml\System\Currencysymbol implements HttpPostActionInterface +class Save extends CurrencysymbolController implements HttpPostActionInterface { + /** + * @var FilterManager + */ + private $filterManager; + + /** + * @var CurrencysymbolFactory + */ + private $currencySymbolFactory; + + /** + * @param Action\Context $context + * @param FilterManager $filterManager + * @param CurrencysymbolFactory $currencySymbolFactory + */ + public function __construct( + Action\Context $context, + FilterManager $filterManager, + CurrencysymbolFactory $currencySymbolFactory + ) { + parent::__construct($context); + $this->filterManager = $filterManager; + $this->currencySymbolFactory = $currencySymbolFactory; + } + /** * Save custom Currency symbol * - * @return void + * @return ResultInterface */ public function execute() { + /** @var Redirect $resultRedirect */ + $resultRedirect = $this->resultRedirectFactory->create(); $symbolsDataArray = $this->getRequest()->getParam('custom_currency_symbol', null); if (is_array($symbolsDataArray)) { foreach ($symbolsDataArray as &$symbolsData) { - /** @var $filterManager \Magento\Framework\Filter\FilterManager */ - $filterManager = $this->_objectManager->get(\Magento\Framework\Filter\FilterManager::class); - $symbolsData = $filterManager->stripTags($symbolsData); + $symbolsData = $this->filterManager->stripTags($symbolsData); } } try { - $this->_objectManager->create(\Magento\CurrencySymbol\Model\System\Currencysymbol::class) - ->setCurrencySymbolsData($symbolsDataArray); + $currencySymbol = $this->currencySymbolFactory->create(); + $currencySymbol->setCurrencySymbolsData($symbolsDataArray); $this->messageManager->addSuccessMessage(__('You applied the custom currency symbols.')); } catch (\Exception $e) { $this->messageManager->addErrorMessage($e->getMessage()); } - $this->getResponse()->setRedirect($this->_redirect->getRedirectUrl($this->getUrl('*'))); + return $resultRedirect->setPath('*'); } } diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AdminCurrencyRatesActionGroup.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AdminCurrencyRatesActionGroup.xml index 6b8a93ef3542d..7f2a9846f3fd0 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AdminCurrencyRatesActionGroup.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AdminCurrencyRatesActionGroup.xml @@ -8,15 +8,12 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminSetCurrencyRatesActionGroup"> + <actionGroup name="AdminImportCurrencyRatesActionGroup"> <arguments> - <argument name="firstCurrency" type="string" defaultValue="USD"/> - <argument name="secondCurrency" type="string" defaultValue="EUR"/> - <argument name="rate" type="string" defaultValue="0.5"/> + <argument name="rateService" type="string" defaultValue="Fixer.io"/> </arguments> - <fillField selector="{{AdminCurrencyRatesSection.currencyRate(firstCurrency, secondCurrency)}}" userInput="{{rate}}" stepKey="setCurrencyRate"/> - <click selector="{{AdminCurrencyRatesSection.saveCurrencyRates}}" stepKey="clickSaveCurrencyRates"/> - <waitForPageLoad stepKey="waitForSave"/> - <see selector="{{AdminMessagesSection.success}}" userInput="{{AdminSaveCurrencyRatesMessageData.success}}" stepKey="seeSuccessMessage"/> + <selectOption selector="{{AdminCurrencyRatesSection.rateService}}" userInput="{{rateService}}" stepKey="selectRateService"/> + <click selector="{{AdminCurrencyRatesSection.import}}" stepKey="clickImport"/> + <waitForElementVisible selector="{{AdminCurrencyRatesSection.oldRate}}" stepKey="waitForOldRateVisible"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AdminSaveCurrencyRatesActionGroup.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AdminSaveCurrencyRatesActionGroup.xml new file mode 100644 index 0000000000000..82401eea34aca --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AdminSaveCurrencyRatesActionGroup.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSaveCurrencyRatesActionGroup"> + <click selector="{{AdminCurrencyRatesSection.saveCurrencyRates}}" stepKey="clickSaveCurrencyRates"/> + <waitForPageLoad stepKey="waitForSave"/> + <see selector="{{AdminMessagesSection.success}}" userInput="All valid rates have been saved." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AdminSetCurrencyRatesActionGroup.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AdminSetCurrencyRatesActionGroup.xml new file mode 100644 index 0000000000000..6b8a93ef3542d --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AdminSetCurrencyRatesActionGroup.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="AdminSetCurrencyRatesActionGroup"> + <arguments> + <argument name="firstCurrency" type="string" defaultValue="USD"/> + <argument name="secondCurrency" type="string" defaultValue="EUR"/> + <argument name="rate" type="string" defaultValue="0.5"/> + </arguments> + <fillField selector="{{AdminCurrencyRatesSection.currencyRate(firstCurrency, secondCurrency)}}" userInput="{{rate}}" stepKey="setCurrencyRate"/> + <click selector="{{AdminCurrencyRatesSection.saveCurrencyRates}}" stepKey="clickSaveCurrencyRates"/> + <waitForPageLoad stepKey="waitForSave"/> + <see selector="{{AdminMessagesSection.success}}" userInput="{{AdminSaveCurrencyRatesMessageData.success}}" stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/StorefrontCurrencyRatesActionGroup.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/StorefrontCurrencyRatesActionGroup.xml deleted file mode 100644 index 61a6123b1a7af..0000000000000 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/StorefrontCurrencyRatesActionGroup.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontSwitchCurrency"> - <arguments> - <argument name="currency" type="string" defaultValue="EUR"/> - </arguments> - <click selector="{{StorefrontSwitchCurrencyRatesSection.currencyTrigger}}" stepKey="openTrigger"/> - <waitForElementVisible selector="{{StorefrontSwitchCurrencyRatesSection.currency(currency)}}" stepKey="waitForCurrency"/> - <click selector="{{StorefrontSwitchCurrencyRatesSection.currency(currency)}}" stepKey="chooseCurrency"/> - <see selector="{{StorefrontSwitchCurrencyRatesSection.selectedCurrency}}" userInput="{{currency}}" stepKey="seeSelectedCurrency"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/StorefrontSwitchCurrencyActionGroup.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/StorefrontSwitchCurrencyActionGroup.xml new file mode 100644 index 0000000000000..2f6d1df102d06 --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/StorefrontSwitchCurrencyActionGroup.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="StorefrontSwitchCurrencyActionGroup"> + <arguments> + <argument name="currency" type="string" defaultValue="EUR"/> + </arguments> + <click selector="{{StorefrontSwitchCurrencyRatesSection.currencyToggle}}" stepKey="openToggle"/> + <waitForElementVisible selector="{{StorefrontSwitchCurrencyRatesSection.currency(currency)}}" stepKey="waitForCurrency"/> + <click selector="{{StorefrontSwitchCurrencyRatesSection.currency(currency)}}" stepKey="chooseCurrency"/> + <see selector="{{StorefrontSwitchCurrencyRatesSection.selectedCurrency}}" userInput="{{currency}}" stepKey="seeSelectedCurrency"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Section/AdminCurrencyRatesSection.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Section/AdminCurrencyRatesSection.xml index a5799356eb459..bc80a51c41c47 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Section/AdminCurrencyRatesSection.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Section/AdminCurrencyRatesSection.xml @@ -12,6 +12,7 @@ <element name="import" type="button" selector="//button[@title='Import']"/> <element name="saveCurrencyRates" type="button" selector="//button[@title='Save Currency Rates']"/> <element name="oldRate" type="text" selector="//div[contains(@class, 'admin__field-note') and contains(text(), 'Old rate:')]/strong"/> + <element name="rateService" type="select" selector="#rate_services"/> <element name="currencyRate" type="input" selector="input[name='rate[{{fistCurrency}}][{{secondCurrency}}]']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Section/StorefrontSwitchCurrencyRatesSection.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Section/StorefrontSwitchCurrencyRatesSection.xml index e69823ad68e0e..43512796a134d 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Section/StorefrontSwitchCurrencyRatesSection.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Section/StorefrontSwitchCurrencyRatesSection.xml @@ -9,7 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontSwitchCurrencyRatesSection"> - <element name="currencyTrigger" type="select" selector="#switcher-currency-trigger" timeout="30"/> + <element name="currencyToggle" type="select" selector="#switcher-currency-trigger" timeout="30"/> <element name="currency" type="button" selector="//div[@id='switcher-currency-trigger']/following-sibling::ul//a[contains(text(), '{{currency}}')]" parameterized="true" timeout="10"/> <element name="selectedCurrency" type="text" selector="#switcher-currency-trigger span"/> </section> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminCurrencyConverterAPIConfigurationTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminCurrencyConverterAPIConfigurationTest.xml new file mode 100644 index 0000000000000..6232f59bb839a --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminCurrencyConverterAPIConfigurationTest.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCurrencyConverterAPIConfigurationTest"> + <annotations> + <features value="CurrencySymbol"/> + <stories value="Currency Rates"/> + <title value="Currency Converter API configuration"/> + <description value="Currency Converter API configuration"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-19272"/> + <useCaseId value="MAGETWO-94919"/> + <group value="currency"/> + <skip> + <issueId value="MQE-1578"/> + </skip> + </annotations> + <before> + <!--Set currency allow config--> + <magentoCLI command="config:set currency/options/allow RHD,CHW,CHE,AMD,EUR,USD" stepKey="setCurrencyAllow"/> + <!--TODO: Add Api key--> + <magentoCLI command="cache:flush" stepKey="clearCache"/> + <!--Create product--> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!--Set currency allow previous config--> + <magentoCLI command="config:set currency/options/allow EUR,USD" stepKey="setCurrencyAllow"/> + <!--Delete created data--> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!--Import rates from Currency Converter API--> + <amOnPage url="{{AdminCurrencyRatesPage.url}}" stepKey="onCurrencyRatePage"/> + <actionGroup ref="AdminImportCurrencyRatesActionGroup" stepKey="importCurrencyRates"> + <argument name="rateService" value="Currency Converter API"/> + </actionGroup> + <see selector="{{AdminMessagesSection.warningMessage}}" userInput='Click "Save" to apply the rates we found.' stepKey="seeImportMessage"/> + <see selector="{{AdminMessagesSection.warningMessage}}" userInput="We can't retrieve a rate from https://free.currconv.com for CHE." stepKey="seeWarningMessageForCHE"/> + <see selector="{{AdminMessagesSection.warningMessage}}" userInput="We can't retrieve a rate from https://free.currconv.com for RHD." stepKey="seeWarningMessageForRHD"/> + <see selector="{{AdminMessagesSection.warningMessage}}" userInput="We can't retrieve a rate from https://free.currconv.com for CHW." stepKey="seeWarningMessageForCHW"/> + <actionGroup ref="AdminSaveCurrencyRatesActionGroup" stepKey="saveCurrencyRates"/> + <see selector="{{AdminMessagesSection.warningMessage}}" userInput='Please correct the input data for "USD => CHE" rate' stepKey="seeCHEMessageAfterSave"/> + <see selector="{{AdminMessagesSection.warningMessage}}" userInput='Please correct the input data for "USD => RHD" rate' stepKey="seeRHDMessageAfterSave"/> + <see selector="{{AdminMessagesSection.warningMessage}}" userInput='Please correct the input data for "USD => CHW" rate' stepKey="seeCHWMessageAfterSave"/> + <!--Go to the Storefront and check currency rates--> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + <actionGroup ref="StorefrontSwitchCurrencyActionGroup" stepKey="switchAMDCurrency"> + <argument name="currency" value="AMD"/> + </actionGroup> + <see selector="{{StorefrontCategoryMainSection.productPrice}}" userInput="AMD" stepKey="seeAMDInPrice"/> + <actionGroup ref="StorefrontSwitchCurrencyActionGroup" stepKey="switchEURCurrency"> + <argument name="currency" value="EUR"/> + </actionGroup> + <see selector="{{StorefrontCategoryMainSection.productPrice}}" userInput="€" stepKey="seeEURInPrice"/> + <!--Set allowed currencies greater then 10--> + <magentoCLI command="config:set currency/options/allow RHD,CHW,YER,ZMK,CHE,EUR,USD,AMD,RUB,DZD,ARS,AWG" stepKey="setCurrencyAllow"/> + <magentoCLI command="cache:flush" stepKey="clearCache"/> + <!--Import rates from Currency Converter API with currencies greater then 10--> + <amOnPage url="{{AdminCurrencyRatesPage.url}}" stepKey="onCurrencyRatePageSecondTime"/> + <actionGroup ref="AdminImportCurrencyRatesActionGroup" stepKey="importCurrencyRatesGreaterThen10"> + <argument name="rateService" value="Currency Converter API"/> + </actionGroup> + <see selector="{{AdminMessagesSection.warningMessage}}" userInput="Too many pairs. Maximum of 10 is supported for the free version." stepKey="seeTooManyPairsMessage"/> + </test> +</tests> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml index 26fbfd394be68..100aa39613b33 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayWhenChooseThreeAllowedCurrenciesTest.xml @@ -45,10 +45,10 @@ <!--Open created product on Storefront and place for order--> <amOnPage url="{{StorefrontProductPage.url($$createNewProduct.custom_attributes[url_key]$$)}}" stepKey="goToNewProductPage"/> <waitForPageLoad stepKey="waitForNewProductPagePageLoad"/> - <actionGroup ref="StorefrontSwitchCurrency" stepKey="switchCurrency"> + <actionGroup ref="StorefrontSwitchCurrencyActionGroup" stepKey="switchCurrency"> <argument name="currency" value="RUB"/> </actionGroup> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontNewProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontNewProductPage"> <argument name="productName" value="$$createNewProduct.name$$"/> </actionGroup> <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutNewProductFromMinicart" /> @@ -61,7 +61,7 @@ </actionGroup> <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabNewOrderNumber"/> <!--Open order and check rates display in one line--> - <actionGroup ref="OpenOrderById" stepKey="openNewOrderById"> + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openNewOrderById"> <argument name="orderId" value="$grabNewOrderNumber"/> </actionGroup> <see selector="{{AdminOrderDetailsInformationSection.orderInformationTable}}" userInput="EUR / USD rate" stepKey="seeUSDandEURRate"/> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml index dc6bdf3db542e..94c9bc67e34c4 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminOrderRateDisplayedInOneLineTest.xml @@ -51,7 +51,7 @@ <!--Open created product on Storefront and place for order--> <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="goToProductPage"/> <waitForPageLoad stepKey="waitForProductPagePageLoad"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> <argument name="productName" value="$$createProduct.name$$"/> </actionGroup> <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart" /> @@ -64,7 +64,7 @@ </actionGroup> <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> <!--Open order and check rates display in one line--> - <actionGroup ref="OpenOrderById" stepKey="openOrderById"> + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openOrderById"> <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> <see selector="{{AdminOrderDetailsInformationSection.orderInformationTable}}" userInput="EUR / USD rate" stepKey="seeEURandUSDRate"/> diff --git a/app/code/Magento/CurrencySymbol/Test/Unit/Controller/Adminhtml/System/Currencysymbol/SaveTest.php b/app/code/Magento/CurrencySymbol/Test/Unit/Controller/Adminhtml/System/Currencysymbol/SaveTest.php index 06f4294ce6397..b3c69c352ac7d 100644 --- a/app/code/Magento/CurrencySymbol/Test/Unit/Controller/Adminhtml/System/Currencysymbol/SaveTest.php +++ b/app/code/Magento/CurrencySymbol/Test/Unit/Controller/Adminhtml/System/Currencysymbol/SaveTest.php @@ -5,132 +5,136 @@ */ namespace Magento\CurrencySymbol\Test\Unit\Controller\Adminhtml\System\Currencysymbol; +use Magento\Backend\Helper\Data; +use Magento\Backend\Model\View\Result\Redirect; +use Magento\Backend\Model\View\Result\RedirectFactory; +use Magento\CurrencySymbol\Controller\Adminhtml\System\Currencysymbol\Save; +use Magento\CurrencySymbol\Model\System\Currencysymbol; +use Magento\CurrencySymbol\Model\System\CurrencysymbolFactory; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\App\Response\RedirectInterface; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Filter\FilterManager; +use Magento\Framework\Message\ManagerInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; /** - * Class SaveTest + * Test ot to save currency symbol controller */ -class SaveTest extends \PHPUnit\Framework\TestCase +class SaveTest extends TestCase { /** - * @var \Magento\CurrencySymbol\Controller\Adminhtml\System\Currencysymbol\Save + * @var Save */ protected $action; /** - * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject + * @var RedirectFactory|MockObject + */ + private $resultRedirectFactory; + + /** + * @var RequestInterface|MockObject */ protected $requestMock; /** - * @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ResponseInterface|MockObject */ protected $responseMock; /** - * @var \Magento\Framework\ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ManagerInterface|MockObject */ - protected $objectManagerMock; + protected $messageManagerMock; /** - * @var \Magento\CurrencySymbol\Model\System\Currencysymbol|\PHPUnit_Framework_MockObject_MockObject + * @var RedirectInterface|MockObject */ - protected $currencySymbolMock; + protected $redirectMock; /** - * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var Data|MockObject */ - protected $messageManagerMock; + protected $helperMock; /** - * @var \Magento\Framework\App\Response\RedirectInterface|\PHPUnit_Framework_MockObject_MockObject + * @var FilterManager|MockObject */ - protected $redirectMock; + private $filterManager; /** - * @var \Magento\Backend\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + * @var CurrencysymbolFactory|MockObject */ - protected $helperMock; + private $currencySymbolFactory; /** - * @var \Magento\Framework\Filter\FilterManager|\PHPUnit_Framework_MockObject_MockObject + * @inheritdoc */ - protected $filterManagerMock; - protected function setUp() { $objectManager = new ObjectManager($this); - - $this->requestMock = $this->createMock(\Magento\Framework\App\RequestInterface::class); - - $this->helperMock = $this->createMock(\Magento\Backend\Helper\Data::class); - - $this->redirectMock = $this->createMock(\Magento\Framework\App\Response\RedirectInterface::class); - + $this->requestMock = $this->createMock(RequestInterface::class); + $this->helperMock = $this->createMock(Data::class); + $this->redirectMock = $this->createMock(RedirectInterface::class); $this->responseMock = $this->createPartialMock( - \Magento\Framework\App\ResponseInterface::class, + ResponseInterface::class, ['setRedirect', 'sendResponse'] ); - - $this->currencySymbolMock = $this->createMock(\Magento\CurrencySymbol\Model\System\Currencysymbol::class); - - $this->filterManagerMock = $this->createPartialMock( - \Magento\Framework\Filter\FilterManager::class, + $this->messageManagerMock = $this->createMock(ManagerInterface::class); + $this->resultRedirectFactory = $this->createMock(RedirectFactory::class); + $this->filterManager = $this->createPartialMock( + FilterManager::class, ['stripTags'] ); + $this->currencySymbolFactory = $this->createMock(CurrencysymbolFactory::class); - $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); - - $this->messageManagerMock = $this->createMock(\Magento\Framework\Message\ManagerInterface::class); $this->action = $objectManager->getObject( - \Magento\CurrencySymbol\Controller\Adminhtml\System\Currencysymbol\Save::class, + Save::class, [ 'request' => $this->requestMock, 'response' => $this->responseMock, - 'objectManager' => $this->objectManagerMock, 'redirect' => $this->redirectMock, 'helper' => $this->helperMock, - 'messageManager' => $this->messageManagerMock + 'messageManager' => $this->messageManagerMock, + 'resultRedirectFactory' => $this->resultRedirectFactory, + 'filterManager' => $this->filterManager, + 'currencySymbolFactory' => $this->currencySymbolFactory, ] ); } + /** + * Test to Save custom Currency symbol + */ public function testExecute() { $firstElement = 'firstElement'; $symbolsDataArray = [$firstElement]; - $redirectUrl = 'redirectUrl'; $this->requestMock->expects($this->once()) ->method('getParam') ->with('custom_currency_symbol') ->willReturn($symbolsDataArray); - $this->helperMock->expects($this->once())->method('getUrl')->with('*'); - $this->redirectMock->expects($this->once())->method('getRedirectUrl')->willReturn($redirectUrl); - - $this->currencySymbolMock->expects($this->once())->method('setCurrencySymbolsData')->with($symbolsDataArray); - $this->responseMock->expects($this->once())->method('setRedirect'); - - $this->filterManagerMock->expects($this->once()) + $currencySymbol = $this->createMock(Currencysymbol::class); + $currencySymbol->expects($this->once())->method('setCurrencySymbolsData')->with($symbolsDataArray); + $this->currencySymbolFactory->method('create')->willReturn($currencySymbol); + $this->filterManager->expects($this->once()) ->method('stripTags') ->with($firstElement) ->willReturn($firstElement); - $this->objectManagerMock->expects($this->once()) - ->method('create') - ->with(\Magento\CurrencySymbol\Model\System\Currencysymbol::class) - ->willReturn($this->currencySymbolMock); - - $this->objectManagerMock->expects($this->once()) - ->method('get') - ->with(\Magento\Framework\Filter\FilterManager::class) - ->willReturn($this->filterManagerMock); - $this->messageManagerMock->expects($this->once()) ->method('addSuccessMessage') ->with(__('You applied the custom currency symbols.')); - $this->action->execute(); + $redirect = $this->createMock(Redirect::class); + $redirect->expects($this->once())->method('setPath')->with('*')->willReturnSelf(); + $this->resultRedirectFactory->method('create')->willReturn($redirect); + + $this->assertEquals($redirect, $this->action->execute()); } } diff --git a/app/code/Magento/CurrencySymbol/registration.php b/app/code/Magento/CurrencySymbol/registration.php index 4dea4264e8b62..bf30fc7fe6a7b 100644 --- a/app/code/Magento/CurrencySymbol/registration.php +++ b/app/code/Magento/CurrencySymbol/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CurrencySymbol', __DIR__); diff --git a/app/code/Magento/Customer/Block/Account/Dashboard.php b/app/code/Magento/Customer/Block/Account/Dashboard.php index 8e0f79d45770a..547074d0bcd81 100644 --- a/app/code/Magento/Customer/Block/Account/Dashboard.php +++ b/app/code/Magento/Customer/Block/Account/Dashboard.php @@ -7,6 +7,14 @@ use Magento\Customer\Api\AccountManagementInterface; use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\Session; +use Magento\Framework\Phrase; +use Magento\Framework\View\Element\Template; +use Magento\Framework\View\Element\Template\Context; +use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\SubscriberFactory; /** * Customer dashboard block @@ -14,20 +22,20 @@ * @api * @since 100.0.2 */ -class Dashboard extends \Magento\Framework\View\Element\Template +class Dashboard extends Template { /** - * @var \Magento\Newsletter\Model\Subscriber + * @var Subscriber */ protected $subscription; /** - * @var \Magento\Customer\Model\Session + * @var Session */ protected $customerSession; /** - * @var \Magento\Newsletter\Model\SubscriberFactory + * @var SubscriberFactory */ protected $subscriberFactory; @@ -42,19 +50,17 @@ class Dashboard extends \Magento\Framework\View\Element\Template protected $customerAccountManagement; /** - * Constructor - * - * @param \Magento\Framework\View\Element\Template\Context $context - * @param \Magento\Customer\Model\Session $customerSession - * @param \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory + * @param Context $context + * @param Session $customerSession + * @param SubscriberFactory $subscriberFactory * @param CustomerRepositoryInterface $customerRepository * @param AccountManagementInterface $customerAccountManagement * @param array $data */ public function __construct( - \Magento\Framework\View\Element\Template\Context $context, - \Magento\Customer\Model\Session $customerSession, - \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory, + Context $context, + Session $customerSession, + SubscriberFactory $subscriberFactory, CustomerRepositoryInterface $customerRepository, AccountManagementInterface $customerAccountManagement, array $data = [] @@ -69,7 +75,7 @@ public function __construct( /** * Return the Customer given the customer Id stored in the session. * - * @return \Magento\Customer\Api\Data\CustomerInterface + * @return CustomerInterface */ public function getCustomer() { @@ -99,7 +105,7 @@ public function getAddressesUrl() /** * Retrieve the Url for editing the specified address. * - * @param \Magento\Customer\Api\Data\AddressInterface $address + * @param AddressInterface $address * @return string */ public function getAddressEditUrl($address) @@ -146,13 +152,14 @@ public function getWishlistUrl() /** * Retrieve the subscription object (i.e. the subscriber). * - * @return \Magento\Newsletter\Model\Subscriber + * @return Subscriber */ public function getSubscriptionObject() { if ($this->subscription === null) { - $this->subscription = - $this->_createSubscriber()->loadByCustomerId($this->customerSession->getCustomerId()); + $websiteId = (int)$this->_storeManager->getWebsite()->getId(); + $this->subscription = $this->_createSubscriber(); + $this->subscription->loadByCustomer((int)$this->getCustomer()->getId(), $websiteId); } return $this->subscription; @@ -171,7 +178,7 @@ public function getManageNewsletterUrl() /** * Retrieve subscription text, either subscribed or not. * - * @return \Magento\Framework\Phrase + * @return Phrase */ public function getSubscriptionText() { @@ -185,7 +192,7 @@ public function getSubscriptionText() /** * Retrieve the customer's primary addresses (i.e. default billing and shipping). * - * @return \Magento\Customer\Api\Data\AddressInterface[]|bool + * @return AddressInterface[]|bool */ public function getPrimaryAddresses() { @@ -230,7 +237,7 @@ public function getBackUrl() /** * Create an instance of a subscriber. * - * @return \Magento\Newsletter\Model\Subscriber + * @return Subscriber */ protected function _createSubscriber() { diff --git a/app/code/Magento/Customer/Block/Account/Dashboard/Info.php b/app/code/Magento/Customer/Block/Account/Dashboard/Info.php index 87132c3afb8bc..a48c706124c92 100644 --- a/app/code/Magento/Customer/Block/Account/Dashboard/Info.php +++ b/app/code/Magento/Customer/Block/Account/Dashboard/Info.php @@ -5,7 +5,15 @@ */ namespace Magento\Customer\Block\Account\Dashboard; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Block\Form\Register; +use Magento\Customer\Helper\Session\CurrentCustomer; +use Magento\Customer\Helper\View; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\View\Element\Template; +use Magento\Framework\View\Element\Template\Context; +use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\SubscriberFactory; /** * Dashboard Customer Info @@ -13,44 +21,44 @@ * @api * @since 100.0.2 */ -class Info extends \Magento\Framework\View\Element\Template +class Info extends Template { /** * Cached subscription object * - * @var \Magento\Newsletter\Model\Subscriber + * @var Subscriber */ protected $_subscription; /** - * @var \Magento\Newsletter\Model\SubscriberFactory + * @var SubscriberFactory */ protected $_subscriberFactory; /** - * @var \Magento\Customer\Helper\View + * @var View */ protected $_helperView; /** - * @var \Magento\Customer\Helper\Session\CurrentCustomer + * @var CurrentCustomer */ protected $currentCustomer; /** * Constructor * - * @param \Magento\Framework\View\Element\Template\Context $context - * @param \Magento\Customer\Helper\Session\CurrentCustomer $currentCustomer - * @param \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory - * @param \Magento\Customer\Helper\View $helperView + * @param Context $context + * @param CurrentCustomer $currentCustomer + * @param SubscriberFactory $subscriberFactory + * @param View $helperView * @param array $data */ public function __construct( - \Magento\Framework\View\Element\Template\Context $context, - \Magento\Customer\Helper\Session\CurrentCustomer $currentCustomer, - \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory, - \Magento\Customer\Helper\View $helperView, + Context $context, + CurrentCustomer $currentCustomer, + SubscriberFactory $subscriberFactory, + View $helperView, array $data = [] ) { $this->currentCustomer = $currentCustomer; @@ -62,7 +70,7 @@ public function __construct( /** * Returns the Magento Customer Model for this block * - * @return \Magento\Customer\Api\Data\CustomerInterface|null + * @return CustomerInterface|null */ public function getCustomer() { @@ -84,6 +92,8 @@ public function getName() } /** + * Get the url to change password + * * @return string */ public function getChangePasswordUrl() @@ -94,7 +104,7 @@ public function getChangePasswordUrl() /** * Get Customer Subscription Object Information * - * @return \Magento\Newsletter\Model\Subscriber + * @return Subscriber */ public function getSubscriptionObject() { @@ -102,7 +112,8 @@ public function getSubscriptionObject() $this->_subscription = $this->_createSubscriber(); $customer = $this->getCustomer(); if ($customer) { - $this->_subscription->loadByCustomerId($customer->getId()); + $websiteId = (int)$this->_storeManager->getWebsite()->getId(); + $this->_subscription->loadByCustomer((int)$customer->getId(), $websiteId); } } return $this->_subscription; @@ -128,12 +139,14 @@ public function getIsSubscribed() public function isNewsletterEnabled() { return $this->getLayout() - ->getBlockSingleton(\Magento\Customer\Block\Form\Register::class) + ->getBlockSingleton(Register::class) ->isNewsletterEnabled(); } /** - * @return \Magento\Newsletter\Model\Subscriber + * Create new instance of Subscriber + * + * @return Subscriber */ protected function _createSubscriber() { @@ -141,7 +154,7 @@ protected function _createSubscriber() } /** - * @return string + * @inheritdoc */ protected function _toHtml() { diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Newsletter.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Newsletter.php index 46a8dcfb28f1b..0d94a01698b31 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Newsletter.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Newsletter.php @@ -5,14 +5,30 @@ */ namespace Magento\Customer\Block\Adminhtml\Edit\Tab; +use Magento\Backend\Block\Template\Context; +use Magento\Backend\Block\Widget\Form\Generic; use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Block\Adminhtml\Form\Element\Newsletter\Subscriptions as SubscriptionsElement; use Magento\Customer\Controller\RegistryConstants; +use Magento\Customer\Model\Config\Share; +use Magento\Framework\Data\Form; +use Magento\Framework\Data\Form\Element\Fieldset; +use Magento\Framework\Data\FormFactory; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Store\Model\System\Store as SystemStore; use Magento\Ui\Component\Layout\Tabs\TabInterface; /** * Customer account form block + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Newsletter extends \Magento\Backend\Block\Widget\Form\Generic implements TabInterface +class Newsletter extends Generic implements TabInterface { /** * @var string @@ -20,7 +36,7 @@ class Newsletter extends \Magento\Backend\Block\Widget\Form\Generic implements T protected $_template = 'Magento_Customer::tab/newsletter.phtml'; /** - * @var \Magento\Newsletter\Model\SubscriberFactory + * @var SubscriberFactory */ protected $_subscriberFactory; @@ -32,37 +48,57 @@ class Newsletter extends \Magento\Backend\Block\Widget\Form\Generic implements T /** * Core registry * - * @var \Magento\Framework\Registry + * @var Registry */ protected $_coreRegistry = null; /** - * Constructor - * - * @param \Magento\Backend\Block\Template\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Framework\Data\FormFactory $formFactory - * @param \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory + * @var SystemStore + */ + private $systemStore; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @var Share + */ + private $shareConfig; + + /** + * @param Context $context + * @param Registry $registry + * @param FormFactory $formFactory + * @param SubscriberFactory $subscriberFactory * @param AccountManagementInterface $customerAccountManagement + * @param SystemStore $systemStore + * @param CustomerRepositoryInterface $customerRepository + * @param Share $shareConfig * @param array $data */ public function __construct( - \Magento\Backend\Block\Template\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Framework\Data\FormFactory $formFactory, - \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory, + Context $context, + Registry $registry, + FormFactory $formFactory, + SubscriberFactory $subscriberFactory, AccountManagementInterface $customerAccountManagement, + SystemStore $systemStore, + CustomerRepositoryInterface $customerRepository, + Share $shareConfig, array $data = [] ) { $this->_subscriberFactory = $subscriberFactory; $this->customerAccountManagement = $customerAccountManagement; parent::__construct($context, $registry, $formFactory, $data); + $this->systemStore = $systemStore; + $this->customerRepository = $customerRepository; + $this->shareConfig = $shareConfig; } /** - * Return Tab label - * - * @return \Magento\Framework\Phrase + * @inheritdoc */ public function getTabLabel() { @@ -70,9 +106,7 @@ public function getTabLabel() } /** - * Return Tab title - * - * @return \Magento\Framework\Phrase + * @inheritdoc */ public function getTabTitle() { @@ -80,9 +114,7 @@ public function getTabTitle() } /** - * Tab class getter - * - * @return string + * @inheritdoc */ public function getTabClass() { @@ -90,9 +122,7 @@ public function getTabClass() } /** - * Return URL link to Tab content - * - * @return string + * @inheritdoc */ public function getTabUrl() { @@ -100,9 +130,7 @@ public function getTabUrl() } /** - * Tab should be loaded trough Ajax call - * - * @return bool + * @inheritdoc */ public function isAjaxLoaded() { @@ -110,19 +138,15 @@ public function isAjaxLoaded() } /** - * Can show tab in tabs - * - * @return boolean + * @inheritdoc */ public function canShowTab() { - return $this->_coreRegistry->registry(RegistryConstants::CURRENT_CUSTOMER_ID); + return (bool)$this->getCurrentCustomerId(); } /** - * Tab is hidden - * - * @return boolean + * @inheritdoc */ public function isHidden() { @@ -130,77 +154,256 @@ public function isHidden() } /** - * Initialize the form. + * @inheritdoc + */ + protected function _prepareForm() + { + $this->initForm(); + + return $this; + } + + /** + * Init form values * * @return $this - * @SuppressWarnings(PHPMD.NPathComplexity) */ public function initForm() { if (!$this->canShowTab()) { return $this; } - /**@var \Magento\Framework\Data\Form $form */ + $form = $this->_formFactory->create(); $form->setHtmlIdPrefix('_newsletter'); - $customerId = $this->_coreRegistry->registry(RegistryConstants::CURRENT_CUSTOMER_ID); - $subscriber = $this->_subscriberFactory->create()->loadByCustomerId($customerId); - $this->_coreRegistry->register('subscriber', $subscriber, true); + $this->setForm($form); + $fieldset = $form->addFieldset( + 'base_fieldset', + [ + 'legend' => __('Newsletter Information'), + 'class' => 'customer-newsletter-fieldset' . (!$this->isSingleWebsiteMode() ? ' multi-website' : ''), + ] + ); - $fieldset = $form->addFieldset('base_fieldset', ['legend' => __('Newsletter Information')]); + $customerSubscriptions = $this->getCustomerSubscriptionsOnWebsites(); + if (empty($customerSubscriptions)) { + return $this; + } - $fieldset->addField( - 'subscription', + if ($this->isSingleWebsiteMode()) { + $this->prepareFormSingleWebsite($fieldset, $customerSubscriptions); + $this->updateFromSession($form, $this->getCurrentCustomerId()); + } else { + $this->prepareFormMultiplyWebsite($fieldset, $customerSubscriptions); + } + + if ($this->customerAccountManagement->isReadonly($this->getCurrentCustomerId())) { + $fieldset->setReadonly(true, true); + } + + return $this; + } + + /** + * Prepare form fields for single website mode + * + * @param Fieldset $fieldset + * @param array $subscriptions + * @return void + */ + private function prepareFormSingleWebsite(Fieldset $fieldset, array $subscriptions): void + { + $customer = $this->getCurrentCustomer(); + $websiteId = (int)$this->_storeManager->getStore($customer->getStoreId())->getWebsiteId(); + $customerSubscription = $subscriptions[$websiteId] ?? $this->retrieveSubscriberData($customer, $websiteId); + + $checkboxElement = $fieldset->addField( + 'subscription_status_' . $websiteId, 'checkbox', [ 'label' => __('Subscribed to Newsletter'), - 'name' => 'subscription', + 'name' => "subscription_status[$websiteId]", 'data-form-part' => $this->getData('target_form'), - 'onchange' => 'this.value = this.checked;' + 'value' => $customerSubscription['status'], + 'onchange' => 'this.value = this.checked;', ] ); - - if ($this->customerAccountManagement->isReadonly($customerId)) { - $form->getElement('subscription')->setReadonly(true, true); + $checkboxElement->setIsChecked($customerSubscription['status']); + if (!$this->isSingleStoreMode()) { + $fieldset->addField( + 'subscription_store_' . $websiteId, + 'select', + [ + 'label' => __('Subscribed on Store View'), + 'name' => "subscription_store[$websiteId]", + 'data-form-part' => $this->getData('target_form'), + 'values' => $customerSubscription['store_options'], + 'value' => $customerSubscription['store_id'] ?? null, + ] + ); } - $isSubscribed = $subscriber->isSubscribed(); - $form->setValues(['subscription' => $isSubscribed ? 'true' : 'false']); - $form->getElement('subscription')->setIsChecked($isSubscribed); - - $this->updateFromSession($form, $customerId); - - $changedDate = $this->getStatusChangedDate(); - if ($changedDate) { + if (!empty($customerSubscription['last_updated'])) { + $text = $customerSubscription['status'] ? __('Last Date Subscribed') : __('Last Date Unsubscribed'); $fieldset->addField( - 'change_status_date', + 'change_status_date_' . $websiteId, 'label', [ - 'label' => $isSubscribed ? __('Last Date Subscribed') : __('Last Date Unsubscribed'), - 'value' => $changedDate, + 'label' => $text, + 'value' => $customerSubscription['last_updated'], 'bold' => true ] ); } + } - $this->setForm($form); - return $this; + /** + * Prepare form fields for multiply website mode + * + * @param Fieldset $fieldset + * @param array $subscriptions + * @return void + */ + private function prepareFormMultiplyWebsite(Fieldset $fieldset, array $subscriptions): void + { + $fieldset->addType('customer_subscription', SubscriptionsElement::class); + $fieldset->addField( + 'subscription', + 'customer_subscription', + [ + 'label' => __('Subscribed to Newsletter'), + 'name' => 'subscription', + 'subscriptions' => $subscriptions, + 'target_form' => $this->getData('target_form'), + 'class' => 'newsletter-subscriptions', + 'customer_id' => $this->getCurrentCustomerId(), + ] + ); + } + + /** + * Get current customer id + * + * @return int + */ + private function getCurrentCustomerId(): int + { + return (int)$this->_coreRegistry->registry(RegistryConstants::CURRENT_CUSTOMER_ID); + } + + /** + * Get current customer model + * + * @return CustomerInterface|null + */ + private function getCurrentCustomer(): ?CustomerInterface + { + $customerId = $this->getCurrentCustomerId(); + try { + $customer = $this->customerRepository->getById($customerId); + } catch (NoSuchEntityException $e) { + return null; + } + + return $customer; + } + + /** + * Get Customer Subscriptions on Websites + * + * @return array + */ + private function getCustomerSubscriptionsOnWebsites(): array + { + $customer = $this->getCurrentCustomer(); + if ($customer === null) { + return []; + } + + $subscriptions = []; + foreach ($this->_storeManager->getWebsites() as $website) { + /** Skip websites without stores */ + if ($website->getStoresCount() === 0) { + continue; + } + $websiteId = (int)$website->getId(); + $subscriptions[$websiteId] = $this->retrieveSubscriberData($customer, $websiteId); + } + + return $subscriptions; + } + + /** + * Retrieve subscriber data + * + * @param CustomerInterface $customer + * @param int $websiteId + * @return array + */ + private function retrieveSubscriberData(CustomerInterface $customer, int $websiteId): array + { + $subscriber = $this->_subscriberFactory->create()->loadByCustomer((int)$customer->getId(), $websiteId); + $storeOptions = $this->systemStore->getStoreOptionsTree(false, [], [], [$websiteId]); + $subscriberData = $subscriber->getData(); + $subscriberData['last_updated'] = $this->getSubscriberStatusChangeDate($subscriber); + $subscriberData['website_id'] = $websiteId; + $subscriberData['website_name'] = $this->systemStore->getWebsiteName($websiteId); + $subscriberData['status'] = $subscriber->isSubscribed(); + $subscriberData['store_options'] = $storeOptions; + + return $subscriberData; + } + + /** + * Is single systemStore mode + * + * @return bool + */ + private function isSingleStoreMode(): bool + { + return $this->_storeManager->isSingleStoreMode(); + } + + /** + * Is single website mode + * + * @return bool + */ + private function isSingleWebsiteMode(): bool + { + return $this->isSingleStoreMode() + || !$this->shareConfig->isGlobalScope() + || count($this->_storeManager->getWebsites()) === 1; } /** * Update form elements from session data * - * @param \Magento\Framework\Data\Form $form + * @param Form $form * @param int $customerId * @return void */ - protected function updateFromSession(\Magento\Framework\Data\Form $form, $customerId) + protected function updateFromSession(Form $form, $customerId) { + if (!$this->isSingleWebsiteMode()) { + return; + } $data = $this->_backendSession->getCustomerFormData(); - if (!empty($data)) { - $dataCustomerId = isset($data['customer']['entity_id']) ? $data['customer']['entity_id'] : null; - if (isset($data['subscription']) && $dataCustomerId == $customerId) { - $form->getElement('subscription')->setIsChecked($data['subscription']); - } + $sessionCustomerId = $data['customer']['entity_id'] ?? null; + if ($sessionCustomerId === null || (int)$sessionCustomerId !== (int)$customerId) { + return; + } + + $websiteId = (int)$this->getCurrentCustomer()->getWebsiteId(); + $statusSessionValue = $data['subscription_status'][$websiteId] ?? null; + if ($statusSessionValue !== null) { + $subscribeElement = $form->getElement('subscription_status_' . $websiteId); + $subscribeElement->setValue($statusSessionValue); + $subscribeElement->setChecked($statusSessionValue); + } + $storeSessionValue = $data['subscription_store'][$websiteId] ?? null; + $storeElement = $form->getElement('subscription_store_' . $websiteId); + if ($storeSessionValue !== null && $storeElement !== null) { + $storeElement->setValue($storeSessionValue); } } @@ -211,28 +414,32 @@ protected function updateFromSession(\Magento\Framework\Data\Form $form, $custom */ public function getStatusChangedDate() { - $subscriber = $this->_coreRegistry->registry('subscriber'); - if ($subscriber->getChangeStatusAt()) { - return $this->formatDate( - $subscriber->getChangeStatusAt(), - \IntlDateFormatter::MEDIUM, - true - ); + $customer = $this->getCurrentCustomerId(); + if ($customer === null) { + return ''; } + $customerId = (int)$customer->getId(); + $subscriber = $this->_subscriberFactory->create()->loadByCustomer($customerId, (int)$customer->getWebsiteId()); - return null; + return $this->getSubscriberStatusChangeDate($subscriber); } /** + * Retrieve the date when the subscriber status changed + * + * @param Subscriber $subscriber * @return string */ - protected function _toHtml() + private function getSubscriberStatusChangeDate(Subscriber $subscriber): string { - if ($this->canShowTab()) { - $this->initForm(); - return parent::_toHtml(); - } else { + if (empty($subscriber->getChangeStatusAt())) { return ''; } + + return $this->formatDate( + $subscriber->getChangeStatusAt(), + \IntlDateFormatter::MEDIUM, + true + ); } } diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Newsletter/Grid.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Newsletter/Grid.php index 4f49c3ba1db9b..97582fbdb19b2 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Newsletter/Grid.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Newsletter/Grid.php @@ -5,47 +5,75 @@ */ namespace Magento\Customer\Block\Adminhtml\Edit\Tab\Newsletter; +use Magento\Backend\Block\Template\Context; +use Magento\Backend\Block\Widget\Grid\Extended; +use Magento\Backend\Helper\Data; +use Magento\Customer\Block\Adminhtml\Edit\Tab\Newsletter\Grid\Renderer\Action; +use Magento\Customer\Block\Adminhtml\Edit\Tab\Newsletter\Grid\Renderer\Status; +use Magento\Customer\Controller\RegistryConstants; +use Magento\Customer\Model\Config\Share; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Registry; +use Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory; +use Magento\Store\Model\System\Store as SystemStore; + /** * Adminhtml newsletter queue grid block * * @api * @since 100.0.2 */ -class Grid extends \Magento\Backend\Block\Widget\Grid\Extended +class Grid extends Extended { /** * Core registry * - * @var \Magento\Framework\Registry|null + * @var Registry|null */ protected $_coreRegistry = null; /** - * @var \Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory + * @var CollectionFactory */ protected $_collectionFactory; /** - * @param \Magento\Backend\Block\Template\Context $context - * @param \Magento\Backend\Helper\Data $backendHelper - * @param \Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory $collectionFactory - * @param \Magento\Framework\Registry $coreRegistry + * @var Share + */ + private $shareConfig; + + /** + * @var SystemStore + */ + private $systemStore; + + /** + * @param Context $context + * @param Data $backendHelper + * @param CollectionFactory $collectionFactory + * @param Registry $coreRegistry * @param array $data + * @param Share|null $shareConfig + * @param SystemStore|null $systemStore */ public function __construct( - \Magento\Backend\Block\Template\Context $context, - \Magento\Backend\Helper\Data $backendHelper, - \Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory $collectionFactory, - \Magento\Framework\Registry $coreRegistry, - array $data = [] + Context $context, + Data $backendHelper, + CollectionFactory $collectionFactory, + Registry $coreRegistry, + array $data = [], + Share $shareConfig = null, + SystemStore $systemStore = null ) { $this->_coreRegistry = $coreRegistry; $this->_collectionFactory = $collectionFactory; parent::__construct($context, $backendHelper, $data); + $this->shareConfig = $shareConfig ?? ObjectManager::getInstance()->get(Share::class); + $this->systemStore = $systemStore ?? ObjectManager::getInstance()->get(SystemStore::class); } /** - * @return void + * @inheritdoc */ protected function _construct() { @@ -60,7 +88,7 @@ protected function _construct() } /** - * @return string + * @inheritdoc */ public function getGridUrl() { @@ -68,22 +96,19 @@ public function getGridUrl() } /** - * @return $this + * @inheritdoc */ protected function _prepareCollection() { - /** @var $collection \Magento\Newsletter\Model\ResourceModel\Queue\Collection */ - $collection = $this->_collectionFactory->create()->addTemplateInfo()->addSubscriberFilter( - $this->_coreRegistry->registry('subscriber')->getId() - ); - + $customerId = $this->getCurrentCustomerId(); + $collection = $this->_collectionFactory->create()->addTemplateInfo()->addCustomerFilter($customerId); $this->setCollection($collection); return parent::_prepareCollection(); } /** - * @return $this + * @inheritdoc */ protected function _prepareColumns() { @@ -132,6 +157,19 @@ protected function _prepareColumns() ['header' => __('Subject'), 'align' => 'center', 'index' => 'template_subject'] ); + if ($this->isMultiplyWebsiteMode()) { + $this->addColumn( + 'store_view', + [ + 'header' => __('Store View'), + 'align' => 'center', + 'index' => 'subscriber_store_id', + 'type' => 'options', + 'option_groups' => $this->systemStore->getStoreValuesForForm(), + ] + ); + } + $this->addColumn( 'status', [ @@ -139,7 +177,7 @@ protected function _prepareColumns() 'align' => 'center', 'filter' => \Magento\Customer\Block\Adminhtml\Edit\Tab\Newsletter\Grid\Filter\Status::class, 'index' => 'queue_status', - 'renderer' => \Magento\Customer\Block\Adminhtml\Edit\Tab\Newsletter\Grid\Renderer\Status::class + 'renderer' => Status::class ] ); @@ -150,10 +188,31 @@ protected function _prepareColumns() 'align' => 'center', 'filter' => false, 'sortable' => false, - 'renderer' => \Magento\Customer\Block\Adminhtml\Edit\Tab\Newsletter\Grid\Renderer\Action::class + 'renderer' => Action::class ] ); return parent::_prepareColumns(); } + + /** + * Get current customer id + * + * @return int + */ + private function getCurrentCustomerId(): int + { + return (int)$this->_coreRegistry->registry(RegistryConstants::CURRENT_CUSTOMER_ID); + } + + /** + * Is multiply website mode + * + * @return bool + */ + private function isMultiplyWebsiteMode(): bool + { + return $this->shareConfig->isGlobalScope() + && count($this->_storeManager->getWebsites()) > 1; + } } diff --git a/app/code/Magento/Customer/Block/Adminhtml/Form/Element/Newsletter/Subscriptions.php b/app/code/Magento/Customer/Block/Adminhtml/Form/Element/Newsletter/Subscriptions.php new file mode 100644 index 0000000000000..e9686daa3e3ab --- /dev/null +++ b/app/code/Magento/Customer/Block/Adminhtml/Form/Element/Newsletter/Subscriptions.php @@ -0,0 +1,161 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Block\Adminhtml\Form\Element\Newsletter; + +use Magento\Framework\App\Request\DataPersistorInterface; +use Magento\Framework\Data\Form\Element\AbstractElement; +use Magento\Framework\Data\Form\Element\CollectionFactory; +use Magento\Framework\Data\Form\Element\Factory; +use Magento\Framework\Escaper; + +/** + * Customer Newsletter Subscriptions Element + */ +class Subscriptions extends AbstractElement +{ + /** + * @var DataPersistorInterface + */ + private $dataPersistor; + + /** + * @param Factory $factoryElement + * @param CollectionFactory $factoryCollection + * @param Escaper $escaper + * @param DataPersistorInterface $dataPersistor + * @param array $data + */ + public function __construct( + Factory $factoryElement, + CollectionFactory $factoryCollection, + Escaper $escaper, + DataPersistorInterface $dataPersistor, + $data = [] + ) { + $this->dataPersistor = $dataPersistor; + parent::__construct($factoryElement, $factoryCollection, $escaper, $data); + } + + /** + * @inheritdoc + */ + public function getElementHtml() + { + $websiteHeader = $this->_escape(__('Website')); + $subscribedHeader = $this->_escape(__('Subscribed')); + $storeHeader = $this->_escape(__('Store View')); + $lastUpdatedHeader = $this->_escape(__('Last Updated At')); + $bodyHtml = ''; + foreach ($this->getData('subscriptions') as $subscriptions) { + $storeId = !empty($subscriptions['store_id']) ? (int)$subscriptions['store_id'] : null; + $websiteId = (int)$subscriptions['website_id']; + $websiteName = $this->_escape($subscriptions['website_name']); + $subscribed = (bool)$subscriptions['status']; + $options = (array)$subscriptions['store_options']; + $statusElement = $this->getSubscriptionStatusElementHtml($websiteId, $subscribed); + $storeSelectElement = $this->getStoreSelectElementHtml($websiteId, $options, $storeId); + $lastUpdated = !empty($subscriptions['last_updated']) ? $subscriptions['last_updated'] : ''; + + $bodyHtml .= "<tr><td>{$websiteName}</td><td class=\"subscriber-status\">$statusElement</td>" + . "<td>$storeSelectElement</td><td>$lastUpdated</td></tr>"; + } + $html = '<table class="admin__table-secondary">' + . "<tr><th>{$websiteHeader}</th><th class=\"subscriber-status\">{$subscribedHeader}</th>" + . "<th>{$storeHeader}</th><th>{$lastUpdatedHeader}</th></tr>" + . $bodyHtml + . '</table>'; + + return $html; + } + + /** + * Get store select element html + * + * @param int $websiteId + * @param array $options + * @param int|null $value + * @return string + */ + private function getStoreSelectElementHtml(int $websiteId, array $options, ?int $value): string + { + $name = $this->getData('name'); + $value = $this->getSessionFormValue("{$name}_store", $websiteId) ?? $value; + $elementId = $name . '_store_' . $websiteId; + $element = $this->_factoryElement->create( + 'select', + [ + 'data' => [ + 'name' => "{$name}_store[$websiteId]", + 'data-form-part' => $this->getData('target_form'), + 'values' => $options, + 'value' => $value, + 'required' => true, + ], + ] + ); + $element->setId($elementId); + $element->setForm($this->getForm()); + if ($this->getReadonly()) { + $element->setReadonly($this->getReadonly(), $this->getDisabled()); + } + + return $element->toHtml(); + } + + /** + * Get subscription status element html + * + * @param int $websiteId + * @param bool $value + * @return string + */ + private function getSubscriptionStatusElementHtml(int $websiteId, bool $value): string + { + $name = $this->getData('name'); + $value = $this->getSessionFormValue("{$name}_status", $websiteId) ?? $value; + $elementId = $name . '_status_' . $websiteId; + $element = $this->_factoryElement->create( + 'checkbox', + [ + 'data' => [ + 'name' => "{$name}_status[$websiteId]", + 'data-form-part' => $this->getData('target_form'), + 'value' => $value, + 'onchange' => 'this.value = this.checked;', + ], + ] + ); + $element->setId($elementId); + $element->setForm($this->getForm()); + $element->setIsChecked($value); + if ($this->getReadonly()) { + $element->setReadonly($this->getReadonly(), $this->getDisabled()); + } + + return $element->toHtml(); + } + + /** + * Get form data value from current session + * + * @param string $name + * @param int $arrayKey + * @return string|null + */ + private function getSessionFormValue(string $name, int $arrayKey): ?string + { + $data = $this->dataPersistor->get('customer_form'); + $currentCustomerId = $this->getData('customer_id'); + $sessionCustomerId = $data['customer']['entity_id'] ?? null; + if ($sessionCustomerId === null || $currentCustomerId !== (int)$sessionCustomerId) { + return null; + } + + return $data[$name][$arrayKey] ?? null; + } +} diff --git a/app/code/Magento/Customer/Controller/Account/Confirm.php b/app/code/Magento/Customer/Controller/Account/Confirm.php index adca90c5e5f24..3aa08cfbf847e 100644 --- a/app/code/Magento/Customer/Controller/Account/Confirm.php +++ b/app/code/Magento/Customer/Controller/Account/Confirm.php @@ -168,7 +168,7 @@ public function execute() $metadata->setPath('/'); $this->getCookieManager()->deleteCookie('mage-cache-sessid', $metadata); } - $this->messageManager->addSuccess($this->getSuccessMessage()); + $this->messageManager->addSuccessMessage($this->getSuccessMessage()); $resultRedirect->setUrl($this->getSuccessRedirect()); return $resultRedirect; } catch (StateException $e) { diff --git a/app/code/Magento/Customer/Controller/Account/CreatePost.php b/app/code/Magento/Customer/Controller/Account/CreatePost.php index 57b670684411e..e2a7c085a0b44 100644 --- a/app/code/Magento/Customer/Controller/Account/CreatePost.php +++ b/app/code/Magento/Customer/Controller/Account/CreatePost.php @@ -327,7 +327,7 @@ public function validateForCsrf(RequestInterface $request): ?bool /** * Create customer account action * - * @return void + * @return \Magento\Framework\Controller\Result\Redirect * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ @@ -381,9 +381,7 @@ public function execute() $resultRedirect->setUrl($this->_redirect->success($url)); } else { $this->session->setCustomerDataAsLoggedIn($customer); - $this->messageManager->addMessage($this->getMessageManagerSuccessMessage()); - $requestedRedirect = $this->accountRedirect->getRedirectCookie(); if (!$this->scopeConfig->getValue('customer/startup/redirect_dashboard') && $requestedRedirect) { $resultRedirect->setUrl($this->_redirect->success($requestedRedirect)); diff --git a/app/code/Magento/Customer/Controller/Account/EditPost.php b/app/code/Magento/Customer/Controller/Account/EditPost.php index 4eb41cedea29a..04b5b72ae776b 100644 --- a/app/code/Magento/Customer/Controller/Account/EditPost.php +++ b/app/code/Magento/Customer/Controller/Account/EditPost.php @@ -216,7 +216,7 @@ public function execute() $isPasswordChanged ); $this->dispatchSuccessEvent($customerCandidateDataObject); - $this->messageManager->addSuccess(__('You saved the account information.')); + $this->messageManager->addSuccessMessage(__('You saved the account information.')); return $resultRedirect->setPath('customer/account'); } catch (InvalidEmailOrPasswordException $e) { $this->messageManager->addErrorMessage($this->escaper->escapeHtml($e->getMessage())); @@ -227,7 +227,7 @@ public function execute() ); $this->session->logout(); $this->session->start(); - $this->messageManager->addError($message); + $this->messageManager->addErrorMessage($message); return $resultRedirect->setPath('customer/account/login'); } catch (InputException $e) { $this->messageManager->addErrorMessage($this->escaper->escapeHtml($e->getMessage())); @@ -235,7 +235,7 @@ public function execute() $this->messageManager->addErrorMessage($this->escaper->escapeHtml($error->getMessage())); } } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { $this->messageManager->addException($e, __('We can\'t save the customer.')); } diff --git a/app/code/Magento/Customer/Controller/Account/LoginPost.php b/app/code/Magento/Customer/Controller/Account/LoginPost.php index 4091a068e3094..36d04e949923f 100644 --- a/app/code/Magento/Customer/Controller/Account/LoginPost.php +++ b/app/code/Magento/Customer/Controller/Account/LoginPost.php @@ -204,11 +204,6 @@ public function execute() 'This account is not confirmed. <a href="%1">Click here</a> to resend confirmation email.', $value ); - } catch (UserLockedException $e) { - $message = __( - 'The account sign-in was incorrect or your account is disabled temporarily. ' - . 'Please wait and try again later.' - ); } catch (AuthenticationException $e) { $message = __( 'The account sign-in was incorrect or your account is disabled temporarily. ' @@ -218,17 +213,17 @@ public function execute() $message = $e->getMessage(); } catch (\Exception $e) { // PA DSS violation: throwing or logging an exception here can disclose customer password - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __('An unspecified error occurred. Please contact us for assistance.') ); } finally { if (isset($message)) { - $this->messageManager->addError($message); + $this->messageManager->addErrorMessage($message); $this->session->setUsername($login['username']); } } } else { - $this->messageManager->addError(__('A login and a password are required.')); + $this->messageManager->addErrorMessage(__('A login and a password are required.')); } } diff --git a/app/code/Magento/Customer/Controller/Account/ResetPasswordPost.php b/app/code/Magento/Customer/Controller/Account/ResetPasswordPost.php index 27a00f86dd95d..a127f2acf538f 100644 --- a/app/code/Magento/Customer/Controller/Account/ResetPasswordPost.php +++ b/app/code/Magento/Customer/Controller/Account/ResetPasswordPost.php @@ -73,13 +73,13 @@ public function execute() $passwordConfirmation = (string)$this->getRequest()->getPost('password_confirmation'); if ($password !== $passwordConfirmation) { - $this->messageManager->addError(__("New Password and Confirm New Password values didn't match.")); + $this->messageManager->addErrorMessage(__("New Password and Confirm New Password values didn't match.")); $resultRedirect->setPath('*/*/createPassword', ['token' => $resetPasswordToken]); return $resultRedirect; } if (iconv_strlen($password) <= 0) { - $this->messageManager->addError(__('Please enter a new password.')); + $this->messageManager->addErrorMessage(__('Please enter a new password.')); $resultRedirect->setPath('*/*/createPassword', ['token' => $resetPasswordToken]); return $resultRedirect; @@ -92,17 +92,17 @@ public function execute() $password ); $this->session->unsRpToken(); - $this->messageManager->addSuccess(__('You updated your password.')); + $this->messageManager->addSuccessMessage(__('You updated your password.')); $resultRedirect->setPath('*/*/login'); return $resultRedirect; } catch (InputException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); foreach ($e->getErrors() as $error) { - $this->messageManager->addError($error->getMessage()); + $this->messageManager->addErrorMessage($error->getMessage()); } } catch (\Exception $exception) { - $this->messageManager->addError(__('Something went wrong while saving the new password.')); + $this->messageManager->addErrorMessage(__('Something went wrong while saving the new password.')); } $resultRedirect->setPath('*/*/createPassword', ['token' => $resetPasswordToken]); diff --git a/app/code/Magento/Customer/Controller/Address/Delete.php b/app/code/Magento/Customer/Controller/Address/Delete.php index a30e15db4b3f8..2024b2c58b8ef 100644 --- a/app/code/Magento/Customer/Controller/Address/Delete.php +++ b/app/code/Magento/Customer/Controller/Address/Delete.php @@ -27,9 +27,9 @@ public function execute() $address = $this->_addressRepository->getById($addressId); if ($address->getCustomerId() === $this->_getSession()->getCustomerId()) { $this->_addressRepository->deleteById($addressId); - $this->messageManager->addSuccess(__('You deleted the address.')); + $this->messageManager->addSuccessMessage(__('You deleted the address.')); } else { - $this->messageManager->addError(__('We can\'t delete the address right now.')); + $this->messageManager->addErrorMessage(__('We can\'t delete the address right now.')); } } catch (\Exception $other) { $this->messageManager->addException($other, __('We can\'t delete the address right now.')); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Customer/InvalidateToken.php b/app/code/Magento/Customer/Controller/Adminhtml/Customer/InvalidateToken.php index b69410ecbfce7..7747d80595cdc 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Customer/InvalidateToken.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Customer/InvalidateToken.php @@ -139,14 +139,14 @@ public function execute() if ($customerId = $this->getRequest()->getParam('customer_id')) { try { $this->tokenService->revokeCustomerAccessToken($customerId); - $this->messageManager->addSuccess(__('You have revoked the customer\'s tokens.')); + $this->messageManager->addSuccessMessage(__('You have revoked the customer\'s tokens.')); $resultRedirect->setPath('customer/index/edit', ['id' => $customerId, '_current' => true]); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $resultRedirect->setPath('customer/index/edit', ['id' => $customerId, '_current' => true]); } } else { - $this->messageManager->addError(__('We can\'t find a customer to revoke.')); + $this->messageManager->addErrorMessage(__('We can\'t find a customer to revoke.')); $resultRedirect->setPath('customer/index/index'); } return $resultRedirect; diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php index ab32ea08a44aa..661ef1cace69b 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Group/Delete.php @@ -1,6 +1,5 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -9,6 +8,9 @@ use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Exception\NoSuchEntityException; +/** + * Class Delete + */ class Delete extends \Magento\Customer\Controller\Adminhtml\Group implements HttpPostActionInterface { /** @@ -24,12 +26,12 @@ public function execute() if ($id) { try { $this->groupRepository->deleteById($id); - $this->messageManager->addSuccess(__('You deleted the customer group.')); + $this->messageManager->addSuccessMessage(__('You deleted the customer group.')); } catch (NoSuchEntityException $e) { - $this->messageManager->addError(__('The customer group no longer exists.')); + $this->messageManager->addErrorMessage(__('The customer group no longer exists.')); return $resultRedirect->setPath('customer/*/'); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); return $resultRedirect->setPath('customer/group/edit', ['id' => $id]); } } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php index 5ffce4cbcd989..64c94fa230fb1 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Group/Save.php @@ -93,10 +93,10 @@ public function execute() $this->groupRepository->save($customerGroup); - $this->messageManager->addSuccess(__('You saved the customer group.')); + $this->messageManager->addSuccessMessage(__('You saved the customer group.')); $resultRedirect->setPath('customer/group'); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); if ($customerGroup != null) { $this->storeCustomerGroupDataToSession( $this->dataObjectProcessor->buildOutputDataArray( diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php index e26b49aaebe7a..e2bde42351d45 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/AbstractMassAction.php @@ -64,7 +64,7 @@ public function execute() $collection = $this->filter->getCollection($this->collectionFactory->create()); return $this->massAction($collection); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); return $resultRedirect->setPath($this->redirectUrl); @@ -73,6 +73,7 @@ public function execute() /** * Return component referer url + * * TODO: Technical dept referer url should be implement as a part of Action configuration in appropriate way * * @return null|string diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Delete.php index ab39ca098162f..4b2f2614948cf 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Delete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Delete.php @@ -31,7 +31,7 @@ public function execute() $formKeyIsValid = $this->_formKeyValidator->validate($this->getRequest()); $isPost = $this->getRequest()->isPost(); if (!$formKeyIsValid || !$isPost) { - $this->messageManager->addError(__('Customer could not be deleted.')); + $this->messageManager->addErrorMessage(__('Customer could not be deleted.')); return $resultRedirect->setPath('customer/index'); } @@ -39,9 +39,9 @@ public function execute() if (!empty($customerId)) { try { $this->_customerRepository->deleteById($customerId); - $this->messageManager->addSuccess(__('You deleted the customer.')); + $this->messageManager->addSuccessMessage(__('You deleted the customer.')); } catch (\Exception $exception) { - $this->messageManager->addError($exception->getMessage()); + $this->messageManager->addErrorMessage($exception->getMessage()); } } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassSubscribe.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassSubscribe.php index 29a66bf1ff933..453035ad3151b 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassSubscribe.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassSubscribe.php @@ -7,15 +7,15 @@ use Magento\Backend\App\Action\Context; use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; +use Magento\Newsletter\Model\SubscriptionManagerInterface; use Magento\Ui\Component\MassAction\Filter; use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory; use Magento\Customer\Api\CustomerRepositoryInterface; -use Magento\Newsletter\Model\SubscriberFactory; use Magento\Eav\Model\Entity\Collection\AbstractCollection; use Magento\Framework\Controller\ResultFactory; /** - * Class MassSubscribe + * Class to mass subscribe customers by ids */ class MassSubscribe extends AbstractMassAction implements HttpPostActionInterface { @@ -25,27 +25,27 @@ class MassSubscribe extends AbstractMassAction implements HttpPostActionInterfac protected $customerRepository; /** - * @var SubscriberFactory + * @var SubscriptionManagerInterface */ - protected $subscriberFactory; + private $subscriptionManager; /** * @param Context $context * @param Filter $filter * @param CollectionFactory $collectionFactory * @param CustomerRepositoryInterface $customerRepository - * @param SubscriberFactory $subscriberFactory + * @param SubscriptionManagerInterface $subscriptionManager */ public function __construct( Context $context, Filter $filter, CollectionFactory $collectionFactory, CustomerRepositoryInterface $customerRepository, - SubscriberFactory $subscriberFactory + SubscriptionManagerInterface $subscriptionManager ) { parent::__construct($context, $filter, $collectionFactory); $this->customerRepository = $customerRepository; - $this->subscriberFactory = $subscriberFactory; + $this->subscriptionManager = $subscriptionManager; } /** @@ -58,9 +58,9 @@ protected function massAction(AbstractCollection $collection) { $customersUpdated = 0; foreach ($collection->getAllIds() as $customerId) { - // Verify customer exists - $this->customerRepository->getById($customerId); - $this->subscriberFactory->create()->subscribeCustomerById($customerId); + $customer = $this->customerRepository->getById($customerId); + $storeId = (int)$customer->getStoreId(); + $this->subscriptionManager->subscribeCustomer($customerId, $storeId); $customersUpdated++; } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php index fddf18489b9a5..245e06699e6b0 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassUnsubscribe.php @@ -5,17 +5,21 @@ */ namespace Magento\Customer\Controller\Adminhtml\Index; +use Magento\Backend\Model\View\Result\Redirect; use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\Config\Share; use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; use Magento\Framework\Controller\ResultFactory; use Magento\Backend\App\Action\Context; -use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Newsletter\Model\SubscriptionManagerInterface; +use Magento\Store\Model\StoreManagerInterface; use Magento\Ui\Component\MassAction\Filter; use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory; use Magento\Eav\Model\Entity\Collection\AbstractCollection; /** - * Class MassUnsubscribe + * Class to mass unsubscribe customers by ids */ class MassUnsubscribe extends AbstractMassAction implements HttpPostActionInterface { @@ -25,52 +29,90 @@ class MassUnsubscribe extends AbstractMassAction implements HttpPostActionInterf protected $customerRepository; /** - * @var SubscriberFactory + * @var SubscriptionManagerInterface */ - protected $subscriberFactory; + private $subscriptionManager; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var Share + */ + private $shareConfig; /** * @param Context $context * @param Filter $filter * @param CollectionFactory $collectionFactory * @param CustomerRepositoryInterface $customerRepository - * @param SubscriberFactory $subscriberFactory + * @param SubscriptionManagerInterface $subscriptionManager + * @param StoreManagerInterface $storeManager + * @param Share $shareConfig */ public function __construct( Context $context, Filter $filter, CollectionFactory $collectionFactory, CustomerRepositoryInterface $customerRepository, - SubscriberFactory $subscriberFactory + SubscriptionManagerInterface $subscriptionManager, + StoreManagerInterface $storeManager, + Share $shareConfig ) { parent::__construct($context, $filter, $collectionFactory); $this->customerRepository = $customerRepository; - $this->subscriberFactory = $subscriberFactory; + $this->subscriptionManager = $subscriptionManager; + $this->storeManager = $storeManager; + $this->shareConfig = $shareConfig; } /** * Customer mass unsubscribe action * * @param AbstractCollection $collection - * @return \Magento\Backend\Model\View\Result\Redirect + * @return Redirect */ protected function massAction(AbstractCollection $collection) { $customersUpdated = 0; foreach ($collection->getAllIds() as $customerId) { - // Verify customer exists - $this->customerRepository->getById($customerId); - $this->subscriberFactory->create()->unsubscribeCustomerById($customerId); + // Verify that customer exists + $customer = $this->customerRepository->getById($customerId); + foreach ($this->getUnsubscribeStoreIds($customer) as $storeId) { + $this->subscriptionManager->unsubscribeCustomer((int)$customerId, $storeId); + } $customersUpdated++; } if ($customersUpdated) { $this->messageManager->addSuccessMessage(__('A total of %1 record(s) were updated.', $customersUpdated)); } - /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ + /** @var Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); $resultRedirect->setPath($this->getComponentRefererUrl()); return $resultRedirect; } + + /** + * Get store ids to unsubscribe customer + * + * @param CustomerInterface $customer + * @return array + */ + private function getUnsubscribeStoreIds(CustomerInterface $customer): array + { + $storeIds = []; + if ($this->shareConfig->isGlobalScope()) { + foreach ($this->storeManager->getStores() as $store) { + $storeIds[(int)$store->getWebsiteId()] = (int)$store->getId(); + } + } else { + $storeIds = [(int)$customer->getStoreId()]; + } + + return $storeIds; + } } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php index 3ee33af9ec073..b85b735ea9c4f 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php @@ -5,25 +5,45 @@ */ namespace Magento\Customer\Controller\Adminhtml\Index; +use Magento\Backend\App\Action\Context; +use Magento\Backend\Model\View\Result\ForwardFactory; +use Magento\Backend\Model\View\Result\Redirect; use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\AddressMetadataInterface; use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\CustomerMetadataInterface; use Magento\Customer\Api\CustomerRepositoryInterface; -use Magento\Customer\Model\Address\Mapper; -use Magento\Customer\Model\AddressRegistry; -use Magento\Framework\Api\DataObjectHelper; use Magento\Customer\Api\Data\AddressInterfaceFactory; -use Magento\Customer\Api\Data\CustomerInterfaceFactory; -use Magento\Framework\DataObjectFactory as ObjectFactory; -use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; -use Magento\Customer\Api\AddressMetadataInterface; -use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Customer\Api\Data\AttributeMetadataInterface; use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Api\Data\CustomerInterfaceFactory; use Magento\Customer\Controller\RegistryConstants; +use Magento\Customer\Helper\View; +use Magento\Customer\Model\Address\Mapper; +use Magento\Customer\Model\AddressFactory; +use Magento\Customer\Model\AddressRegistry; +use Magento\Customer\Model\CustomerFactory; use Magento\Customer\Model\EmailNotificationInterface; use Magento\Customer\Model\Metadata\Form; +use Magento\Customer\Model\Metadata\FormFactory; +use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\Api\ExtensibleDataObjectConverter; +use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\Response\Http\FileFactory; +use Magento\Framework\Controller\Result\JsonFactory; +use Magento\Framework\DataObject; +use Magento\Framework\DataObjectFactory as ObjectFactory; +use Magento\Framework\Exception\AbstractAggregateException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\Math\Random; +use Magento\Framework\Reflection\DataObjectProcessor; +use Magento\Framework\Registry; +use Magento\Framework\View\Result\LayoutFactory; +use Magento\Framework\View\Result\PageFactory; +use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Newsletter\Model\SubscriptionManagerInterface; /** * Save customer action. @@ -37,6 +57,11 @@ class Save extends \Magento\Customer\Controller\Adminhtml\Index implements HttpP */ private $emailNotification; + /** + * @var SubscriptionManagerInterface + */ + private $subscriptionManager; + /** * @var AddressRegistry */ @@ -45,60 +70,62 @@ class Save extends \Magento\Customer\Controller\Adminhtml\Index implements HttpP /** * Constructor * - * @param \Magento\Backend\App\Action\Context $context - * @param \Magento\Framework\Registry $coreRegistry - * @param \Magento\Framework\App\Response\Http\FileFactory $fileFactory - * @param \Magento\Customer\Model\CustomerFactory $customerFactory - * @param \Magento\Customer\Model\AddressFactory $addressFactory - * @param \Magento\Customer\Model\Metadata\FormFactory $formFactory - * @param \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory - * @param \Magento\Customer\Helper\View $viewHelper - * @param \Magento\Framework\Math\Random $random + * @param Context $context + * @param Registry $coreRegistry + * @param FileFactory $fileFactory + * @param CustomerFactory $customerFactory + * @param AddressFactory $addressFactory + * @param FormFactory $formFactory + * @param SubscriberFactory $subscriberFactory + * @param View $viewHelper + * @param Random $random * @param CustomerRepositoryInterface $customerRepository - * @param \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter + * @param ExtensibleDataObjectConverter $extensibleDataObjectConverter * @param Mapper $addressMapper * @param AccountManagementInterface $customerAccountManagement * @param AddressRepositoryInterface $addressRepository * @param CustomerInterfaceFactory $customerDataFactory * @param AddressInterfaceFactory $addressDataFactory * @param \Magento\Customer\Model\Customer\Mapper $customerMapper - * @param \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor + * @param DataObjectProcessor $dataObjectProcessor * @param DataObjectHelper $dataObjectHelper * @param ObjectFactory $objectFactory * @param \Magento\Framework\View\LayoutFactory $layoutFactory - * @param \Magento\Framework\View\Result\LayoutFactory $resultLayoutFactory - * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory - * @param \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory - * @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory + * @param LayoutFactory $resultLayoutFactory + * @param PageFactory $resultPageFactory + * @param ForwardFactory $resultForwardFactory + * @param JsonFactory $resultJsonFactory + * @param SubscriptionManagerInterface $subscriptionManager * @param AddressRegistry|null $addressRegistry * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Backend\App\Action\Context $context, - \Magento\Framework\Registry $coreRegistry, - \Magento\Framework\App\Response\Http\FileFactory $fileFactory, - \Magento\Customer\Model\CustomerFactory $customerFactory, - \Magento\Customer\Model\AddressFactory $addressFactory, - \Magento\Customer\Model\Metadata\FormFactory $formFactory, - \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory, - \Magento\Customer\Helper\View $viewHelper, - \Magento\Framework\Math\Random $random, + Context $context, + Registry $coreRegistry, + FileFactory $fileFactory, + CustomerFactory $customerFactory, + AddressFactory $addressFactory, + FormFactory $formFactory, + SubscriberFactory $subscriberFactory, + View $viewHelper, + Random $random, CustomerRepositoryInterface $customerRepository, - \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter, + ExtensibleDataObjectConverter $extensibleDataObjectConverter, Mapper $addressMapper, AccountManagementInterface $customerAccountManagement, AddressRepositoryInterface $addressRepository, CustomerInterfaceFactory $customerDataFactory, AddressInterfaceFactory $addressDataFactory, \Magento\Customer\Model\Customer\Mapper $customerMapper, - \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor, + DataObjectProcessor $dataObjectProcessor, DataObjectHelper $dataObjectHelper, ObjectFactory $objectFactory, \Magento\Framework\View\LayoutFactory $layoutFactory, - \Magento\Framework\View\Result\LayoutFactory $resultLayoutFactory, - \Magento\Framework\View\Result\PageFactory $resultPageFactory, - \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory, - \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory, + LayoutFactory $resultLayoutFactory, + PageFactory $resultPageFactory, + ForwardFactory $resultForwardFactory, + JsonFactory $resultJsonFactory, + SubscriptionManagerInterface $subscriptionManager, AddressRegistry $addressRegistry = null ) { parent::__construct( @@ -128,6 +155,7 @@ public function __construct( $resultForwardFactory, $resultJsonFactory ); + $this->subscriptionManager = $subscriptionManager; $this->addressRegistry = $addressRegistry ?: ObjectManager::getInstance()->get(AddressRegistry::class); } @@ -186,7 +214,7 @@ protected function _extractData( $formData = $metadataForm->compactData($formData); // Initialize additional attributes - /** @var \Magento\Framework\DataObject $object */ + /** @var DataObject $object */ $object = $this->_objectFactory->create(['data' => $this->getRequest()->getPostValue()]); $requestData = $object->getData($scope); foreach ($additionalAttributes as $attributeCode) { @@ -196,7 +224,7 @@ protected function _extractData( // Unset unused attributes $formAttributes = $metadataForm->getAttributes(); foreach ($formAttributes as $attribute) { - /** @var \Magento\Customer\Api\Data\AttributeMetadataInterface $attribute */ + /** @var AttributeMetadataInterface $attribute */ $attributeCode = $attribute->getAttributeCode(); if ($attribute->getFrontendInput() != 'boolean' && $formData[$attributeCode] === false @@ -281,7 +309,7 @@ protected function _extractCustomerAddressData(array & $extractedCustomerData) /** * Save customer action * - * @return \Magento\Backend\Model\View\Result\Redirect + * @return Redirect * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.NPathComplexity) @@ -312,7 +340,7 @@ public function execute() $this->dataObjectHelper->populateWithArray( $customer, $customerData, - \Magento\Customer\Api\Data\CustomerInterface::class + CustomerInterface::class ); $this->_eventManager->dispatch( @@ -334,17 +362,7 @@ public function execute() $customerId = $customer->getId(); } - $isSubscribed = null; - if ($this->_authorization->isAllowed(null)) { - $isSubscribed = $this->getRequest()->getPost('subscription'); - } - if ($isSubscribed !== null) { - if ($isSubscribed !== '0') { - $this->_subscriberFactory->create()->subscribeCustomerById($customerId); - } else { - $this->_subscriberFactory->create()->unsubscribeCustomerById($customerId); - } - } + $this->updateSubscriptions($customer); // After save $this->_eventManager->dispatch( @@ -364,7 +382,7 @@ public function execute() $this->_addSessionErrorMessages($messages); $this->_getSession()->setCustomerFormData($this->retrieveFormattedFormData()); $returnToEdit = true; - } catch (\Magento\Framework\Exception\AbstractAggregateException $exception) { + } catch (AbstractAggregateException $exception) { $errors = $exception->getErrors(); $messages = []; foreach ($errors as $error) { @@ -406,6 +424,34 @@ public function execute() return $resultRedirect; } + /** + * Update customer website subscriptions + * + * @param CustomerInterface $customer + * @return void + */ + private function updateSubscriptions(CustomerInterface $customer): void + { + if (!$this->_authorization->isAllowed(null)) { + return; + } + + $subscriptionStatus = (array)$this->getRequest()->getParam('subscription_status'); + $subscriptionStore = (array)$this->getRequest()->getParam('subscription_store'); + if (empty($subscriptionStatus)) { + return; + } + + foreach ($subscriptionStatus as $websiteId => $status) { + $storeId = $subscriptionStore[$websiteId] ?? $customer->getStoreId(); + if ($status) { + $this->subscriptionManager->subscribeCustomer((int)$customer->getId(), $storeId); + } else { + $this->subscriptionManager->unsubscribeCustomer((int)$customer->getId(), $storeId); + } + } + } + /** * Get email notification * @@ -415,7 +461,7 @@ public function execute() private function getEmailNotification() { if (!($this->emailNotification instanceof EmailNotificationInterface)) { - return \Magento\Framework\App\ObjectManager::getInstance()->get( + return ObjectManager::getInstance()->get( EmailNotificationInterface::class ); } else { diff --git a/app/code/Magento/Customer/Model/Address/Validator/General.php b/app/code/Magento/Customer/Model/Address/Validator/General.php index 679f288712b4b..7cbb6ef1ab623 100644 --- a/app/code/Magento/Customer/Model/Address/Validator/General.php +++ b/app/code/Magento/Customer/Model/Address/Validator/General.php @@ -41,7 +41,7 @@ public function __construct( public function validate(AbstractAddress $address) { $errors = array_merge( - $this->checkRequredFields($address), + $this->checkRequiredFields($address), $this->checkOptionalFields($address) ); @@ -55,7 +55,7 @@ public function validate(AbstractAddress $address) * @return array * @throws \Zend_Validate_Exception */ - private function checkRequredFields(AbstractAddress $address) + private function checkRequiredFields(AbstractAddress $address) { $errors = []; if (!\Zend_Validate::is($address->getFirstname(), 'NotEmpty')) { diff --git a/app/code/Magento/Customer/Model/Attribute.php b/app/code/Magento/Customer/Model/Attribute.php index d05bf14fbc97d..98a97872f15f4 100644 --- a/app/code/Magento/Customer/Model/Attribute.php +++ b/app/code/Magento/Customer/Model/Attribute.php @@ -202,9 +202,6 @@ public function canBeFilterableInGrid() /** * @inheritdoc - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __sleep() { @@ -217,9 +214,6 @@ public function __sleep() /** * @inheritdoc - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __wakeup() { diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php index ae342a1b10dd8..cf837e2924161 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php @@ -1,14 +1,18 @@ <?php /** - * Customer address entity resource model - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Customer\Model\ResourceModel\Address; +use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Model\Address; use Magento\Customer\Model\Customer; +use Magento\Customer\Model\CustomerFactory; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Framework\Model\AbstractModel; use Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationInterface; /** @@ -17,29 +21,36 @@ class Relation implements RelationInterface { /** - * @var \Magento\Customer\Model\CustomerFactory + * @var CustomerFactory */ protected $customerFactory; /** - * @param \Magento\Customer\Model\CustomerFactory $customerFactory + * @var CustomerRegistry */ - public function __construct(\Magento\Customer\Model\CustomerFactory $customerFactory) - { + private $customerRegistry; + + /** + * @param CustomerFactory $customerFactory + * @param CustomerRegistry $customerRegistry + */ + public function __construct( + CustomerFactory $customerFactory, + CustomerRegistry $customerRegistry + ) { $this->customerFactory = $customerFactory; + $this->customerRegistry = $customerRegistry; } /** * Process object relations * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object * @return void */ - public function processRelation(\Magento\Framework\Model\AbstractModel $object) + public function processRelation(AbstractModel $object): void { - /** - * @var $object Address - */ + /** @var $object Address */ if (!$object->getIsCustomerSaveTransaction() && $object->getId()) { $customer = $this->customerFactory->create()->load($object->getCustomerId()); @@ -53,6 +64,7 @@ public function processRelation(\Magento\Framework\Model\AbstractModel $object) $changedAddresses, $customer->getResource()->getConnection()->quoteInto('entity_id = ?', $customer->getId()) ); + $this->updateCustomerRegistry($customer, $changedAddresses); } } } @@ -71,12 +83,12 @@ private function getDefaultBillingChangedAddress( array $changedAddresses ): array { if ($object->getIsDefaultBilling()) { - $changedAddresses['default_billing'] = $object->getId(); + $changedAddresses[CustomerInterface::DEFAULT_BILLING] = $object->getId(); } elseif ($customer->getDefaultBillingAddress() && $object->getIsDefaultBilling() === false && (int)$customer->getDefaultBillingAddress()->getId() === (int)$object->getId() ) { - $changedAddresses['default_billing'] = null; + $changedAddresses[CustomerInterface::DEFAULT_BILLING] = null; } return $changedAddresses; @@ -96,27 +108,47 @@ private function getDefaultShippingChangedAddress( array $changedAddresses ): array { if ($object->getIsDefaultShipping()) { - $changedAddresses['default_shipping'] = $object->getId(); + $changedAddresses[CustomerInterface::DEFAULT_SHIPPING] = $object->getId(); } elseif ($customer->getDefaultShippingAddress() && $object->getIsDefaultShipping() === false && (int)$customer->getDefaultShippingAddress()->getId() === (int)$object->getId() ) { - $changedAddresses['default_shipping'] = null; + $changedAddresses[CustomerInterface::DEFAULT_SHIPPING] = null; } return $changedAddresses; } + /** + * Push updated customer entity to the registry. + * + * @param Customer $customer + * @param array $changedAddresses + * @return void + */ + private function updateCustomerRegistry(Customer $customer, array $changedAddresses): void + { + if (array_key_exists(CustomerInterface::DEFAULT_BILLING, $changedAddresses)) { + $customer->setDefaultBilling($changedAddresses[CustomerInterface::DEFAULT_BILLING]); + } + + if (array_key_exists(CustomerInterface::DEFAULT_SHIPPING, $changedAddresses)) { + $customer->setDefaultShipping($changedAddresses[CustomerInterface::DEFAULT_SHIPPING]); + } + + $this->customerRegistry->push($customer); + } + /** * Checks if address has chosen as default and has had an id * * @deprecated Is not used anymore due to changes in logic of save of address. * If address was default and becomes not default than default address id for customer must be * set to null - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object * @return bool */ - protected function isAddressDefault(\Magento\Framework\Model\AbstractModel $object) + protected function isAddressDefault(AbstractModel $object) { return $object->getId() && ($object->getIsDefaultBilling() || $object->getIsDefaultShipping()); } diff --git a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php index 03cf4b1bdddec..0611a2df641e7 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php +++ b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php @@ -17,6 +17,7 @@ use Magento\Customer\Model\Data\CustomerSecureFactory; use Magento\Customer\Model\Delegation\Data\NewOperation; use Magento\Customer\Model\Delegation\Storage as DelegatedStorage; +use Magento\Customer\Model\ResourceModel\Customer\Collection; use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\Api\ExtensibleDataObjectConverter; use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface; @@ -31,6 +32,8 @@ /** * Customer repository. * + * CRUD operations for customer entity + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) */ @@ -187,8 +190,7 @@ public function save(CustomerInterface $customer, $passwordHash = null) { /** @var NewOperation|null $delegatedNewOperation */ $delegatedNewOperation = !$customer->getId() ? $this->delegatedStorage->consumeNewOperation() : null; - $prevCustomerData = null; - $prevCustomerDataArr = null; + $prevCustomerData = $prevCustomerDataArr = null; if ($customer->getId()) { $prevCustomerData = $this->getById($customer->getId()); $prevCustomerDataArr = $prevCustomerData->__toArray(); @@ -214,6 +216,7 @@ public function save(CustomerInterface $customer, $passwordHash = null) $prevCustomerData ? $prevCustomerData->getStoreId() : $this->storeManager->getStore()->getId() ); } + $this->setCustomerGroupId($customerModel, $customerArr, $prevCustomerDataArr); // Need to use attribute set or future updates can cause data loss if (!$customerModel->getAttributeSetId()) { $customerModel->setAttributeSetId(CustomerMetadataInterface::ATTRIBUTE_SET_ID_CUSTOMER); @@ -452,4 +455,18 @@ private function setValidationFlag($customerArray, $customerModel) $customerModel->setData('ignore_validation_flag', true); } } + + /** + * Set customer group id + * + * @param Customer $customerModel + * @param array $customerArr + * @param array $prevCustomerDataArr + */ + private function setCustomerGroupId($customerModel, $customerArr, $prevCustomerDataArr) + { + if (!isset($customerArr['group_id']) && $prevCustomerDataArr && isset($prevCustomerDataArr['group_id'])) { + $customerModel->setGroupId($prevCustomerDataArr['group_id']); + } + } } diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup.xml new file mode 100644 index 0000000000000..49373bb7bebf9 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerSubscribeNewsletterActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertCustomerIsSubscribedToNewsletters"> + <annotations> + <description>Verify that check box "Newsletter subscribed" is checked on "Newsletter" tab on customer edit page.</description> + </annotations> + <arguments> + <argument name="websiteId" type="string" defaultValue="1"/> + </arguments> + + <click selector="{{AdminEditCustomerInformationSection.newsLetter}}" stepKey="clickToNewsletterTabHeader"/> + <waitForPageLoad stepKey="waitForShowNewsletterTab"/> + <seeCheckboxIsChecked selector="{{AdminEditCustomerNewsletterSection.subscribedStatus(websiteId)}}" stepKey="assertSubscribedToNewsletter"/> + </actionGroup> + + <actionGroup name="AdminAssertCustomerIsSubscribedToNewslettersAndSelectedStoreView" extends="AdminAssertCustomerIsSubscribedToNewsletters"> + <annotations> + <description>Verify that check box "Newsletter subscribed" is checked and Store View is selected on "Newsletter" tab on customer edit page.</description> + </annotations> + <arguments> + <argument name="storeView"/> + </arguments> + + <seeOptionIsSelected selector="{{AdminEditCustomerNewsletterSection.subscribedStore(websiteId)}}" userInput="{{storeView.name}}" stepKey="assertSubscribedStoreView" after="assertSubscribedToNewsletter"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertDefaultValueDisableAutoGroupInCustomerFormActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertDefaultValueDisableAutoGroupInCustomerFormActionGroup.xml new file mode 100644 index 0000000000000..8271cdec46df9 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertDefaultValueDisableAutoGroupInCustomerFormActionGroup.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 Default Value for Disable Automatic Group Changes Based on VAT ID in Customer Form --> + <actionGroup name="AdminAssertDefaultValueDisableAutoGroupInCustomerFormActionGroup"> + <annotations> + <description>Check Default Value for Disable Automatic Group Changes Based on VAT ID in Create Customer form.</description> + </annotations> + <arguments> + <argument name="isChecked" type="string"/> + </arguments> + + <grabValueFrom selector="{{AdminCustomerAccountInformationSection.disableAutomaticGroupChange}}" stepKey="grabDisableAutomaticGroupChange"/> + <assertEquals stepKey="assertDisableAutomaticGroupChangeNo" message="pass"> + <expectedResult type="string">{{isChecked}}</expectedResult> + <actualResult type="variable">grabDisableAutomaticGroupChange</actualResult> + </assertEquals> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertErrorMessageCustomerGroupAlreadyExistsActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertErrorMessageCustomerGroupAlreadyExistsActionGroup.xml index 5eb52630d906b..36b41b155b2b3 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertErrorMessageCustomerGroupAlreadyExistsActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertErrorMessageCustomerGroupAlreadyExistsActionGroup.xml @@ -9,7 +9,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminAssertErrorMessageCustomerGroupAlreadyExists" extends="AdminCreateCustomerGroupActionGroup"> <remove keyForRemoval="seeCustomerGroupSaveMessage"/> - <waitForElementVisible selector="{{AdminMessagesSection.errorMessage}}" stepKey="waitForElementVisible"/> - <see selector="{{AdminMessagesSection.errorMessage}}" userInput="Customer Group already exists." stepKey="seeErrorMessage"/> + <waitForElementVisible selector="{{AdminMessagesSection.error}}" stepKey="waitForElementVisible"/> + <see selector="{{AdminMessagesSection.error}}" userInput="Customer Group already exists." stepKey="seeErrorMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup.xml new file mode 100644 index 0000000000000..49ea772569cc0 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerSubscribeNewsletterActionGroup.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="AdminSubscribeCustomerToNewsletters"> + <annotations> + <description>Set checkbox "Newsletter subscribed" on "Newsletter" tab on customer edit page.</description> + </annotations> + <arguments> + <argument name="websiteId" type="string" defaultValue="1"/> + </arguments> + + <click selector="{{AdminEditCustomerInformationSection.newsLetter}}" stepKey="clickToNewsletterTabHeader"/> + <waitForPageLoad stepKey="waitForShowNewsletterTab"/> + <checkOption selector="{{AdminEditCustomerNewsletterSection.subscribedStatus(websiteId)}}" stepKey="subscribeToNewsletter"/> + <click selector="{{AdminCustomerMainActionsSection.saveAndContinue}}" stepKey="saveAndContinue"/> + <waitForPageLoad stepKey="waitForSaving"/> + <see userInput="You saved the customer." stepKey="seeSuccessMessage"/> + </actionGroup> + + <actionGroup name="AdminSubscribeCustomerToNewslettersAndSelectStoreView" extends="AdminSubscribeCustomerToNewsletters"> + <annotations> + <description>Set checkbox "Newsletter subscribed" and select Store View on "Newsletter" tab on customer edit page.</description> + </annotations> + <arguments> + <argument name="storeView"/> + </arguments> + <selectOption selector="{{AdminEditCustomerNewsletterSection.subscribedStore(websiteId)}}" userInput="{{storeView.name}}" stepKey="selectSubscribeStoreView" after="subscribeToNewsletter"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerGroupActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerGroupActionGroup.xml index ab5ae53fd4caa..8171b97258157 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerGroupActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerGroupActionGroup.xml @@ -15,7 +15,7 @@ <arguments> <argument name="customerGroupName" type="string"/> </arguments> - + <amOnPage url="{{AdminCustomerGroupsIndexPage.url}}" stepKey="goToAdminCustomerGroupIndexPage"/> <waitForPageLoad time="30" stepKey="waitForCustomerGroupIndexPageLoad"/> <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openFiltersSectionOnCustomerGroupIndexPage"/> @@ -24,6 +24,8 @@ <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> <click selector="{{AdminCustomerGroupGridActionsSection.selectButton(customerGroupName)}}" stepKey="clickSelectButton"/> <click selector="{{AdminCustomerGroupGridActionsSection.deleteAction(customerGroupName)}}" stepKey="clickOnDeleteItem"/> + <waitForElementVisible selector="{{AdminGridConfirmActionSection.message}}" stepKey="waitForConfirmModal"/> + <see selector="{{AdminGridConfirmActionSection.message}}" userInput="Are you sure you want to delete a {{customerGroupName}} record?" stepKey="seeRemoveMessage"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDeleteCustomerGroup"/> <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSuccessMessage"/> </actionGroup> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminNavigateCustomerEditPageAddressesTabActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminNavigateCustomerEditPageAddressesTabActionGroup.xml new file mode 100644 index 0000000000000..790a99c9092bc --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminNavigateCustomerEditPageAddressesTabActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminNavigateCustomerEditPageAddressesTabActionGroup" extends="AdminOpenCustomerEditPageActionGroup"> + <annotations> + <description>EXTENDS: AdminOpenCustomerEditPageActionGroup. Navigates to Addresses Tab in Admin Customer Edit page for the provided Customer ID #.</description> + </annotations> + + <click selector="{{AdminEditCustomerInformationSection.addresses}}" after="waitForPageLoad" stepKey="navigateToAddressesTab"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminNavigateNewCustomerActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminNavigateNewCustomerActionGroup.xml new file mode 100644 index 0000000000000..81c788fc4445a --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminNavigateNewCustomerActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminNavigateNewCustomerActionGroup"> + <annotations> + <description>Goes to the New Customer page.</description> + </annotations> + + <amOnPage url="{{AdminNewCustomerPage.url}}" stepKey="navigateToCustomers"/> + <waitForPageLoad stepKey="waitForLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminSaveCustomerAddressActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminSaveCustomerAddressActionGroup.xml index e47aa8809f080..62c35dd230f10 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminSaveCustomerAddressActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminSaveCustomerAddressActionGroup.xml @@ -9,6 +9,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminSaveCustomerAddressActionGroup"> <click selector="{{StorefrontCustomerAddressFormSection.saveAddress}}" stepKey="saveCustomerAddress"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="You saved the address." stepKey="seeSuccessMessage"/> + <see selector="{{StorefrontMessagesSection.success}}" userInput="You saved the address." stepKey="seeSuccessMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminUpdateCustomerGenderInCustomersGridActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminUpdateCustomerGenderInCustomersGridActionGroup.xml new file mode 100644 index 0000000000000..0b51140471ab7 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminUpdateCustomerGenderInCustomersGridActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminUpdateCustomerGenderInCustomersGridActionGroup"> + <annotations> + <description>Update customer gender attribute value on customers grid page</description> + </annotations> + <arguments> + <argument name="customerEmail" defaultValue="{{Simple_US_Customer.email}}" type="string"/> + <argument name="genderValue" defaultValue="{{Gender.empty}}" type="string"/> + </arguments> + + <click selector="{{AdminDataGridTableSection.rowTemplate(customerEmail)}}" stepKey="clickCustomersGridRow"/> + <waitForElementVisible selector="{{AdminCustomerGridInlineEditorSection.customerGenderEditor}}" stepKey="waitForGenderElementAppears"/> + <selectOption userInput="{{genderValue}}" selector="{{AdminCustomerGridInlineEditorSection.customerGenderEditor}}" stepKey="selectGenderValue"/> + <click selector="{{AdminCustomerGridInlineEditorSection.saveInGrid}}" stepKey="saveCustomer"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertAdminCustomerGenderInCustomersGridActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertAdminCustomerGenderInCustomersGridActionGroup.xml new file mode 100644 index 0000000000000..8b73a9c3307e3 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertAdminCustomerGenderInCustomersGridActionGroup.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="AssertAdminCustomerGenderInCustomersGridActionGroup"> + <annotations> + <description>Assert customer "Gender" attribute value on customer grid page</description> + </annotations> + <arguments> + <argument name="customerEmail" defaultValue="{{Simple_US_Customer.email}}" type="string"/> + <argument name="expectedGenderValue" defaultValue="{{Gender.empty}}" type="string"/> + </arguments> + + <see userInput="{{expectedGenderValue}}" selector="{{AdminCustomerGridSection.customerGenderByEmail(customerEmail)}}" stepKey="assertGenderValue"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertAdminCustomerGenderOnCustomerFormActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertAdminCustomerGenderOnCustomerFormActionGroup.xml new file mode 100644 index 0000000000000..b21b0054b0c78 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertAdminCustomerGenderOnCustomerFormActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminCustomerGenderOnCustomerFormActionGroup"> + <annotations> + <description>Validates that the provided Customer Gender is selected on the Admin Customer edit page.</description> + </annotations> + <arguments> + <argument name="customerGender" defaultValue="{{Gender.empty}}" type="string"/> + </arguments> + + <conditionalClick selector="{{AdminCustomerAccountInformationSection.accountInformationTab}}" dependentSelector="{{AdminCustomerAccountInformationSection.gender}}" visible="false" stepKey="clickOnAccountInfoTab"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <seeOptionIsSelected userInput="{{customerGender}}" selector="{{AdminCustomerAccountInformationSection.gender}}" stepKey="verifyNeededCustomerGenderSelected"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerCreateAccountPasswordComplexityActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerCreateAccountPasswordComplexityActionGroup.xml new file mode 100644 index 0000000000000..b62f5435275a3 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertMessageCustomerCreateAccountPasswordComplexityActionGroup.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="AssertMessageCustomerCreateAccountPasswordComplexityActionGroup"> + <annotations> + <description>Assert is shown an error about their password during new user form filling.</description> + </annotations> + <arguments> + <argument name="message" type="string"/> + </arguments> + <see userInput="{{message}}" selector="{{StorefrontCustomerCreateFormSection.passwordErrorMessages}}" stepKey="verifyMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml index 60a8a49954bab..e338d1ae4bbd0 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/OpenEditCustomerFromAdminActionGroup.xml @@ -15,7 +15,7 @@ <arguments> <argument name="customer"/> </arguments> - + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="navigateToCustomers"/> <waitForPageLoad stepKey="waitForPageLoad1"/> <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilters"/> @@ -26,7 +26,7 @@ <click selector="{{AdminCustomerGridSection.firstRowEditLink}}" stepKey="clickEdit"/> <waitForPageLoad stepKey="waitForPageLoad3"/> </actionGroup> - + <actionGroup name="OpenEditCustomerAddressFromAdminActionGroup"> <annotations> <description>Filters the Admin Customers Addresses based on the provided Address. Clicks on Edit.</description> @@ -34,7 +34,7 @@ <arguments> <argument name="address"/> </arguments> - + <click selector="{{AdminCustomerAccountInformationSection.addressesButton}}" stepKey="openAddressesTab"/> <waitForElementVisible selector="{{AdminCustomerAddressFiltersSection.filtersButton}}" stepKey="waitForComponentLoad"/> <click selector="{{AdminCustomerAddressFiltersSection.filtersButton}}" stepKey="openAddressesFilter"/> @@ -67,7 +67,7 @@ <click selector="{{AdminCustomerGridMainActionsSection.delete}}" stepKey="clickDelete"/> <waitForAjaxLoad stepKey="waitForLoadConfirmation"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="A total of 1 record(s) were deleted" stepKey="seeSuccess"/> + <see selector="{{AdminMessagesSection.success}}" userInput="A total of 1 record(s) were deleted" stepKey="seeSuccess"/> </actionGroup> <actionGroup name="AdminClearCustomersFiltersActionGroup"> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml index 44988b202ab57..4260417b46fd0 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml @@ -117,10 +117,10 @@ <arguments> <argument name="address"/> </arguments> - - <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> + + <amOnPage url="{{StorefrontCustomerAddressesPage.url}}" 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"/> @@ -139,7 +139,7 @@ <argument name="address"/> </arguments> - <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> + <amOnPage url="{{StorefrontCustomerAddressesPage.url}}" stepKey="goToAddressPage"/> <waitForPageLoad stepKey="waitForAddressPageLoad"/> <!--Verify customer default shipping address--> <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="{{address.firstname}} {{address.lastname}}" stepKey="seeAssertCustomerDefaultShippingAddressFirstnameAndLastname"/> @@ -159,7 +159,7 @@ <argument name="address"/> </arguments> - <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> + <amOnPage url="{{StorefrontCustomerAddressesPage.url}}" stepKey="goToAddressPage"/> <waitForPageLoad stepKey="waitForAddressPageLoad"/> <!--Verify customer default billing address--> @@ -180,7 +180,7 @@ <argument name="address"/> </arguments> - <amOnPage url="customer/address/index/" stepKey="goToAddressPage"/> + <amOnPage url="{{StorefrontCustomerAddressesPage.url}}" stepKey="goToAddressPage"/> <waitForPageLoad stepKey="waitForAddressPageLoad"/> <!--Verify customer default shipping address--> @@ -202,7 +202,7 @@ </arguments> <!--Verify customer name on frontend--> - <amOnPage url="customer/account/edit/" stepKey="goToAddressPage"/> + <amOnPage url="{{StorefrontCustomerEditPage.url}}" stepKey="goToAddressPage"/> <waitForPageLoad stepKey="waitForAddressPageLoad"/> <click selector="{{StorefrontCustomerSidebarSection.sidebarCurrentTab('Account Information')}}" stepKey="clickAccountInformationFromSidebarCurrentTab"/> <waitForPageLoad stepKey="waitForAccountInformationTabToOpen"/> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAssertCustomerAddressItemsActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAssertCustomerAddressItemsActionGroup.xml new file mode 100644 index 0000000000000..e8dceb8f65926 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAssertCustomerAddressItemsActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAssertCustomerAddressItemsActionGroup"> + <annotations> + <description>Validate that the Storefront Customer Address contains correct items.</description> + </annotations> + <arguments> + <argument name="address" type="entity"/> + </arguments> + + <seeInField selector="{{StorefrontCustomerAddressFormSection.firstName}}" userInput="{{address.firstName}}" stepKey="seeFirstName"/> + <seeInField selector="{{StorefrontCustomerAddressFormSection.lastName}}" userInput="{{address.lastName}}" stepKey="seeLastName"/> + <seeInField selector="{{StorefrontCustomerAddressFormSection.company}}" userInput="{{address.company}}" stepKey="seeCompany"/> + <seeInField selector="{{StorefrontCustomerAddressFormSection.phoneNumber}}" userInput="{{address.telephone}}" stepKey="seePhoneNumber"/> + <seeInField selector="{{StorefrontCustomerAddressFormSection.streetAddress}}" userInput="{{address.street[0]}}" stepKey="seeStreet"/> + <seeInField selector="{{StorefrontCustomerAddressFormSection.city}}" userInput="{{address.city}}" stepKey="seeCity"/> + <seeInField selector="{{StorefrontCustomerAddressFormSection.state}}" userInput="{{address.state}}" stepKey="seeState"/> + <seeInField selector="{{StorefrontCustomerAddressFormSection.zip}}" userInput="{{address.postcode}}" stepKey="seePostcode"/> + <seeInField selector="{{StorefrontCustomerAddressFormSection.country}}" userInput="{{address.country}}" stepKey="seeCountry"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerElementNotVisibleActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerElementNotVisibleActionGroup.xml new file mode 100644 index 0000000000000..12a0b8f47c5fa --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerElementNotVisibleActionGroup.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCustomerReorderButtonNotVisibleActionGroup"> + <dontSeeElement selector="{{StorefrontCustomerOrderViewSection.reorder}}" stepKey="assertNotVisibleElement"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontNavigateToCustomerOrdersHistoryPageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontNavigateToCustomerOrdersHistoryPageActionGroup.xml new file mode 100644 index 0000000000000..40a79b87b01a9 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontNavigateToCustomerOrdersHistoryPageActionGroup.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="StorefrontNavigateToCustomerOrdersHistoryPageActionGroup"> + <amOnPage url="{{StorefrontCustomerOrdersHistoryPage.url}}" stepKey="amOnTheCustomerPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </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 74365ef02fe87..08e13885d10d4 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -292,7 +292,7 @@ <item>Piwowarska 6</item> </array> <data key="city">Bielsko-Biała</data> - <data key="state"> Bielsko</data> + <data key="state">śląskie</data> <data key="country_id">PL</data> <data key="country">Poland</data> <data key="postcode">43-310</data> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerConfigData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerConfigData.xml index ab4307082595d..3eb14604220e9 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerConfigData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerConfigData.xml @@ -51,4 +51,14 @@ <data key="label">5</data> <data key="value">5</data> </entity> + <entity name="CustomerAccountShareWebsiteConfigData"> + <data key="path">customer/account_share/scope</data> + <data key="label">Per Website</data> + <data key="value">1</data> + </entity> + <entity name="CustomerAccountShareGlobalConfigData"> + <data key="path">customer/account_share/scope</data> + <data key="label">Global</data> + <data key="value">0</data> + </entity> </entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml index 093d6a05e8c5c..b9227505871cf 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml @@ -300,6 +300,24 @@ <requiredEntity type="address">US_Address_TX</requiredEntity> <requiredEntity type="address">US_Address_NY_Not_Default_Address</requiredEntity> </entity> + <entity name="Simple_Customer_With_Password_Length_Is_Below_Eight_Characters" type="customer"> + <data key="group_id">1</data> + <data key="email" unique="prefix">John.Doe@example.com</data> + <data key="firstname">John</data> + <data key="lastname">Doe</data> + <data key="fullname">John Doe</data> + <data key="password">123123</data> + <data key="store_id">0</data> + <data key="website_id">0</data> + </entity> + <entity name="Simple_Customer_With_Not_Secure_Password" type="customer"> + <data key="group_id">1</data> + <data key="email" unique="prefix">John.Doe@example.com</data> + <data key="firstname">John</data> + <data key="lastname">Doe</data> + <data key="fullname">John Doe</data> + <data key="password">123123qa</data> + </entity> <entity name="Simple_US_Customer_Incorrect_Email" type="customer"> <data key="group_id">0</data> <data key="default_billing">true</data> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml index 28305d37cf77b..68e4090d64910 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml @@ -30,7 +30,7 @@ </array> </entity> <entity name="CustomCustomerGroup" type="customerGroup"> - <data key="code" unique="suffix">Group </data> + <data key="code" unique="suffix">Group-</data> <data key="tax_class_id">3</data> <data key="tax_class_name">Retail Customer</data> </entity> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/GenderData.xml b/app/code/Magento/Customer/Test/Mftf/Data/GenderData.xml new file mode 100644 index 0000000000000..68dee0ffa31d0 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Data/GenderData.xml @@ -0,0 +1,17 @@ +<?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="Gender" type="gender"> + <data key="empty"/> + <data key="male">Male</data> + <data key="female">Female</data> + <data key="not_specified">Not Specified</data> + </entity> +</entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Page/AdminCustomerPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/AdminCustomerPage.xml index 114c737e361ed..d77dc15840e4c 100644 --- a/app/code/Magento/Customer/Test/Mftf/Page/AdminCustomerPage.xml +++ b/app/code/Magento/Customer/Test/Mftf/Page/AdminCustomerPage.xml @@ -13,5 +13,6 @@ <section name="AdminCustomerMessagesSection"/> <section name="AdminCustomerGridSection"/> <section name="AdminCustomerFiltersSection"/> + <section name="AdminCustomerGridInlineEditorSection"/> </page> </pages> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountInformationSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountInformationSection.xml index 4b36486f0bd17..2c9e66c15bbab 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountInformationSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountInformationSection.xml @@ -17,6 +17,7 @@ <element name="firstName" type="input" selector="input[name='customer[firstname]']"/> <element name="lastName" type="input" selector="input[name='customer[lastname]']"/> <element name="email" type="input" selector="input[name='customer[email]']"/> + <element name="disableAutomaticGroupChange" type="input" selector="input[name='customer[disable_auto_group_change]']"/> <element name="group" type="select" selector="[name='customer[group_id]']"/> <element name="groupIdValue" type="text" selector="//*[@name='customer[group_id]']/option"/> <element name="groupValue" type="button" selector="//span[text()='{{groupValue}}']" parameterized="true"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerCreateNewOrderSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerCreateNewOrderSection.xml index a01687990999e..6b0cafe8dc00b 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerCreateNewOrderSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerCreateNewOrderSection.xml @@ -12,7 +12,7 @@ <element name="updateChangesBtn" type="button" selector=".order-sidebar .actions .action-default.scalable" timeout="30"/> <element name="productName" type="text" selector="#order-items_grid span[id*=order_item]"/> <element name="productPrice" type="text" selector=".even td[class=col-price] span[class=price]"/> - <element name="productQty" type="input" selector="td[class=col-qty] input"/> + <element name="productQty" type="input" selector="td[class=col-qty] .input-text.item-qty.admin__control-text"/> <element name="gridCell" type="text" selector="//div[contains(@id, 'order-items_grid')]//tbody[{{row}}]//td[count(//table[contains(@class, 'order-tables')]//th[contains(., '{{column}}')]/preceding-sibling::th) +1 ]" parameterized="true" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridInlineEditorSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridInlineEditorSection.xml new file mode 100644 index 0000000000000..d010844cfffcf --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridInlineEditorSection.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="AdminCustomerGridInlineEditorSection"> + <element name="customerGenderEditor" type="select" selector="tr.data-grid-editable-row:not([style*='display: none']) [name='gender']"/> + <element name="saveInGrid" type="button" selector="tr.data-grid-editable-row-actions button.action-primary" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml index 91363c614c1f8..9562a902b26da 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml @@ -16,5 +16,7 @@ <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"/> + <element name="customerGenderByEmail" type="text" selector="//tr[@class='data-row']//div[text()='{{customerEmail}}']/ancestor::tr/td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., 'Gender')]/preceding-sibling::th) +1]" parameterized="true"/> </section> </sections> + diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerNewsletterSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerNewsletterSection.xml index 51b4b54c5c8b6..e6bdf2819e2a5 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerNewsletterSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerNewsletterSection.xml @@ -9,6 +9,8 @@ <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']"/> + <element name="subscribedStatus" type="checkbox" selector="//div[@class='admin__field-control control']//input[@name='subscription_status[{{websiteId}}]']" parameterized="true"/> + <element name="subscribedStore" type="select" selector="//div[@class='admin__field-control control']//select[@name='subscription_store[{{websiteId}}]']" parameterized="true"/> + <element name="subscribedLastUpdatedDate" type="text" selector="//div[@class='admin__field-control control']//div[@class='field-change_status_date_{{websiteId}}']//div[@class='control-value']" parameterized="true"/> </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 ba1f10a480745..9fc26a03b04ee 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml @@ -17,6 +17,7 @@ <element name="passwordField" type="input" selector="#password"/> <element name="confirmPasswordField" type="input" selector="#password-confirmation"/> <element name="createAccountButton" type="button" selector="button.action.submit.primary" timeout="30"/> + <element name="passwordErrorMessages" type="text" selector="#password-error"/> </section> <section name="StoreFrontCustomerAdvancedAttributesSection"> <element name="textFieldAttribute" type="input" selector="//input[@id='{{var}}']" parameterized="true" /> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminChangeCustomerGenderInCustomersGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminChangeCustomerGenderInCustomersGridTest.xml new file mode 100644 index 0000000000000..7ca9a6993f2fc --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminChangeCustomerGenderInCustomersGridTest.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminChangeCustomerGenderInCustomersGridTest"> + <annotations> + <features value="Customer"/> + <stories value="Update Customer"/> + <title value="Gender attribute blank value is saved in direct edits from customer grid"/> + <description value="Check that gender attribute blank value can be saved on customers grid"/> + <severity value="MAJOR"/> + <testCaseId value="MC-22025"/> + <useCaseId value="MC-17259"/> + <group value="customer"/> + </annotations> + <before> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + </before> + <after> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <!-- Reset customer grid filter --> + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="goToCustomersGridPage"/> + <waitForPageLoad stepKey="waitForCustomersGrid"/> + <actionGroup ref="AdminResetFilterInCustomerGrid" stepKey="resetFilter"/> + + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Open customers grid page, filter by created customer--> + <actionGroup ref="AdminFilterCustomerByEmail" stepKey="filterCustomerGridByEmail"> + <argument name="email" value="$createCustomer.email$"/> + </actionGroup> + <!-- Check customer is in grid--> + <actionGroup ref="AdminAssertCustomerInCustomersGrid" stepKey="assertCustomerInCustomersGrid"> + <argument name="text" value="$createCustomer.email$"/> + <argument name="row" value="1"/> + </actionGroup> + <!--Check customer Gender value in grid--> + <actionGroup ref="AssertAdminCustomerGenderInCustomersGridActionGroup" stepKey="assertCustomerGenderInCustomersGrid"> + <argument name="customerEmail" value="$createCustomer.email$"/> + </actionGroup> + <!--Update customer Gender to Male--> + <actionGroup ref="AdminUpdateCustomerGenderInCustomersGridActionGroup" stepKey="updateCustomerGenderWithMaleValueInCustomersGrid"> + <argument name="customerEmail" value="$createCustomer.email$"/> + <argument name="genderValue" value="{{Gender.male}}"/> + </actionGroup> + <!--Check customer Gender value in grid--> + <actionGroup ref="AssertAdminCustomerGenderInCustomersGridActionGroup" stepKey="assertCustomerGenderMaleInCustomersGrid"> + <argument name="customerEmail" value="$createCustomer.email$"/> + <argument name="expectedGenderValue" value="{{Gender.male}}"/> + </actionGroup> + <!--Open customer edit page and check Gender value--> + <actionGroup ref="AdminOpenCustomerEditPageActionGroup" stepKey="openCustomerEditPageWithMaleGender"> + <argument name="customerId" value="$createCustomer.id$"/> + </actionGroup> + <actionGroup ref="AssertAdminCustomerGenderOnCustomerFormActionGroup" stepKey="assertCustomerGenderValueIsMaleOnCustomerForm"> + <argument name="customerGender" value="{{Gender.male}}"/> + </actionGroup> + <!--Filter customers grid by email--> + <actionGroup ref="AdminFilterCustomerByEmail" stepKey="filterCustomerByEmailToUpdateWithEmptyGender"> + <argument name="email" value="$createCustomer.email$"/> + </actionGroup> + <!--Update customer Gender to empty value--> + <actionGroup ref="AdminUpdateCustomerGenderInCustomersGridActionGroup" stepKey="updateCustomerGenderWithEmptyValueInCustomersGrid"> + <argument name="customerEmail" value="$createCustomer.email$"/> + </actionGroup> + <!--Check customer Gender value in grid--> + <actionGroup ref="AssertAdminCustomerGenderInCustomersGridActionGroup" stepKey="assertCustomerGenderEmptyInCustomersGrid"> + <argument name="customerEmail" value="$createCustomer.email$"/> + </actionGroup> + <!--Open customer edit page and check Gender value--> + <actionGroup ref="AdminOpenCustomerEditPageActionGroup" stepKey="openCustomerEditPageWithEmptyGender"> + <argument name="customerId" value="$createCustomer.id$"/> + </actionGroup> + <actionGroup ref="AssertAdminCustomerGenderOnCustomerFormActionGroup" stepKey="assertCustomerGenderValueIsEmptyOnCustomerForm"/> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsNoTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsNoTest.xml new file mode 100644 index 0000000000000..be96765920bf5 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsNoTest.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckDefaultValueDisableAutoGroupChangeIsNoTest"> + <annotations> + <features value="Customer"/> + <stories value="Default Value for Disable Automatic Group Changes Based on VAT ID"/> + <title value="Check settings Default Value for Disable Automatic Group Changes Based on VAT ID is No"/> + <description value="Check settings Default Value for Disable Automatic Group Changes Based on VAT ID is No"/> + <severity value="MAJOR"/> + <group value="customer"/> + <group value="create"/> + </annotations> + <before> + <magentoCLI command="config:set customer/create_account/viv_disable_auto_group_assign_default 0" stepKey="setConfigDefaultIsNo"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + </before> + <after> + <actionGroup ref="logout" stepKey="adminLogout"/> + </after> + + <actionGroup ref="AdminNavigateNewCustomerActionGroup" stepKey="navigateToNewCustomer"/> + + <actionGroup ref="AdminAssertDefaultValueDisableAutoGroupInCustomerFormActionGroup" stepKey="seeDefaultValueInForm"> + <argument name="isChecked" value="0"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsYesTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsYesTest.xml new file mode 100644 index 0000000000000..87cba0c10dbc1 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCheckDefaultValueDisableAutoGroupChangeIsYesTest.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="AdminCheckDefaultValueDisableAutoGroupChangeIsYesTest"> + <annotations> + <features value="Customer"/> + <stories value="Default Value for Disable Automatic Group Changes Based on VAT ID"/> + <title value="Check settings Default Value for Disable Automatic Group Changes Based on VAT ID is Yes"/> + <description value="Check settings Default Value for Disable Automatic Group Changes Based on VAT ID is Yes"/> + <severity value="MAJOR"/> + <group value="customer"/> + <group value="create"/> + </annotations> + <before> + <magentoCLI command="config:set customer/create_account/viv_disable_auto_group_assign_default 1" stepKey="setConfigDefaultIsYes"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> + </before> + <after> + <magentoCLI command="config:set customer/create_account/viv_disable_auto_group_assign_default 0" stepKey="setConfigDefaultIsNo"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + <actionGroup ref="logout" stepKey="adminLogout"/> + </after> + + <actionGroup ref="AdminNavigateNewCustomerActionGroup" stepKey="navigateToNewCustomer"/> + + <actionGroup ref="AdminAssertDefaultValueDisableAutoGroupInCustomerFormActionGroup" stepKey="seeDefaultValueInForm"> + <argument name="isChecked" value="1"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryPolandTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryPolandTest.xml index cbc8b89d3f242..d2435a093046a 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryPolandTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerWithCountryPolandTest.xml @@ -34,7 +34,7 @@ <click selector="{{AdminCustomerGridSection.firstRowEditLink}}" stepKey="clickOnEditButton"/> <waitForPageLoad stepKey="waitForCustomerEditPageToLoad"/> - <!--Add the Address --> + <!-- Add the Address --> <click selector="{{AdminEditCustomerAddressesSection.addresses}}" stepKey="selectAddress"/> <waitForPageLoad stepKey="waitForAddressPageToLoad"/> <click selector="{{AdminEditCustomerAddressesSection.addNewAddress}}" stepKey="ClickOnAddNewAddressButton"/> @@ -44,6 +44,7 @@ <fillField selector="{{AdminEditCustomerAddressesSection.city}}" userInput="{{PolandAddress.city}}" stepKey="fillCity"/> <scrollTo selector="{{AdminEditCustomerAddressesSection.phone}}" x="0" y="-80" stepKey="scrollToPhone"/> <selectOption selector="{{AdminEditCustomerAddressesSection.country}}" userInput="{{PolandAddress.country}}" stepKey="fillCountry"/> + <selectOption selector="{{AdminEditCustomerAddressesSection.state}}" userInput="{{PolandAddress.state}}" stepKey="fillState"/> <fillField selector="{{AdminEditCustomerAddressesSection.zipCode}}" userInput="{{PolandAddress.postcode}}" stepKey="fillPostCode"/> <fillField selector="{{AdminEditCustomerAddressesSection.phone}}" userInput="{{PolandAddress.telephone}}" stepKey="fillPhoneNumber"/> <scrollToTopOfPage stepKey="scrollToTopOfPage"/> @@ -60,6 +61,7 @@ <see userInput="$$createCustomer.firstname$$" selector="{{AdminCustomerGridSection.customerGrid}}" stepKey="assertFirstName"/> <see userInput="$$createCustomer.lastname$$" selector="{{AdminCustomerGridSection.customerGrid}}" stepKey="assertLastName"/> <see userInput="$$createCustomer.email$$" selector="{{AdminCustomerGridSection.customerGrid}}" stepKey="assertEmail"/> + <see userInput="{{PolandAddress.state}}" selector="{{AdminCustomerGridSection.customerGrid}}" stepKey="assertState"/> <see userInput="{{PolandAddress.country}}" selector="{{AdminCustomerGridSection.customerGrid}}" stepKey="assertCountry"/> <see userInput="{{PolandAddress.postcode}}" selector="{{AdminCustomerGridSection.customerGrid}}" stepKey="assertPostCode"/> <see userInput="{{PolandAddress.telephone}}" selector="{{AdminCustomerGridSection.customerGrid}}" stepKey="assertPhoneNumber"/> @@ -86,6 +88,7 @@ <see selector="{{AdminCustomerAddressesGridSection.customerAddressGrid}}" userInput="{{PolandAddress.street}}" stepKey="seeStreetAddress"/> <see selector="{{AdminCustomerAddressesGridSection.customerAddressGrid}}" userInput="{{PolandAddress.city}}" stepKey="seeCity"/> <see selector="{{AdminCustomerAddressesGridSection.customerAddressGrid}}" userInput="{{PolandAddress.country}}" stepKey="seeCountry"/> + <see selector="{{AdminCustomerAddressesGridSection.customerAddressGrid}}" userInput="{{PolandAddress.state}}" stepKey="seeState"/> <see selector="{{AdminCustomerAddressesGridSection.customerAddressGrid}}" userInput="{{PolandAddress.postcode}}" stepKey="seePostCode"/> <see selector="{{AdminCustomerAddressesGridSection.customerAddressGrid}}" userInput="{{PolandAddress.telephone}}" stepKey="seePhoneNumber"/> </test> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml index 22ad60ff5de34..5d09f819bcbc0 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml @@ -49,6 +49,6 @@ <waitForPageLoad stepKey="waitForEditLinkLoad"/> <click selector="{{AdminEditCustomerInformationSection.newsLetter}}" stepKey="clickNewsLetter"/> <waitForPageLoad stepKey="waitForNewsletterTabToOpen"/> - <seeCheckboxIsChecked selector="{{AdminEditCustomerNewsletterSection.subscribedToNewsletter}}" stepKey="seeAssertSubscribedToNewsletterCheckboxIsChecked"/> + <seeCheckboxIsChecked selector="{{AdminEditCustomerNewsletterSection.subscribedStatus('1')}}" stepKey="seeAssertSubscribedToNewsletterCheckboxIsChecked"/> </test> </tests> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateRetailCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateRetailCustomerGroupTest.xml index 4f1d88ffe99f5..6e08d98a53c56 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateRetailCustomerGroupTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateRetailCustomerGroupTest.xml @@ -26,7 +26,7 @@ <actionGroup ref="AdminDeleteCustomerGroupActionGroup" stepKey="deleteCustomerGroup"> <argument name="customerGroupName" value="{{CustomCustomerGroup.code}}"/> </actionGroup> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilters"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilters"/> <actionGroup ref="logout" stepKey="logout"/> </after> <!-- Steps: 1. Log in to backend as admin user. diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateTaxClassCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateTaxClassCustomerGroupTest.xml index 7d54ede7c1612..4b539ec350435 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateTaxClassCustomerGroupTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateTaxClassCustomerGroupTest.xml @@ -31,7 +31,7 @@ <actionGroup ref="AdminDeleteCustomerGroupActionGroup" stepKey="deleteCustomerGroup"> <argument name="customerGroupName" value="{{CustomCustomerGroup.code}}"/> </actionGroup> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilters"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilters"/> <deleteData createDataKey="createCustomerTaxClass" stepKey="deleteCustomerTaxClass"/> <actionGroup ref="logout" stepKey="logout"/> </after> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomerSubscribeNewsletterPerWebsiteTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomerSubscribeNewsletterPerWebsiteTest.xml new file mode 100644 index 0000000000000..6c1a27c395917 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCustomerSubscribeNewsletterPerWebsiteTest.xml @@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCustomerSubscribeNewsletterPerWebsiteTest"> + <annotations> + <features value="Customer"/> + <stories value="Customer Subscriptions"/> + <title value="Newsletter subscriptions per website"/> + <description value="Admin should be able to subscribe customer to newsletters on each website separately"/> + <testCaseId value="MC-22173"/> + <severity value="MAJOR"/> + <group value="customer"/> + </annotations> + <before> + <createData entity="CustomerAccountSharingGlobal" stepKey="setConfigCustomerAccountToGlobal"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> + <argument name="customStore" value="NewStoreViewData"/> + </actionGroup> + <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteCustomer"> + <argument name="email" value="{{CustomerEntityOne.email}}"/> + </actionGroup> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> + <argument name="websiteName" value="{{secondCustomWebsite.name}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + <createData entity="CustomerAccountSharingDefault" stepKey="setConfigCustomerAccountDefault"/> + </after> + + <!-- Create a new Store View --> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"> + <argument name="customStore" value="NewStoreViewData"/> + </actionGroup> + <!-- Switch to the new Store View on storefront --> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage"/> + <waitForPageLoad stepKey="waitForNavigateHomePage"/> + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchToCustomStoreView"> + <argument name="storeView" value="NewStoreViewData"/> + </actionGroup> + <!-- Create a new customer and sign up newsletter on the new Store View --> + <actionGroup ref="StorefrontCreateCustomerSignedUpNewsletterActionGroup" stepKey="createCustomer"> + <argument name="customer" value="CustomerEntityOne" /> + </actionGroup> + <!-- Go to the customer edit page on admin area --> + <actionGroup ref="AdminFilterCustomerByEmail" stepKey="filterCustomerGrid"> + <argument name="email" value="{{CustomerEntityOne.email}}"/> + </actionGroup> + <click selector="{{AdminCustomerGridSection.firstRowEditLink}}" stepKey="clickToEditCustomerPage"/> + <waitForPageLoad stepKey="waitForOpenCustomerPage"/> + <grabFromCurrentUrl regex="~(\d+)/~" stepKey="grabCustomerId"/> + <!-- Assert that created customer is subscribed to newsletter on the new Store View --> + <actionGroup ref="AdminAssertCustomerIsSubscribedToNewslettersAndSelectedStoreView" stepKey="assertSubscribedToNewsletter"> + <argument name="storeView" value="NewStoreViewData"/> + </actionGroup> + <!-- Create second website --> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createSecondWebsite"> + <argument name="newWebsiteName" value="{{secondCustomWebsite.name}}"/> + <argument name="websiteCode" value="{{secondCustomWebsite.code}}"/> + </actionGroup> + <!-- Create second store --> + <actionGroup ref="AdminCreateNewStoreGroupActionGroup" stepKey="createSecondStoreGroup"> + <argument name="website" value="{{secondCustomWebsite.name}}"/> + <argument name="storeGroupName" value="{{SecondStoreGroupUnique.name}}"/> + <argument name="storeGroupCode" value="{{SecondStoreGroupUnique.code}}"/> + </actionGroup> + <!-- Create second store view --> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createSecondStoreView"> + <argument name="StoreGroup" value="SecondStoreGroupUnique"/> + <argument name="customStore" value="SecondStoreUnique"/> + </actionGroup> + <!-- Grab second website id into $grabFromCurrentUrlGetSecondWebsiteId --> + <actionGroup ref="AdminGetWebsiteIdActionGroup" stepKey="getSecondWebsiteId"> + <argument name="website" value="secondCustomWebsite"/> + </actionGroup> + <!-- Go to the customer edit page on admin area --> + <actionGroup ref="AdminOpenCustomerEditPageActionGroup" stepKey="openCustomerEditPage"> + <argument name="customerId" value="$grabCustomerId"/> + </actionGroup> + <!-- Assert that customer still subscribed to newsletter on default website --> + <actionGroup ref="AdminAssertCustomerIsSubscribedToNewsletters" stepKey="assertStillSubscribedToNewsletter"/> + <!-- Subscribe to newsletters customer on the second website --> + <actionGroup ref="AdminSubscribeCustomerToNewslettersAndSelectStoreView" stepKey="subscribeToNewsletterSecondWebsite"> + <argument name="websiteId" value="$grabFromCurrentUrlGetSecondWebsiteId"/> + <argument name="storeView" value="SecondStoreUnique"/> + </actionGroup> + <!-- Assert that created customer is subscribed to newsletter on second website --> + <actionGroup ref="AdminAssertCustomerIsSubscribedToNewslettersAndSelectedStoreView" stepKey="assertSubscribedToNewsletterSecondWebsite"> + <argument name="websiteId" value="$grabFromCurrentUrlGetSecondWebsiteId"/> + <argument name="storeView" value="SecondStoreUnique"/> + </actionGroup> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminExactMatchSearchInCustomerGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminExactMatchSearchInCustomerGridTest.xml index 6a7aeab78bcde..d278b6c52d330 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminExactMatchSearchInCustomerGridTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminExactMatchSearchInCustomerGridTest.xml @@ -35,7 +35,7 @@ <!--Step 1: Go to Customers > All Customers--> <amOnPage url="{{AdminCustomerPage.url}}" stepKey="openCustomersGridPage"/> <!--Step 2: On Customers grid page search customer by keyword with quotes--> - <actionGroup ref="searchAdminDataGridByKeyword" stepKey="searchCustomer"> + <actionGroup ref="SearchAdminDataGridByKeywordActionGroup" stepKey="searchCustomer"> <argument name="keyword" value="$$createSecondCustomer.firstname$$"/> </actionGroup> <!--Step 3: Check if customer is placed in a first row and clear grid filter--> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminPanelIsFrozenIfStorefrontIsOpenedViaCustomerViewTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminPanelIsFrozenIfStorefrontIsOpenedViaCustomerViewTest.xml index d2d3343a3b8d3..ee32214435428 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminPanelIsFrozenIfStorefrontIsOpenedViaCustomerViewTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminPanelIsFrozenIfStorefrontIsOpenedViaCustomerViewTest.xml @@ -30,22 +30,22 @@ <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderPage"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderPage"> <argument name="customer" value="$simpleCustomer$"/> </actionGroup> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSecondProduct"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSecondProduct"> <argument name="product" value="$createSimpleProduct$"/> </actionGroup> - <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerInfo"> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerInfo"> <argument name="customer" value="$simpleCustomer$"/> <argument name="address" value="US_Address_TX"/> </actionGroup> - <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRate"/> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRate"/> <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId"/> - <actionGroup ref="StartCreateInvoiceFromOrderPage" stepKey="startCreateInvoice"/> - <actionGroup ref="SubmitInvoice" stepKey="submitInvoice"/> + <actionGroup ref="StartCreateInvoiceFromOrderPageActionGroup" stepKey="startCreateInvoice"/> + <actionGroup ref="SubmitInvoiceActionGroup" stepKey="submitInvoice"/> <actionGroup ref="goToShipmentIntoOrder" stepKey="goToShipment"/> <actionGroup ref="submitShipmentIntoOrder" stepKey="submitShipment"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminProductBackRedirectNavigateFromCustomerViewCartProduct.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminProductBackRedirectNavigateFromCustomerViewCartProduct.xml index 9de2339f2e217..18106836ce137 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminProductBackRedirectNavigateFromCustomerViewCartProduct.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminProductBackRedirectNavigateFromCustomerViewCartProduct.xml @@ -11,6 +11,7 @@ <test name="AdminProductBackRedirectNavigateFromCustomerViewCartProduct"> <annotations> <features value="Customer"/> + <stories value="Product Back Button"/> <title value="Product back redirect navigate from customer view cart product"/> <description value="Back button on product page is redirecting to customer page if opened form shopping cart"/> <severity value="MINOR"/> @@ -36,7 +37,7 @@ </actionGroup> <!-- Add product to cart --> - <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart"> <argument name="product" value="$$createProduct$$"/> </actionGroup> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AllowedCountriesRestrictionApplyOnBackendTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AllowedCountriesRestrictionApplyOnBackendTest.xml index 6de03e225ae08..194c6e6164f8c 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AllowedCountriesRestrictionApplyOnBackendTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AllowedCountriesRestrictionApplyOnBackendTest.xml @@ -53,7 +53,7 @@ <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteTestWebsite"> <argument name="websiteName" value="{{NewWebSiteData.name}}"/> </actionGroup> - <actionGroup ref="NavigateToConfigurationGeneralPage" stepKey="navigateToConfigGeneralPage2"/> + <actionGroup ref="NavigateToConfigurationGeneralPageActionGroup" stepKey="navigateToConfigGeneralPage2"/> <actionGroup ref="AdminSwitchWebsiteActionGroup" stepKey="adminSwitchWebsiteActionGroup"> <argument name="website" value="_defaultWebsite"/> </actionGroup> @@ -65,7 +65,7 @@ </after> <!--Check that all countries are allowed initially and get amount--> <comment userInput="Check that all countries are allowed initially and get amount" stepKey="checkAllCountriesAreAllowed"/> - <actionGroup ref="NavigateToConfigurationGeneralPage" stepKey="navigateToConfigGeneralPage"/> + <actionGroup ref="NavigateToConfigurationGeneralPageActionGroup" stepKey="navigateToConfigGeneralPage"/> <createData entity="DisableAdminAccountAllowCountry" stepKey="setDefaultValueForAllowCountries"/> <executeJS function="return document.querySelectorAll('{{CountryOptionsSection.allowedCountries}} option').length" stepKey="countriesAmount"/> <!-- Create customer for US --> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml index e35a1ad61dc7c..d9b71e1e6e9ba 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml @@ -73,7 +73,7 @@ </annotations> <remove keyForRemoval="filterCustomer"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilters" before="selectCustomer"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilters" before="selectCustomer"/> <actionGroup ref="AdminSelectAllCustomers" stepKey="selectCustomer"/> </test> </tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.xml index f364d24806b9c..6d8f55daecc13 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.xml @@ -42,7 +42,7 @@ </after> <!-- Go to the created product page and add it to the cart--> - <actionGroup ref="AddSimpleProductToCart" stepKey="cartAddSimpleProductToCart"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="cartAddSimpleProductToCart"> <argument name="product" value="$$product$$"/> </actionGroup> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/SearchByEmailInCustomerGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/SearchByEmailInCustomerGridTest.xml index e16ec92e507e6..5cce64495bbd2 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/SearchByEmailInCustomerGridTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/SearchByEmailInCustomerGridTest.xml @@ -33,7 +33,7 @@ <!--Step 1: Go to Customers > All Customers--> <amOnPage url="{{AdminCustomerPage.url}}" stepKey="openCustomersGridPage"/> <!--Step 2: On Customers grid page search customer by keyword--> - <actionGroup ref="searchAdminDataGridByKeyword" stepKey="searchCustomer"> + <actionGroup ref="SearchAdminDataGridByKeywordActionGroup" stepKey="searchCustomer"> <argument name="keyword" value="$$createSecondCustomer.email$$"/> </actionGroup> <!--Step 3: Check if customer is placed in a first row and clear grid filter--> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml index 8469126547eb1..4c35cdbdb7cbb 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml @@ -30,23 +30,23 @@ </before> <!--Add new tax rates. Go to tax rule page --> - <actionGroup ref="addNewTaxRuleActionGroup" stepKey="addFirstTaxRuleActionGroup"/> + <actionGroup ref="AddNewTaxRuleActionGroup" stepKey="addFirstTaxRuleActionGroup"/> <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="{{TaxRule.name}}"/> <!-- Add NY and CA tax rules --> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addSimpleTaxUK"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addSimpleTaxUK"> <argument name="taxCode" value="SimpleTaxUK"/> </actionGroup> <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> <waitForPageLoad stepKey="waitForNewTaxRuleCreated"/> <!-- Go to tax rule page to create second Tax Rule--> - <actionGroup ref="addNewTaxRuleActionGroup" stepKey="addSecondTaxRuleActionGroup"/> + <actionGroup ref="AddNewTaxRuleActionGroup" stepKey="addSecondTaxRuleActionGroup"/> <fillField stepKey="fillSecondRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="{{TaxRuleZeroRate.name}}"/> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addSimpleTaxUKZeroRate"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addSimpleTaxUKZeroRate"> <argument name="taxCode" value="SimpleTaxUKZeroRate"/> </actionGroup> - <actionGroup ref="addCustomerTaxClass" stepKey="addCustomerTaxClass"> + <actionGroup ref="AddCustomerTaxClassActionGroup" stepKey="addCustomerTaxClass"> <argument name="customerTaxClassName" value="UK_zero"/> </actionGroup> <click stepKey="disableDefaultProdTaxClass" selector="{{AdminTaxRulesSection.defaultCustomerTaxClass}}"/> @@ -138,7 +138,7 @@ <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategoryFirst"/> - <actionGroup ref="deleteProductTaxClass" stepKey="deleteFirstProductTaxClass"> + <actionGroup ref="DeleteProductTaxClassActionGroup" stepKey="deleteFirstProductTaxClass"> <argument name="taxClassName" value="UK_zero"/> </actionGroup> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressBelgiumTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressBelgiumTest.xml index 6c0615f701df6..d36d640c5ad17 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressBelgiumTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressBelgiumTest.xml @@ -47,5 +47,13 @@ <actionGroup ref="VerifyCustomerShippingAddressWithState" stepKey="verifyShippingAddress"> <argument name="address" value="updateCustomerBelgiumAddress"/> </actionGroup> + + <!-- Verify country Belgium can be saved without state as state not required --> + <actionGroup ref="StoreFrontClickEditDefaultShippingAddressActionGroup" stepKey="clickOnDefaultShippingAddress"/> + <selectOption selector="{{StorefrontCustomerAddressFormSection.country}}" userInput="Belgium" stepKey="selectCountry"/> + <selectOption selector="{{StorefrontCustomerAddressFormSection.state}}" userInput="Please select a region, state or province." stepKey="selectState"/> + <actionGroup ref="AdminSaveCustomerAddressActionGroup" stepKey="saveAddress"/> + <see selector="{{StorefrontCustomerAddressesSection.defaultShippingAddress}}" userInput="Belgium" stepKey="seeAssertCustomerDefaultShippingAddressCountry"/> + </test> </tests> diff --git a/app/code/Magento/Customer/Test/Unit/Block/Account/NavigationTest.php b/app/code/Magento/Customer/Test/Unit/Block/Account/NavigationTest.php new file mode 100644 index 0000000000000..e8c7bd886ab01 --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Block/Account/NavigationTest.php @@ -0,0 +1,106 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Test\Unit\Block\Account; + +use PHPUnit\Framework\TestCase; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Customer\Block\Account\Navigation; +use Magento\Framework\View\Element\Template\Context; +use Magento\Framework\View\LayoutInterface; +use Magento\Wishlist\Block\Link as WishListLink; +use Magento\Customer\Block\Account\Link as CustomerAccountLink; + +class NavigationTest extends TestCase +{ + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var Navigation + */ + private $navigation; + + /** + * @var Context|\PHPUnit_Framework_MockObject_MockObject + */ + private $contextMock; + + /** + * @var LayoutInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $layoutMock; + + /** + * Setup environment for test + */ + protected function setUp() + { + $this->contextMock = $this->createMock(Context::class); + $this->layoutMock = $this->createMock(LayoutInterface::class); + $this->contextMock->expects($this->any()) + ->method('getLayout') + ->willReturn($this->layoutMock); + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->navigation = $this->objectManagerHelper->getObject( + Navigation::class, + [ + 'context' => $this->contextMock + ] + ); + } + + /** + * Test get links with block customer account link and wish list link + * + * @return void + */ + public function testGetLinksWithCustomerAndWishList() + { + $wishListLinkMock = $this->getMockBuilder(WishListLink::class) + ->disableOriginalConstructor() + ->setMethods(['getSortOrder']) + ->getMock(); + + $customerAccountLinkMock = $this->getMockBuilder(CustomerAccountLink::class) + ->disableOriginalConstructor() + ->setMethods(['getSortOrder']) + ->getMock(); + + $wishListLinkMock->expects($this->any()) + ->method('getSortOrder') + ->willReturn(100); + + $customerAccountLinkMock->expects($this->any()) + ->method('getSortOrder') + ->willReturn(20); + + $nameInLayout = 'top.links'; + + $blockChildren = [ + 'wishListLink' => $wishListLinkMock, + 'customerAccountLink' => $customerAccountLinkMock + ]; + + $this->navigation->setNameInLayout($nameInLayout); + $this->layoutMock->expects($this->any()) + ->method('getChildBlocks') + ->with($nameInLayout) + ->willReturn($blockChildren); + + /* Assertion */ + $this->assertEquals( + [ + 0 => $wishListLinkMock, + 1 => $customerAccountLinkMock + ], + $this->navigation->getLinks() + ); + } +} diff --git a/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/Edit/Tab/NewsletterTest.php b/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/Edit/Tab/NewsletterTest.php index 1c252bfc75a53..d0ea012a11e1e 100644 --- a/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/Edit/Tab/NewsletterTest.php +++ b/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/Edit/Tab/NewsletterTest.php @@ -3,192 +3,322 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Customer\Test\Unit\Block\Adminhtml\Edit\Tab; +use Magento\Backend\Block\Template\Context; use Magento\Backend\Model\Session; +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Block\Adminhtml\Edit\Tab\Newsletter; use Magento\Customer\Controller\RegistryConstants; +use Magento\Customer\Model\Config\Share; +use Magento\Framework\Data\Form; +use Magento\Framework\Data\Form\Element\Checkbox; +use Magento\Framework\Data\Form\Element\Fieldset; +use Magento\Framework\Data\Form\Element\Select; +use Magento\Framework\Data\FormFactory; +use Magento\Framework\Registry; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\UrlInterface; +use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Store\Model\System\Store as SystemStore; +use Magento\Store\Model\Website; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** + * Test Customer account form block + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class NewsletterTest extends \PHPUnit\Framework\TestCase +class NewsletterTest extends TestCase { /** - * @var \Magento\Customer\Block\Adminhtml\Edit\Tab\Newsletter + * @var Newsletter + */ + private $model; + + /** + * @var Context|MockObject + */ + private $contextMock; + + /** + * Store manager + * + * @var StoreManagerInterface|MockObject + */ + private $storeManager; + + /** + * @var Registry|MockObject */ - protected $model; + private $registryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var FormFactory|MockObject */ - protected $contextMock; + private $formFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var SubscriberFactory|MockObject */ - protected $registryMock; + private $subscriberFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var AccountManagementInterface|MockObject */ - protected $formFactoryMock; + private $accountManagementMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var UrlInterface|MockObject */ - protected $subscriberFactoryMock; + private $urlBuilderMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Session|MockObject */ - protected $accountManagementMock; + private $backendSessionMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var SystemStore|MockObject */ - protected $urlBuilderMock; + private $systemStore; /** - * @var Session|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerRepositoryInterface|MockObject */ - protected $backendSessionMock; + private $customerRepository; + /** + * @var Share|MockObject + */ + private $shareConfig; + + /** + * @inheritdoc + */ public function setUp() { - $this->contextMock = $this->createMock(\Magento\Backend\Block\Template\Context::class); - $this->registryMock = $this->createMock(\Magento\Framework\Registry::class); - $this->formFactoryMock = $this->createMock(\Magento\Framework\Data\FormFactory::class); + $this->contextMock = $this->createMock(Context::class); + $this->registryMock = $this->createMock(Registry::class); + $this->formFactoryMock = $this->createMock(FormFactory::class); $this->subscriberFactoryMock = $this->createPartialMock( - \Magento\Newsletter\Model\SubscriberFactory::class, + SubscriberFactory::class, ['create'] ); - $this->accountManagementMock = $this->createMock(\Magento\Customer\Api\AccountManagementInterface::class); - $this->urlBuilderMock = $this->createMock(\Magento\Framework\UrlInterface::class); - $this->backendSessionMock = $this->getMockBuilder(\Magento\Backend\Model\Session::class) + $this->accountManagementMock = $this->createMock(AccountManagementInterface::class); + $this->urlBuilderMock = $this->createMock(UrlInterface::class); + $this->storeManager = $this->createMock(StoreManagerInterface::class); + $this->backendSessionMock = $this->getMockBuilder(Session::class) ->setMethods(['getCustomerFormData']) ->disableOriginalConstructor() ->getMock(); - $this->contextMock->expects($this->once())->method('getUrlBuilder')->willReturn($this->urlBuilderMock); - $this->contextMock->expects($this->once())->method('getBackendSession')->willReturn($this->backendSessionMock); - - $this->model = new \Magento\Customer\Block\Adminhtml\Edit\Tab\Newsletter( - $this->contextMock, - $this->registryMock, - $this->formFactoryMock, - $this->subscriberFactoryMock, - $this->accountManagementMock + $this->contextMock->expects($this->once()) + ->method('getUrlBuilder') + ->willReturn($this->urlBuilderMock); + $this->contextMock->expects($this->once()) + ->method('getBackendSession') + ->willReturn($this->backendSessionMock); + $this->contextMock->method('getStoreManager') + ->willReturn($this->storeManager); + $this->systemStore = $this->createMock(SystemStore::class); + $this->customerRepository = $this->createMock(CustomerRepositoryInterface::class); + $this->shareConfig = $this->createMock(Share::class); + + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + Newsletter::class, + [ + 'context' => $this->contextMock, + 'registry' => $this->registryMock, + 'formFactory' => $this->formFactoryMock, + 'subscriberFactory' => $this->subscriberFactoryMock, + 'customerAccountManagement' => $this->accountManagementMock, + 'systemStore' => $this->systemStore, + 'customerRepository' => $this->customerRepository, + 'shareConfig' => $this->shareConfig, + ] ); } + /** + * Test to initialize the form without current customer + */ public function testInitFormCanNotShowTab() { - $this->registryMock->expects($this->once())->method('registry')->with(RegistryConstants::CURRENT_CUSTOMER_ID) + $this->registryMock->expects($this->once()) + ->method('registry') + ->with(RegistryConstants::CURRENT_CUSTOMER_ID) ->willReturn(false); + $this->assertSame($this->model, $this->model->initForm()); } + /** + * Test to initialize the form + */ public function testInitForm() { $customerId = 1; + $websiteId = 1; + $storeId = 2; + $websiteName = 'Website Name'; + $isSubscribed = true; - $subscriberMock = $this->createMock(\Magento\Newsletter\Model\Subscriber::class); - $fieldsetMock = $this->createMock(\Magento\Framework\Data\Form\Element\Fieldset::class); - $elementMock = $this->createPartialMock(\Magento\Framework\Data\Form\Element\Checkbox::class, ['setIsChecked']); - $formMock = $this->createPartialMock( - \Magento\Framework\Data\Form::class, - ['setHtmlIdPrefix', 'addFieldset', 'setValues', 'getElement', 'setForm', 'setParent', 'setBaseUrl'] - ); - $this->registryMock->expects($this->exactly(3)) - ->method('registry') - ->willReturnMap( - [ - [RegistryConstants::CURRENT_CUSTOMER_ID, $customerId], - ['subscriber', $subscriberMock], - ] - ); - $this->formFactoryMock->expects($this->once())->method('create')->willReturn($formMock); - $formMock->expects($this->once())->method('setHtmlIdPrefix')->with('_newsletter'); + $this->registryMock->method('registry')->with(RegistryConstants::CURRENT_CUSTOMER_ID) + ->willReturn($customerId); + + $customer = $this->createMock(CustomerInterface::class); + $customer->method('getWebsiteId')->willReturn($websiteId); + $customer->method('getStoreId')->willReturn($storeId); + $customer->method('getId')->willReturn($customerId); + $this->customerRepository->method('getById')->with($customerId)->willReturn($customer); + $subscriberMock = $this->createMock(Subscriber::class); + $subscriberMock->method('loadByCustomer')->with($customerId, $websiteId)->willReturnSelf(); + $subscriberMock->method('isSubscribed')->willReturn($isSubscribed); + $subscriberMock->method('getData')->willReturn([]); $this->subscriberFactoryMock->expects($this->once())->method('create')->willReturn($subscriberMock); - $subscriberMock->expects($this->once())->method('loadByCustomerId')->with($customerId)->willReturnSelf(); - $this->registryMock->expects($this->once())->method('register')->with('subscriber', $subscriberMock); - $formMock->expects($this->once())->method('addFieldset')->willReturn($fieldsetMock); - $fieldsetMock->expects($this->once())->method('addField')->willReturn($elementMock); - $this->accountManagementMock->expects($this->once())->method('isReadOnly')->with($customerId) - ->willReturn(false); - $subscriberMock->expects($this->once())->method('isSubscribed')->willReturn(true); - $this->urlBuilderMock->expects($this->once())->method('getBaseUrl')->willReturn('domain.com'); - $this->backendSessionMock->expects($this->once())->method('getCustomerFormData')->willReturn(null); + $website = $this->createMock(Website::class); + $website->method('getStoresCount')->willReturn(1); + $website->method('getId')->willReturn($websiteId); + $store = $this->createMock(Store::class); + $store->method('getWebsiteId')->willReturn($websiteId); + $this->storeManager->method('getStore')->with($storeId)->willReturn($store); + $this->storeManager->method('getWebsites')->willReturn([$website]); + $this->storeManager->method('isSingleStoreMode')->willReturn(true); + $this->systemStore->method('getStoreOptionsTree')->willReturn([]); + $this->systemStore->method('getWebsiteName')->with($websiteId)->willReturn($websiteName); - $formMock->expects($this->once()) - ->method('getElement') - ->willReturnMap( + $statusElementMock = $this->createMock(Checkbox::class); + $statusElementMock->expects($this->once()) + ->method('setIsChecked') + ->with($isSubscribed); + $fieldsetMock = $this->createMock(Fieldset::class); + $fieldsetMock->expects($this->once()) + ->method('addField') + ->with( + 'subscription_status_' . $websiteId, + 'checkbox', [ - ['subscription', $elementMock], + 'label' => __('Subscribed to Newsletter'), + 'name' => "subscription_status[$websiteId]", + 'data-form-part' => null, + 'value' => $isSubscribed, + 'onchange' => 'this.value = this.checked;' ] - ); - - $elementMock->expects($this->once()) - ->method('setIsChecked') - ->with(true); + ) + ->willReturn($statusElementMock); + $fieldsetMock->expects($this->once())->method('setReadonly')->with(true, true); + $formMock = $this->createPartialMock( + Form::class, + ['setHtmlIdPrefix', 'addFieldset', 'setValues', 'getElement', 'setForm', 'setParent', 'setBaseUrl'] + ); + $formMock->expects($this->once())->method('setHtmlIdPrefix')->with('_newsletter'); + $formMock->expects($this->once())->method('addFieldset')->willReturn($fieldsetMock); + $this->formFactoryMock->expects($this->once())->method('create')->willReturn($formMock); + $this->accountManagementMock->expects($this->once()) + ->method('isReadOnly') + ->with($customerId) + ->willReturn(true); + $this->backendSessionMock->expects($this->once()) + ->method('getCustomerFormData') + ->willReturn(null); $this->assertSame($this->model, $this->model->initForm()); } + /** + * Test to initialize the form with session form data + */ public function testInitFormWithCustomerFormData() { $customerId = 1; + $websiteId = 1; + $storeId = 2; + $websiteName = 'Website Name'; + $isSubscribed = true; + $isSubscribedCustomerSession = false; - $subscriberMock = $this->createMock(\Magento\Newsletter\Model\Subscriber::class); - $fieldsetMock = $this->createMock(\Magento\Framework\Data\Form\Element\Fieldset::class); - $elementMock = $this->createPartialMock(\Magento\Framework\Data\Form\Element\Checkbox::class, ['setIsChecked']); + $this->registryMock->method('registry')->with(RegistryConstants::CURRENT_CUSTOMER_ID) + ->willReturn($customerId); + $customer = $this->createMock(CustomerInterface::class); + $customer->method('getWebsiteId')->willReturn($websiteId); + $customer->method('getStoreId')->willReturn($storeId); + $customer->method('getId')->willReturn($customerId); + $this->customerRepository->method('getById')->with($customerId)->willReturn($customer); + $subscriberMock = $this->createMock(Subscriber::class); + $subscriberMock->method('loadByCustomer')->with($customerId, $websiteId)->willReturnSelf(); + $subscriberMock->method('isSubscribed')->willReturn($isSubscribed); + $subscriberMock->method('getData')->willReturn([]); + $this->subscriberFactoryMock->expects($this->once())->method('create')->willReturn($subscriberMock); + $website = $this->createMock(Website::class); + $website->method('getStoresCount')->willReturn(1); + $website->method('getId')->willReturn($websiteId); + $store = $this->createMock(Store::class); + $store->method('getWebsiteId')->willReturn($websiteId); + $this->storeManager->method('getStore')->with($storeId)->willReturn($store); + $this->storeManager->method('getWebsites')->willReturn([$website]); + $this->storeManager->method('isSingleStoreMode')->willReturn(true); + $this->systemStore->method('getStoreOptionsTree')->willReturn([]); + $this->systemStore->method('getWebsiteName')->with($websiteId)->willReturn($websiteName); + $statusElementMock = $this->createMock(Checkbox::class); + $statusElementMock->expects($this->once()) + ->method('setIsChecked') + ->with($isSubscribed); + $fieldsetMock = $this->createMock(Fieldset::class); + $fieldsetMock->expects($this->once()) + ->method('addField') + ->with( + 'subscription_status_' . $websiteId, + 'checkbox', + [ + 'label' => __('Subscribed to Newsletter'), + 'name' => "subscription_status[$websiteId]", + 'data-form-part' => null, + 'value' => $isSubscribed, + 'onchange' => 'this.value = this.checked;' + ] + ) + ->willReturn($statusElementMock); + $fieldsetMock->expects($this->once())->method('setReadonly')->with(true, true); + $statusElementForm = $this->createPartialMock(Checkbox::class, ['setChecked', 'setValue']); + $statusElementForm->method('setValue') + ->with($isSubscribedCustomerSession); + $statusElementForm->method('setChecked') + ->with($isSubscribedCustomerSession); + $storeElementForm = $this->createPartialMock(Select::class, ['setValue']); + $storeElementForm->method('setValue') + ->with(Store::DEFAULT_STORE_ID); $formMock = $this->createPartialMock( - \Magento\Framework\Data\Form::class, + Form::class, ['setHtmlIdPrefix', 'addFieldset', 'setValues', 'getElement', 'setForm', 'setParent', 'setBaseUrl'] ); - $this->registryMock->expects($this->exactly(3)) - ->method('registry') + $formMock->expects($this->once())->method('setHtmlIdPrefix')->with('_newsletter'); + $formMock->expects($this->once())->method('addFieldset')->willReturn($fieldsetMock); + $formMock->method('getElement') ->willReturnMap( [ - [RegistryConstants::CURRENT_CUSTOMER_ID, $customerId], - ['subscriber', $subscriberMock], + ['subscription_status_' . $websiteId, $statusElementForm], + ['subscription_store_' . $websiteId, $storeElementForm], ] ); $this->formFactoryMock->expects($this->once())->method('create')->willReturn($formMock); - $formMock->expects($this->once())->method('setHtmlIdPrefix')->with('_newsletter'); - $this->subscriberFactoryMock->expects($this->once())->method('create')->willReturn($subscriberMock); - $subscriberMock->expects($this->once())->method('loadByCustomerId')->with($customerId)->willReturnSelf(); - $formMock->expects($this->once())->method('addFieldset')->willReturn($fieldsetMock); - $fieldsetMock->expects($this->once())->method('addField')->willReturn($elementMock); - $this->accountManagementMock->expects($this->once())->method('isReadOnly')->with($customerId) - ->willReturn(false); - $subscriberMock->expects($this->once())->method('isSubscribed')->willReturn(false); - $this->urlBuilderMock->expects($this->once())->method('getBaseUrl')->willReturn('domain.com'); - + $this->accountManagementMock->expects($this->once()) + ->method('isReadOnly') + ->with($customerId) + ->willReturn(true); $this->backendSessionMock->expects($this->once()) ->method('getCustomerFormData') - ->willReturn([ - 'customer' => [ - 'entity_id' => $customerId, - ], - 'subscription' => true, - ]); - - $formMock->expects($this->exactly(2)) - ->method('getElement') - ->willReturnMap( - [ - ['subscription', $elementMock], - ] - ); - - $elementMock->expects($this->exactly(2)) - ->method('setIsChecked') - ->willReturnMap( + ->willReturn( [ - [false], - [true], + 'customer' => ['entity_id' => $customerId], + 'subscription_status' => [$websiteId => $isSubscribedCustomerSession] ] ); diff --git a/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/From/Element/Newsletter/SubscriptionsTest.php b/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/From/Element/Newsletter/SubscriptionsTest.php new file mode 100644 index 0000000000000..f6d6777654c5b --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/From/Element/Newsletter/SubscriptionsTest.php @@ -0,0 +1,185 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Test\Unit\Block\Adminhtml\From\Element\Newsletter; + +use Magento\Customer\Block\Adminhtml\Form\Element\Newsletter\Subscriptions; +use Magento\Framework\App\Request\DataPersistorInterface; +use Magento\Framework\Data\Form\Element\AbstractElement; +use Magento\Framework\Data\Form\Element\CollectionFactory; +use Magento\Framework\Data\Form\Element\Factory; +use Magento\Framework\Escaper; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test class for Customer Newsletter Subscriptions Element + */ +class SubscriptionsTest extends TestCase +{ + /** + * @var Factory|MockObject + */ + private $factoryElement; + + /** + * @var CollectionFactory|MockObject + */ + private $factoryCollection; + + /** + * @var Escaper|MockObject + */ + private $escaper; + + /** + * @var DataPersistorInterface|MockObject + */ + private $dataPersistor; + + /** + * @var Subscriptions + */ + private $element; + + /** + * @inheritdoc + */ + public function setUp() + { + $this->factoryElement = $this->createMock(Factory::class); + $this->factoryCollection = $this->createMock(CollectionFactory::class); + $this->escaper = $this->createMock(Escaper::class); + $this->dataPersistor = $this->createMock(DataPersistorInterface::class); + + $objectManager = new ObjectManager($this); + $this->element = $objectManager->getObject( + Subscriptions::class, + [ + 'factoryElement' => $this->factoryElement, + 'factoryCollection' => $this->factoryCollection, + 'escaper' => $this->escaper, + 'dataPersistor' => $this->dataPersistor, + 'data' => [] + ] + ); + } + + /** + * Test to Get the Html for the element + * + * @param array $data + * @param array $elementsHtml + * @param string $expectedHtml + * @return void + * @dataProvider getElementHtmlDataProvider + */ + public function testGetElementHtml(array $data, array $elementsHtml, string $expectedHtml): void + { + $this->escaper->method('escapeHtml')->withAnyParameters()->willReturnArgument(0); + $selectElementId = $data['name'] . '_store_' . $data['subscriptions'][0]['website_id']; + $selectElement = $this->createMock(AbstractElement::class); + $selectElement->expects($this->once())->method('setId')->with($selectElementId); + $selectElement->expects($this->once())->method('setForm')->willReturnSelf(); + $selectElement->method('toHtml')->willReturn($elementsHtml['store']); + $statusElementId = $data['name'] . '_status_' . $data['subscriptions'][0]['website_id']; + $statusElement = $this->createMock(AbstractElement::class); + $statusElement->expects($this->once())->method('setId')->with($statusElementId); + $statusElement->expects($this->once())->method('setForm')->willReturnSelf(); + $statusElement->method('toHtml')->willReturn($elementsHtml['status']); + $this->factoryElement->method('create')->willReturnMap( + [ + [ + 'select', + [ + 'data' => [ + 'name' => "{$data['name']}_store[{$data['subscriptions'][0]['website_id']}]", + 'data-form-part' => $data['target_form'], + 'values' => $data['subscriptions'][0]['store_options'], + 'value' => $data['subscriptions'][0]['store_id'], + 'required' => true, + ] + ], + $selectElement + ], + [ + 'checkbox', + [ + 'data' => [ + 'name' => "{$data['name']}_status[{$data['subscriptions'][0]['website_id']}]", + 'data-form-part' => $data['target_form'], + 'value' => $data['subscriptions'][0]['status'], + 'onchange' => 'this.value = this.checked;', + ] + ], + $statusElement + ] + ] + ); + $this->dataPersistor->method('get')->willReturn([]); + $this->element->setData($data); + + $this->assertEquals($expectedHtml, $this->element->getElementHtml()); + } + + /** + * Data provider for test to get the html + * + * @return array + */ + public function getElementHtmlDataProvider(): array + { + $customerId = 33; + $elementName = 'element_name'; + $targetForm = 'target_form'; + $websiteId = 1; + $websiteName = 'Website 1'; + $storeId = 2; + $status = true; + $storeOptions = ['array_of_store_options']; + $lastUpdated = 'last updated'; + $storeElementHtml = 'storeElementHtml'; + $statusElementHtml = 'statusElementHtml'; + $outputHtmlTemplate = "<table class=\"admin__table-secondary\">" + . "<tr><th>%s</th><th class=\"subscriber-status\">%s</th><th>%s</th><th>%s</th></tr>" + . "<tr><td>%s</td><td class=\"subscriber-status\">%s</td><td>%s</td><td>%s</td></tr></table>"; + + return [ + [ + 'data' => [ + 'customer_id' => $customerId, + 'name' => $elementName, + 'target_form' => $targetForm, + 'subscriptions' => [ + [ + 'store_id' => $storeId, + 'website_id' => $websiteId, + 'website_name' => $websiteName, + 'status' => $status, + 'store_options' => $storeOptions, + 'last_updated' => $lastUpdated, + ], + ], + ], + 'elementsHtml' => [ + 'status' => $statusElementHtml, + 'store' => $storeElementHtml, + ], + 'expectedHtml' => sprintf( + $outputHtmlTemplate, + 'Website', + 'Subscribed', + 'Store View', + 'Last Updated At', + $websiteName, + $statusElementHtml, + $storeElementHtml, + $lastUpdated + ) + ], + ]; + } +} diff --git a/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php b/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php index b1d7c455324b3..1fd7fc340e542 100644 --- a/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php +++ b/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php @@ -354,7 +354,15 @@ public function testGetDateFormat(string $locale, string $expectedFormat) public function getDateFormatDataProvider(): array { return [ - ['ar_SA', 'd/M/y'], + [ + 'ar_SA', + preg_replace( + '/[^MmDdYy\/\.\-]/', + '', + (new \IntlDateFormatter('ar_SA', \IntlDateFormatter::SHORT, \IntlDateFormatter::NONE)) + ->getPattern() + ) + ], [Resolver::DEFAULT_LOCALE, self::DATE_FORMAT], ]; } diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php index 28f897adf9176..5565a807b8135 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Account/ConfirmTest.php @@ -282,7 +282,7 @@ public function testSuccessMessage($customerId, $key, $vatValidationEnabled, $ad ->willReturnSelf(); $this->messageManagerMock->expects($this->any()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with($this->stringContains($successMessage)) ->willReturnSelf(); @@ -402,7 +402,7 @@ public function testSuccessRedirect( ->willReturnSelf(); $this->messageManagerMock->expects($this->any()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with($this->stringContains($successMessage)) ->willReturnSelf(); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php index 13cf195ab5f69..05a8b6448af99 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Account/LoginPostTest.php @@ -223,7 +223,7 @@ public function testExecuteEmptyLoginData() ->willReturn([]); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with(__('A login and a password are required.')) ->willReturnSelf(); @@ -557,7 +557,7 @@ protected function mockExceptions($exception, $username) $url ); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with($message) ->willReturnSelf(); @@ -569,7 +569,7 @@ protected function mockExceptions($exception, $username) case \Magento\Framework\Exception\AuthenticationException::class: $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with( __( 'The account sign-in was incorrect or your account is disabled temporarily. ' @@ -586,7 +586,7 @@ protected function mockExceptions($exception, $username) case '\Exception': $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with(__('An unspecified error occurred. Please contact us for assistance.')) ->willReturnSelf(); break; @@ -597,7 +597,7 @@ protected function mockExceptions($exception, $username) . 'Please wait and try again later.' ); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with($message) ->willReturnSelf(); $this->session->expects($this->once()) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php index 4064b8586257d..3af3cc60010bb 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Address/DeleteTest.php @@ -146,7 +146,7 @@ public function testExecute() ->method('deleteById') ->with($addressId); $this->messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('You deleted the address.')); $this->resultRedirect->expects($this->once()) ->method('setPath') @@ -183,7 +183,7 @@ public function testExecuteWithException() ->willReturn(34); $exception = new \Exception('Exception'); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with(__('We can\'t delete the address right now.')) ->willThrowException($exception); $this->messageManager->expects($this->once()) diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php index 5f7064d5b124b..c9f885315b0ef 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Group/SaveTest.php @@ -167,7 +167,7 @@ public function testExecuteWithTaxClassAndException() ->method('save') ->with($this->group); $this->messageManager->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('You saved the customer group.')); $exception = new \Exception('Exception'); $this->resultRedirect->expects($this->at(0)) @@ -175,7 +175,7 @@ public function testExecuteWithTaxClassAndException() ->with('customer/group') ->willThrowException($exception); $this->messageManager->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('Exception'); $this->dataObjectProcessorMock->expects($this->once()) ->method('buildOutputDataArray') diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php index cb5ff88ab704a..4157359959ae4 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php @@ -199,7 +199,7 @@ public function testExecuteWithException() ->willThrowException(new \Exception('Some message.')); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('Some message.'); $this->massAction->execute(); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassDeleteTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassDeleteTest.php index 1f39e6306b996..b436b5b137c78 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassDeleteTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassDeleteTest.php @@ -179,7 +179,7 @@ public function testExecuteWithException() ->willThrowException(new \Exception('Some message.')); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('Some message.'); $this->massAction->execute(); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php index 90bff0b61bcbf..d8b88bba2cdbe 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassSubscribeTest.php @@ -6,119 +6,131 @@ namespace Magento\Customer\Test\Unit\Controller\Adminhtml\Index; +use Magento\Backend\Model\View\Result\Redirect; +use Magento\Backend\Model\View\Result\RedirectFactory; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Controller\Adminhtml\Index\MassSubscribe; +use Magento\Customer\Model\ResourceModel\Customer\Collection; +use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory; use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Request\Http; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\Message\Manager; +use Magento\Framework\ObjectManager\ObjectManager; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Newsletter\Model\SubscriptionManagerInterface; +use Magento\Ui\Component\MassAction\Filter; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** - * Class MassSubscribeTest + * Class to test mass subscribe customers by ids + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class MassSubscribeTest extends \PHPUnit\Framework\TestCase +class MassSubscribeTest extends TestCase { /** - * @var \Magento\Customer\Controller\Adminhtml\Index\MassSubscribe + * @var MassSubscribe */ protected $massAction; /** - * @var Context|\PHPUnit_Framework_MockObject_MockObject + * @var Context|MockObject */ protected $contextMock; /** - * @var \Magento\Backend\Model\View\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject + * @var Redirect|MockObject */ protected $resultRedirectMock; /** - * @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject + * @var Http|MockObject */ protected $requestMock; /** - * @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ResponseInterface|MockObject */ protected $responseMock; /** - * @var \Magento\Framework\Message\Manager|\PHPUnit_Framework_MockObject_MockObject + * @var Manager|MockObject */ protected $messageManagerMock; /** - * @var \Magento\Framework\ObjectManager\ObjectManager|\PHPUnit_Framework_MockObject_MockObject + * @var ObjectManager|MockObject */ protected $objectManagerMock; /** - * @var \Magento\Customer\Model\ResourceModel\Customer\Collection|\PHPUnit_Framework_MockObject_MockObject + * @var Collection|MockObject */ protected $customerCollectionMock; /** - * @var \Magento\Customer\Model\ResourceModel\Customer\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CollectionFactory|MockObject */ protected $customerCollectionFactoryMock; /** - * @var \Magento\Ui\Component\MassAction\Filter|\PHPUnit_Framework_MockObject_MockObject + * @var Filter|MockObject */ protected $filterMock; /** - * @var \Magento\Customer\Api\CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerRepositoryInterface|MockObject */ protected $customerRepositoryMock; /** - * @var \Magento\Newsletter\Model\Subscriber|\PHPUnit_Framework_MockObject_MockObject + * @var SubscriptionManagerInterface|MockObject */ - protected $subscriberMock; + private $subscriptionManager; + /** + * @inheritdoc + */ protected function setUp() { $objectManagerHelper = new ObjectManagerHelper($this); $this->contextMock = $this->createMock(\Magento\Backend\App\Action\Context::class); - $resultRedirectFactory = $this->createMock(\Magento\Backend\Model\View\Result\RedirectFactory::class); - $this->responseMock = $this->createMock(\Magento\Framework\App\ResponseInterface::class); - $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) + $resultRedirectFactory = $this->createMock(RedirectFactory::class); + $this->responseMock = $this->createMock(ResponseInterface::class); + $this->requestMock = $this->getMockBuilder(Http::class) ->disableOriginalConstructor()->getMock(); $this->objectManagerMock = $this->createPartialMock( - \Magento\Framework\ObjectManager\ObjectManager::class, + ObjectManager::class, ['create'] ); - $this->messageManagerMock = $this->createMock(\Magento\Framework\Message\Manager::class); + $this->messageManagerMock = $this->createMock(Manager::class); $this->customerCollectionMock = - $this->getMockBuilder(\Magento\Customer\Model\ResourceModel\Customer\Collection::class) + $this->getMockBuilder(Collection::class) ->disableOriginalConstructor() ->getMock(); $this->customerCollectionFactoryMock = - $this->getMockBuilder(\Magento\Customer\Model\ResourceModel\Customer\CollectionFactory::class) + $this->getMockBuilder(CollectionFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $redirectMock = $this->getMockBuilder(\Magento\Backend\Model\View\Result\Redirect::class) + $redirectMock = $this->getMockBuilder(Redirect::class) ->disableOriginalConstructor() ->getMock(); - $resultFactoryMock = $this->getMockBuilder(\Magento\Framework\Controller\ResultFactory::class) + $resultFactoryMock = $this->getMockBuilder(ResultFactory::class) ->disableOriginalConstructor() ->getMock(); $resultFactoryMock->expects($this->any()) ->method('create') - ->with(\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT) + ->with(ResultFactory::TYPE_REDIRECT) ->willReturn($redirectMock); - $this->subscriberMock = $this->createMock(\Magento\Newsletter\Model\Subscriber::class); - $subscriberFactoryMock = $this->getMockBuilder(\Magento\Newsletter\Model\SubscriberFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - $subscriberFactoryMock->expects($this->any()) - ->method('create') - ->willReturn($this->subscriberMock); - - $this->resultRedirectMock = $this->createMock(\Magento\Backend\Model\View\Result\Redirect::class); + $this->subscriptionManager = $this->createMock(SubscriptionManagerInterface::class); + $this->resultRedirectMock = $this->createMock(Redirect::class); $resultRedirectFactory->expects($this->any())->method('create')->willReturn($this->resultRedirectMock); $this->contextMock->expects($this->once())->method('getMessageManager')->willReturn($this->messageManagerMock); @@ -132,7 +144,7 @@ protected function setUp() ->method('getResultFactory') ->willReturn($resultFactoryMock); - $this->filterMock = $this->createMock(\Magento\Ui\Component\MassAction\Filter::class); + $this->filterMock = $this->createMock(Filter::class); $this->filterMock->expects($this->once()) ->method('getCollection') ->with($this->customerCollectionMock) @@ -140,35 +152,37 @@ protected function setUp() $this->customerCollectionFactoryMock->expects($this->once()) ->method('create') ->willReturn($this->customerCollectionMock); - $this->customerRepositoryMock = $this->getMockBuilder(\Magento\Customer\Api\CustomerRepositoryInterface::class) + $this->customerRepositoryMock = $this->getMockBuilder(CustomerRepositoryInterface::class) ->getMockForAbstractClass(); $this->massAction = $objectManagerHelper->getObject( - \Magento\Customer\Controller\Adminhtml\Index\MassSubscribe::class, + MassSubscribe::class, [ 'context' => $this->contextMock, 'filter' => $this->filterMock, 'collectionFactory' => $this->customerCollectionFactoryMock, 'customerRepository' => $this->customerRepositoryMock, - 'subscriberFactory' => $subscriberFactoryMock, + 'subscriptionManager' => $this->subscriptionManager, ] ); } + /** + * Test to mass subscribe customers to newsletters + */ public function testExecute() { - $customersIds = [10, 11, 12]; - - $this->customerCollectionMock->expects($this->any()) - ->method('getAllIds') - ->willReturn($customersIds); - - $this->customerRepositoryMock->expects($this->any()) - ->method('getById') - ->willReturnMap([[10, true], [11, true], [12, true]]); - - $this->subscriberMock->expects($this->any()) - ->method('subscribeCustomerById') - ->willReturnMap([[10, true], [11, true], [12, true]]); + $storeId = 2; + $customerId = 10; + $customersIds = [$customerId, $customerId, $customerId]; + + $this->customerCollectionMock->method('getAllIds')->willReturn($customersIds); + $customer = $this->createMock(CustomerInterface::class); + $customer->method('getStoreId')->willReturn($storeId); + $customer->method('getId')->willReturn($customerId); + $this->customerRepositoryMock->method('getById')->with($customerId)->willReturn($customer); + $this->subscriptionManager->expects($this->exactly(3)) + ->method('subscribeCustomer') + ->with($customerId, $storeId); $this->messageManagerMock->expects($this->once()) ->method('addSuccessMessage') @@ -182,6 +196,9 @@ public function testExecute() $this->massAction->execute(); } + /** + * Test to mass subscribe customers to newsletters with throws exception + */ public function testExecuteWithException() { $customersIds = [10, 11, 12]; @@ -195,7 +212,7 @@ public function testExecuteWithException() ->willThrowException(new \Exception('Some message.')); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('Some message.'); $this->massAction->execute(); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php index 1bffa836f5034..8220d50f418be 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassUnsubscribeTest.php @@ -6,119 +6,131 @@ namespace Magento\Customer\Test\Unit\Controller\Adminhtml\Index; +use Magento\Backend\Model\View\Result\Redirect; +use Magento\Backend\Model\View\Result\RedirectFactory; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Controller\Adminhtml\Index\MassUnsubscribe; +use Magento\Customer\Model\ResourceModel\Customer\Collection; +use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory; use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Request\Http; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\Message\Manager; +use Magento\Framework\ObjectManager\ObjectManager; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Newsletter\Model\SubscriptionManagerInterface; +use Magento\Ui\Component\MassAction\Filter; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** - * Class MassUnsubscribeTest + * Class to test mass unsubscribe customers by ids + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class MassUnsubscribeTest extends \PHPUnit\Framework\TestCase +class MassUnsubscribeTest extends TestCase { /** - * @var \Magento\Customer\Controller\Adminhtml\Index\MassUnsubscribe + * @var MassUnsubscribe */ protected $massAction; /** - * @var Context|\PHPUnit_Framework_MockObject_MockObject + * @var Context|MockObject */ protected $contextMock; /** - * @var \Magento\Backend\Model\View\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject + * @var Redirect|MockObject */ protected $resultRedirectMock; /** - * @var \Magento\Framework\App\Request\Http|\PHPUnit_Framework_MockObject_MockObject + * @var Http|MockObject */ protected $requestMock; /** - * @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ResponseInterface|MockObject */ protected $responseMock; /** - * @var \Magento\Framework\Message\Manager|\PHPUnit_Framework_MockObject_MockObject + * @var Manager|MockObject */ protected $messageManagerMock; /** - * @var \Magento\Framework\ObjectManager\ObjectManager|\PHPUnit_Framework_MockObject_MockObject + * @var ObjectManager|MockObject */ protected $objectManagerMock; /** - * @var \Magento\Customer\Model\ResourceModel\Customer\Collection|\PHPUnit_Framework_MockObject_MockObject + * @var Collection|MockObject */ protected $customerCollectionMock; /** - * @var \Magento\Customer\Model\ResourceModel\Customer\CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CollectionFactory|MockObject */ protected $customerCollectionFactoryMock; /** - * @var \Magento\Ui\Component\MassAction\Filter|\PHPUnit_Framework_MockObject_MockObject + * @var Filter|MockObject */ protected $filterMock; /** - * @var \Magento\Customer\Api\CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerRepositoryInterface|MockObject */ protected $customerRepositoryMock; /** - * @var \Magento\Newsletter\Model\Subscriber|\PHPUnit_Framework_MockObject_MockObject + * @var SubscriptionManagerInterface|MockObject */ - protected $subscriberMock; + private $subscriptionManager; + /** + * @inheritdoc + */ protected function setUp() { $objectManagerHelper = new ObjectManagerHelper($this); $this->contextMock = $this->createMock(\Magento\Backend\App\Action\Context::class); - $resultRedirectFactory = $this->createMock(\Magento\Backend\Model\View\Result\RedirectFactory::class); - $this->responseMock = $this->createMock(\Magento\Framework\App\ResponseInterface::class); - $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) + $resultRedirectFactory = $this->createMock(RedirectFactory::class); + $this->responseMock = $this->createMock(ResponseInterface::class); + $this->requestMock = $this->getMockBuilder(Http::class) ->disableOriginalConstructor()->getMock(); $this->objectManagerMock = $this->createPartialMock( - \Magento\Framework\ObjectManager\ObjectManager::class, + ObjectManager::class, ['create'] ); - $this->messageManagerMock = $this->createMock(\Magento\Framework\Message\Manager::class); + $this->messageManagerMock = $this->createMock(Manager::class); $this->customerCollectionMock = - $this->getMockBuilder(\Magento\Customer\Model\ResourceModel\Customer\Collection::class) + $this->getMockBuilder(Collection::class) ->disableOriginalConstructor() ->getMock(); $this->customerCollectionFactoryMock = - $this->getMockBuilder(\Magento\Customer\Model\ResourceModel\Customer\CollectionFactory::class) + $this->getMockBuilder(CollectionFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $redirectMock = $this->getMockBuilder(\Magento\Backend\Model\View\Result\Redirect::class) + $redirectMock = $this->getMockBuilder(Redirect::class) ->disableOriginalConstructor() ->getMock(); - $resultFactoryMock = $this->getMockBuilder(\Magento\Framework\Controller\ResultFactory::class) + $resultFactoryMock = $this->getMockBuilder(ResultFactory::class) ->disableOriginalConstructor() ->getMock(); $resultFactoryMock->expects($this->any()) ->method('create') - ->with(\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT) + ->with(ResultFactory::TYPE_REDIRECT) ->willReturn($redirectMock); - $this->subscriberMock = $this->createMock(\Magento\Newsletter\Model\Subscriber::class); - $subscriberFactoryMock = $this->getMockBuilder(\Magento\Newsletter\Model\SubscriberFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - $subscriberFactoryMock->expects($this->any()) - ->method('create') - ->willReturn($this->subscriberMock); - - $this->resultRedirectMock = $this->createMock(\Magento\Backend\Model\View\Result\Redirect::class); + $this->subscriptionManager = $this->createMock(SubscriptionManagerInterface::class); + $this->resultRedirectMock = $this->createMock(Redirect::class); $resultRedirectFactory->expects($this->any())->method('create')->willReturn($this->resultRedirectMock); $this->contextMock->expects($this->once())->method('getMessageManager')->willReturn($this->messageManagerMock); @@ -132,7 +144,7 @@ protected function setUp() ->method('getResultFactory') ->willReturn($resultFactoryMock); - $this->filterMock = $this->createMock(\Magento\Ui\Component\MassAction\Filter::class); + $this->filterMock = $this->createMock(Filter::class); $this->filterMock->expects($this->once()) ->method('getCollection') ->with($this->customerCollectionMock) @@ -140,35 +152,37 @@ protected function setUp() $this->customerCollectionFactoryMock->expects($this->once()) ->method('create') ->willReturn($this->customerCollectionMock); - $this->customerRepositoryMock = $this->getMockBuilder(\Magento\Customer\Api\CustomerRepositoryInterface::class) + $this->customerRepositoryMock = $this->getMockBuilder(CustomerRepositoryInterface::class) ->getMockForAbstractClass(); $this->massAction = $objectManagerHelper->getObject( - \Magento\Customer\Controller\Adminhtml\Index\MassUnsubscribe::class, + MassUnsubscribe::class, [ 'context' => $this->contextMock, 'filter' => $this->filterMock, 'collectionFactory' => $this->customerCollectionFactoryMock, 'customerRepository' => $this->customerRepositoryMock, - 'subscriberFactory' => $subscriberFactoryMock, + 'subscriptionManager' => $this->subscriptionManager, ] ); } + /** + * Test to mass unsubscribe customers from newsletters + */ public function testExecute() { - $customersIds = [10, 11, 12]; - - $this->customerCollectionMock->expects($this->any()) - ->method('getAllIds') - ->willReturn($customersIds); - - $this->customerRepositoryMock->expects($this->any()) - ->method('getById') - ->willReturnMap([[10, true], [11, true], [12, true]]); - - $this->subscriberMock->expects($this->any()) - ->method('unsubscribeCustomerById') - ->willReturnMap([[10, true], [11, true], [12, true]]); + $storeId = 2; + $customerId = 10; + $customersIds = [$customerId, $customerId, $customerId]; + + $this->customerCollectionMock->method('getAllIds')->willReturn($customersIds); + $customer = $this->createMock(CustomerInterface::class); + $customer->method('getStoreId')->willReturn($storeId); + $customer->method('getId')->willReturn($customerId); + $this->customerRepositoryMock->method('getById')->with($customerId)->willReturn($customer); + $this->subscriptionManager->expects($this->exactly(3)) + ->method('unsubscribeCustomer') + ->with($customerId, $storeId); $this->messageManagerMock->expects($this->once()) ->method('addSuccessMessage') @@ -182,6 +196,9 @@ public function testExecute() $this->massAction->execute(); } + /** + * Test to mass unsubscribe customers to newsletters with throws exception + */ public function testExecuteWithException() { $customersIds = [10, 11, 12]; @@ -195,7 +212,7 @@ public function testExecuteWithException() ->willThrowException(new \Exception('Some message.')); $this->messageManagerMock->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('Some message.'); $this->massAction->execute(); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php index 9724ac13dde8c..2e729873961c0 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php @@ -5,13 +5,45 @@ */ namespace Magento\Customer\Test\Unit\Controller\Adminhtml\Index; +use Magento\Backend\App\Action\Context; +use Magento\Backend\Model\Session; +use Magento\Backend\Model\View\Result\Forward; +use Magento\Backend\Model\View\Result\ForwardFactory; +use Magento\Backend\Model\View\Result\RedirectFactory; +use Magento\Customer\Api\AddressRepositoryInterface; use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterfaceFactory; use Magento\Customer\Api\Data\AttributeMetadataInterface; use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Api\Data\CustomerInterfaceFactory; +use Magento\Customer\Controller\Adminhtml\Index\Save; use Magento\Customer\Controller\RegistryConstants; +use Magento\Customer\Model\AccountManagement; +use Magento\Customer\Model\Address\Mapper; use Magento\Customer\Model\EmailNotificationInterface; use Magento\Customer\Model\Metadata\Form; +use Magento\Customer\Model\Metadata\FormFactory; +use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\App\Request\Http; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\AuthorizationInterface; use Magento\Framework\Controller\Result\Redirect; +use Magento\Framework\DataObject; +use Magento\Framework\DataObjectFactory; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Message\Error; +use Magento\Framework\Message\ManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\View\Page\Config; +use Magento\Framework\View\Page\Title; +use Magento\Framework\View\Result\Page; +use Magento\Framework\View\Result\PageFactory; +use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Newsletter\Model\SubscriptionManagerInterface; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; /** * Testing Save Customer use case from admin page @@ -20,220 +52,226 @@ * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @covers \Magento\Customer\Controller\Adminhtml\Index\Save */ -class SaveTest extends \PHPUnit\Framework\TestCase +class SaveTest extends TestCase { /** - * @var \Magento\Customer\Controller\Adminhtml\Index\Save + * @var Save */ protected $model; /** - * @var \Magento\Backend\App\Action\Context + * @var Context */ protected $context; /** - * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject + * @var RequestInterface|MockObject */ protected $requestMock; /** - * @var \Magento\Backend\Model\View\Result\ForwardFactory|\PHPUnit_Framework_MockObject_MockObject + * @var ForwardFactory|MockObject */ protected $resultForwardFactoryMock; /** - * @var \Magento\Backend\Model\View\Result\Forward|\PHPUnit_Framework_MockObject_MockObject + * @var Forward|MockObject */ protected $resultForwardMock; /** - * @var \Magento\Framework\View\Result\PageFactory|\PHPUnit_Framework_MockObject_MockObject + * @var PageFactory|MockObject */ protected $resultPageFactoryMock; /** - * @var \Magento\Framework\View\Result\Page|\PHPUnit_Framework_MockObject_MockObject + * @var Page|MockObject */ protected $resultPageMock; /** - * @var \Magento\Framework\View\Page\Config|\PHPUnit_Framework_MockObject_MockObject + * @var Config|MockObject */ protected $pageConfigMock; /** - * @var \Magento\Framework\View\Page\Title|\PHPUnit_Framework_MockObject_MockObject + * @var Title|MockObject */ protected $pageTitleMock; /** - * @var \Magento\Backend\Model\Session|\PHPUnit_Framework_MockObject_MockObject + * @var Session|MockObject */ protected $sessionMock; /** - * @var \Magento\Customer\Model\Metadata\FormFactory|\PHPUnit_Framework_MockObject_MockObject + * @var FormFactory|MockObject */ protected $formFactoryMock; /** - * @var \Magento\Framework\DataObjectFactory|\PHPUnit_Framework_MockObject_MockObject + * @var DataObjectFactory|MockObject */ protected $objectFactoryMock; /** - * @var \Magento\Customer\Api\Data\CustomerInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerInterfaceFactory|MockObject */ protected $customerDataFactoryMock; /** - * @var \Magento\Customer\Api\CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerRepositoryInterface|MockObject */ protected $customerRepositoryMock; /** - * @var \Magento\Customer\Model\Customer\Mapper|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Customer\Model\Customer\Mapper|MockObject */ protected $customerMapperMock; /** - * @var \Magento\Framework\Api\DataObjectHelper|\PHPUnit_Framework_MockObject_MockObject + * @var DataObjectHelper|MockObject */ protected $dataHelperMock; /** - * @var \Magento\Framework\AuthorizationInterface|\PHPUnit_Framework_MockObject_MockObject + * @var AuthorizationInterface|MockObject */ protected $authorizationMock; /** - * @var \Magento\Newsletter\Model\SubscriberFactory|\PHPUnit_Framework_MockObject_MockObject + * @var SubscriberFactory|MockObject */ protected $subscriberFactoryMock; /** - * @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject + * @var Registry|MockObject */ protected $registryMock; /** - * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ManagerInterface|MockObject */ protected $messageManagerMock; /** - * @var \Magento\Backend\Model\View\Result\RedirectFactory|\PHPUnit_Framework_MockObject_MockObject + * @var RedirectFactory|MockObject */ protected $redirectFactoryMock; /** - * @var \Magento\Customer\Model\AccountManagement|\PHPUnit_Framework_MockObject_MockObject + * @var AccountManagement|MockObject */ protected $managementMock; /** - * @var \Magento\Customer\Api\Data\AddressInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + * @var AddressInterfaceFactory|MockObject */ protected $addressDataFactoryMock; /** - * @var EmailNotificationInterface|\PHPUnit_Framework_MockObject_MockObject + * @var EmailNotificationInterface|MockObject */ protected $emailNotificationMock; /** - * @var \Magento\Customer\Model\Address\Mapper|\PHPUnit_Framework_MockObject_MockObject + * @var Mapper|MockObject */ protected $customerAddressMapperMock; /** - * @var \Magento\Customer\Api\AddressRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var AddressRepositoryInterface|MockObject */ protected $customerAddressRepositoryMock; + /** + * @var SubscriptionManagerInterface|MockObject + */ + private $subscriptionManager; + /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function setUp() { - $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) + $this->requestMock = $this->getMockBuilder(Http::class) ->disableOriginalConstructor() ->getMock(); $this->resultForwardFactoryMock = $this->getMockBuilder( - \Magento\Backend\Model\View\Result\ForwardFactory::class + ForwardFactory::class )->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->resultForwardMock = $this->getMockBuilder(\Magento\Backend\Model\View\Result\Forward::class) + $this->resultForwardMock = $this->getMockBuilder(Forward::class) ->disableOriginalConstructor() ->getMock(); - $this->resultPageFactoryMock = $this->getMockBuilder(\Magento\Framework\View\Result\PageFactory::class) + $this->resultPageFactoryMock = $this->getMockBuilder(PageFactory::class) ->disableOriginalConstructor() ->getMock(); - $this->resultPageMock = $this->getMockBuilder(\Magento\Framework\View\Result\Page::class) + $this->resultPageMock = $this->getMockBuilder(Page::class) ->disableOriginalConstructor() ->setMethods(['setActiveMenu', 'getConfig', 'addBreadcrumb']) ->getMock(); - $this->pageConfigMock = $this->getMockBuilder(\Magento\Framework\View\Page\Config::class) + $this->pageConfigMock = $this->getMockBuilder(Config::class) ->disableOriginalConstructor() ->getMock(); - $this->pageTitleMock = $this->getMockBuilder(\Magento\Framework\View\Page\Title::class) + $this->pageTitleMock = $this->getMockBuilder(Title::class) ->disableOriginalConstructor() ->getMock(); - $this->sessionMock = $this->getMockBuilder(\Magento\Backend\Model\Session::class) + $this->sessionMock = $this->getMockBuilder(Session::class) ->disableOriginalConstructor() ->setMethods(['unsCustomerFormData', 'setCustomerFormData']) ->getMock(); - $this->formFactoryMock = $this->getMockBuilder(\Magento\Customer\Model\Metadata\FormFactory::class) + $this->formFactoryMock = $this->getMockBuilder(FormFactory::class) ->disableOriginalConstructor() ->getMock(); - $this->objectFactoryMock = $this->getMockBuilder(\Magento\Framework\DataObjectFactory::class) + $this->objectFactoryMock = $this->getMockBuilder(DataObjectFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); $this->customerDataFactoryMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\CustomerInterfaceFactory::class + CustomerInterfaceFactory::class )->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->customerRepositoryMock = $this->getMockBuilder(\Magento\Customer\Api\CustomerRepositoryInterface::class) + $this->customerRepositoryMock = $this->getMockBuilder(CustomerRepositoryInterface::class) ->disableOriginalConstructor() ->getMock(); $this->customerAddressRepositoryMock = $this->getMockBuilder( - \Magento\Customer\Api\AddressRepositoryInterface::class + AddressRepositoryInterface::class )->disableOriginalConstructor()->getMock(); $this->customerMapperMock = $this->getMockBuilder( \Magento\Customer\Model\Customer\Mapper::class )->disableOriginalConstructor()->getMock(); $this->customerAddressMapperMock = $this->getMockBuilder( - \Magento\Customer\Model\Address\Mapper::class + Mapper::class )->disableOriginalConstructor()->getMock(); $this->dataHelperMock = $this->getMockBuilder( - \Magento\Framework\Api\DataObjectHelper::class + DataObjectHelper::class )->disableOriginalConstructor()->getMock(); - $this->authorizationMock = $this->getMockBuilder(\Magento\Framework\AuthorizationInterface::class) + $this->authorizationMock = $this->getMockBuilder(AuthorizationInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->subscriberFactoryMock = $this->getMockBuilder(\Magento\Newsletter\Model\SubscriberFactory::class) + $this->subscriberFactoryMock = $this->getMockBuilder(SubscriberFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->registryMock = $this->getMockBuilder(\Magento\Framework\Registry::class) + $this->subscriptionManager = $this->createMock(SubscriptionManagerInterface::class); + $this->registryMock = $this->getMockBuilder(Registry::class) ->disableOriginalConstructor() ->getMock(); - $this->messageManagerMock = $this->getMockBuilder(\Magento\Framework\Message\ManagerInterface::class) + $this->messageManagerMock = $this->getMockBuilder(ManagerInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->redirectFactoryMock = $this->getMockBuilder(\Magento\Backend\Model\View\Result\RedirectFactory::class) + $this->redirectFactoryMock = $this->getMockBuilder(RedirectFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->managementMock = $this->getMockBuilder(\Magento\Customer\Model\AccountManagement::class) + $this->managementMock = $this->getMockBuilder(AccountManagement::class) ->disableOriginalConstructor() ->setMethods(['createAccount']) ->getMock(); - $this->addressDataFactoryMock = $this->getMockBuilder(\Magento\Customer\Api\Data\AddressInterfaceFactory::class) + $this->addressDataFactoryMock = $this->getMockBuilder(AddressInterfaceFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); @@ -241,10 +279,10 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $objectManager = new ObjectManager($this); $this->model = $objectManager->getObject( - \Magento\Customer\Controller\Adminhtml\Index\Save::class, + Save::class, [ 'resultForwardFactory' => $this->resultForwardFactoryMock, 'resultPageFactory' => $this->resultPageFactoryMock, @@ -265,6 +303,7 @@ protected function setUp() 'resultRedirectFactory' => $this->redirectFactoryMock, 'addressRepository' => $this->customerAddressRepositoryMock, 'addressMapper' => $this->customerAddressMapperMock, + 'subscriptionManager' => $this->subscriptionManager, ] ); @@ -282,7 +321,10 @@ protected function setUp() public function testExecuteWithExistentCustomer() { $customerId = 22; - $subscription = 'true'; + $customerEmail = 'customer@email.com'; + $subscriptionWebsite = 1; + $subscriptionStatus = true; + $subscriptionStore = 3; $postValue = [ 'customer' => [ 'entity_id' => $customerId, @@ -290,7 +332,8 @@ public function testExecuteWithExistentCustomer() 'coolness' => false, 'disable_auto_group_change' => 'false', ], - 'subscription' => $subscription, + 'subscription_status' => [$subscriptionWebsite => $subscriptionStatus], + 'subscription_store' => [$subscriptionWebsite => $subscriptionStore], ]; $extractedData = [ 'entity_id' => $customerId, @@ -324,9 +367,9 @@ public function testExecuteWithExistentCustomer() 'id' => $customerId, ]; - /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ + /** @var AttributeMetadataInterface|MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\AttributeMetadataInterface::class + AttributeMetadataInterface::class )->disableOriginalConstructor()->getMock(); $attributeMock->expects($this->atLeastOnce()) ->method('getAttributeCode') @@ -346,15 +389,20 @@ public function testExecuteWithExistentCustomer() ); $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') + ->with('customer') + ->willReturn($postValue['customer']); + $this->requestMock->expects($this->atLeastOnce()) + ->method('getParam') ->willReturnMap( [ - ['customer', null, $postValue['customer']], - ['subscription', null, $subscription], + ['subscription_status', null, [$subscriptionWebsite => $subscriptionStatus]], + ['subscription_store', null, [$subscriptionWebsite => $subscriptionStore]], + ['back', false, true], ] ); - /** @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject $objectMock */ - $objectMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) + /** @var DataObject|MockObject $objectMock */ + $objectMock = $this->getMockBuilder(DataObject::class) ->disableOriginalConstructor() ->getMock(); $objectMock->expects($this->atLeastOnce()) @@ -371,7 +419,7 @@ public function testExecuteWithExistentCustomer() ->willReturn($objectMock); $customerFormMock = $this->getMockBuilder( - \Magento\Customer\Model\Metadata\Form::class + Form::class )->disableOriginalConstructor()->getMock(); $customerFormMock->expects($this->once()) ->method('extractData') @@ -400,32 +448,26 @@ public function testExecuteWithExistentCustomer() ] ); - /** @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customerMock */ - $customerMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\CustomerInterface::class - )->disableOriginalConstructor()->getMock(); - + /** @var CustomerInterface|MockObject $customerMock */ + $customerMock = $this->createMock(CustomerInterface::class); + $customerMock->method('getId')->willReturn($customerId); $this->customerDataFactoryMock->expects($this->once()) ->method('create') ->willReturn($customerMock); - - $this->customerRepositoryMock->expects($this->exactly(2)) - ->method('getById') + $this->customerRepositoryMock->method('getById') ->with($customerId) ->willReturn($customerMock); - $this->customerMapperMock->expects($this->exactly(2)) ->method('toFlatArray') ->with($customerMock) ->willReturn($savedData); - $this->dataHelperMock->expects($this->atLeastOnce()) ->method('populateWithArray') ->willReturnMap( [ [ $customerMock, - $mergedData, \Magento\Customer\Api\Data\CustomerInterface::class, + $mergedData, CustomerInterface::class, $this->dataHelperMock ], ] @@ -435,10 +477,7 @@ public function testExecuteWithExistentCustomer() ->method('save') ->with($customerMock) ->willReturnSelf(); - - $customerEmail = 'customer@email.com'; $customerMock->expects($this->once())->method('getEmail')->willReturn($customerEmail); - $customerMock->expects($this->once()) ->method('getAddresses') ->willReturn([]); @@ -453,25 +492,12 @@ public function testExecuteWithExistentCustomer() ->with(null) ->willReturn(true); - /** @var \Magento\Newsletter\Model\Subscriber|\PHPUnit_Framework_MockObject_MockObject $subscriberMock */ - $subscriberMock = $this->getMockBuilder(\Magento\Newsletter\Model\Subscriber::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->subscriberFactoryMock->expects($this->once()) - ->method('create') - ->with() - ->willReturn($subscriberMock); - - $subscriberMock->expects($this->once()) - ->method('subscribeCustomerById') - ->with($customerId); - $subscriberMock->expects($this->never()) - ->method('unsubscribeCustomerById'); + $this->subscriptionManager->expects($this->once()) + ->method($subscriptionStatus ? 'subscribeCustomer' : 'unsubscribeCustomer') + ->with($customerId, $subscriptionStore); $this->sessionMock->expects($this->once()) ->method('unsCustomerFormData'); - $this->registryMock->expects($this->once()) ->method('register') ->with(RegistryConstants::CURRENT_CUSTOMER_ID, $customerId); @@ -481,13 +507,8 @@ public function testExecuteWithExistentCustomer() ->with(__('You saved the customer.')) ->willReturnSelf(); - $this->requestMock->expects($this->once()) - ->method('getParam') - ->with('back', false) - ->willReturn(true); - - /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ - $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) + /** @var Redirect|MockObject $redirectMock */ + $redirectMock = $this->getMockBuilder(Redirect::class) ->disableOriginalConstructor() ->getMock(); @@ -511,14 +532,17 @@ public function testExecuteWithExistentCustomer() public function testExecuteWithNewCustomer() { $customerId = 22; + $subscriptionWebsite = 1; + $subscriptionStatus = false; + $subscriptionStore = 3; - $subscription = '0'; $postValue = [ 'customer' => [ 'coolness' => false, 'disable_auto_group_change' => 'false', ], - 'subscription' => $subscription, + 'subscription_status' => [$subscriptionWebsite => $subscriptionStatus], + 'subscription_store' => [$subscriptionWebsite => $subscriptionStore], ]; $extractedData = [ 'coolness' => false, @@ -530,9 +554,9 @@ public function testExecuteWithNewCustomer() CustomerInterface::DEFAULT_SHIPPING => null, 'confirmation' => false, ]; - /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ + /** @var AttributeMetadataInterface|MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\AttributeMetadataInterface::class + AttributeMetadataInterface::class )->disableOriginalConstructor()->getMock(); $attributeMock->expects($this->atLeastOnce()) ->method('getAttributeCode') @@ -552,15 +576,20 @@ public function testExecuteWithNewCustomer() ); $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') + ->with('customer') + ->willReturn($postValue['customer']); + $this->requestMock->expects($this->atLeastOnce()) + ->method('getParam') ->willReturnMap( [ - ['customer', null, $postValue['customer']], - ['subscription', null, $subscription], + ['subscription_status', null, [$subscriptionWebsite => $subscriptionStatus]], + ['subscription_store', null, [$subscriptionWebsite => $subscriptionStore]], + ['back', false, false], ] ); - /** @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject $objectMock */ - $objectMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) + /** @var DataObject|MockObject $objectMock */ + $objectMock = $this->getMockBuilder(DataObject::class) ->disableOriginalConstructor() ->getMock(); $objectMock->expects($this->atLeastOnce()) @@ -577,7 +606,7 @@ public function testExecuteWithNewCustomer() ->willReturn($objectMock); $customerFormMock = $this->getMockBuilder( - \Magento\Customer\Model\Metadata\Form::class + Form::class )->disableOriginalConstructor()->getMock(); $customerFormMock->expects($this->once()) ->method('extractData') @@ -607,60 +636,36 @@ public function testExecuteWithNewCustomer() ] ); - /** @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customerMock */ - $customerMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\CustomerInterface::class - )->disableOriginalConstructor()->getMock(); - + /** @var CustomerInterface|MockObject $customerMock */ + $customerMock = $this->createMock(CustomerInterface::class); + $customerMock->method('getId')->willReturn($customerId); $this->customerDataFactoryMock->expects($this->once()) ->method('create') ->willReturn($customerMock); - $this->dataHelperMock->expects($this->atLeastOnce()) ->method('populateWithArray') ->willReturnMap( [ [ $customerMock, - $mergedData, \Magento\Customer\Api\Data\CustomerInterface::class, + $mergedData, CustomerInterface::class, $this->dataHelperMock ], ] ); - $this->managementMock->expects($this->once()) ->method('createAccount') ->with($customerMock, null, '') ->willReturn($customerMock); - - $customerMock->expects($this->once()) - ->method('getId') - ->willReturn($customerId); - $this->authorizationMock->expects($this->once()) ->method('isAllowed') ->with(null) ->willReturn(true); - - /** @var \Magento\Newsletter\Model\Subscriber|\PHPUnit_Framework_MockObject_MockObject $subscriberMock */ - $subscriberMock = $this->getMockBuilder(\Magento\Newsletter\Model\Subscriber::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->subscriberFactoryMock->expects($this->once()) - ->method('create') - ->with() - ->willReturn($subscriberMock); - - $subscriberMock->expects($this->once()) - ->method('unsubscribeCustomerById') - ->with($customerId); - $subscriberMock->expects($this->never()) - ->method('subscribeCustomerById'); - + $this->subscriptionManager->expects($this->once()) + ->method($subscriptionStatus ? 'subscribeCustomer' : 'unsubscribeCustomer') + ->with($customerId, $subscriptionStore); $this->sessionMock->expects($this->once()) ->method('unsCustomerFormData'); - $this->registryMock->expects($this->once()) ->method('register') ->with(RegistryConstants::CURRENT_CUSTOMER_ID, $customerId); @@ -670,13 +675,8 @@ public function testExecuteWithNewCustomer() ->with(__('You saved the customer.')) ->willReturnSelf(); - $this->requestMock->expects($this->once()) - ->method('getParam') - ->with('back', false) - ->willReturn(false); - - /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ - $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) + /** @var Redirect|MockObject $redirectMock */ + $redirectMock = $this->getMockBuilder(Redirect::class) ->disableOriginalConstructor() ->getMock(); @@ -713,9 +713,9 @@ public function testExecuteWithNewCustomerAndValidationException() 'dob' => '1996-03-12', ]; - /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ + /** @var AttributeMetadataInterface|MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\AttributeMetadataInterface::class + AttributeMetadataInterface::class )->disableOriginalConstructor()->getMock(); $attributeMock->expects($this->exactly(2)) ->method('getAttributeCode') @@ -741,8 +741,8 @@ public function testExecuteWithNewCustomerAndValidationException() ] ); - /** @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject $objectMock */ - $objectMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) + /** @var DataObject|MockObject $objectMock */ + $objectMock = $this->getMockBuilder(DataObject::class) ->disableOriginalConstructor() ->getMock(); $objectMock->expects($this->exactly(2)) @@ -756,7 +756,7 @@ public function testExecuteWithNewCustomerAndValidationException() ->willReturn($objectMock); $customerFormMock = $this->getMockBuilder( - \Magento\Customer\Model\Metadata\Form::class + Form::class )->disableOriginalConstructor()->getMock(); $customerFormMock->expects($this->exactly(2)) ->method('extractData') @@ -780,9 +780,9 @@ public function testExecuteWithNewCustomerAndValidationException() Form::DONT_IGNORE_INVISIBLE )->willReturn($customerFormMock); - /** @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customerMock */ + /** @var CustomerInterface|MockObject $customerMock */ $customerMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\CustomerInterface::class + CustomerInterface::class )->disableOriginalConstructor()->getMock(); $this->customerDataFactoryMock->expects($this->once()) @@ -814,7 +814,7 @@ public function testExecuteWithNewCustomerAndValidationException() $this->messageManagerMock->expects($this->once()) ->method('addMessage') - ->with(new \Magento\Framework\Message\Error('Validator Exception')); + ->with(new Error('Validator Exception')); $this->sessionMock->expects($this->once()) ->method('setCustomerFormData') @@ -825,8 +825,8 @@ public function testExecuteWithNewCustomerAndValidationException() ] ); - /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ - $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) + /** @var Redirect|MockObject $redirectMock */ + $redirectMock = $this->getMockBuilder(Redirect::class) ->disableOriginalConstructor() ->getMock(); @@ -864,9 +864,9 @@ public function testExecuteWithNewCustomerAndLocalizedException() 'dob' => '1996-03-12', ]; - /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ + /** @var AttributeMetadataInterface|MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\AttributeMetadataInterface::class + AttributeMetadataInterface::class )->disableOriginalConstructor()->getMock(); $attributeMock->expects($this->exactly(2)) ->method('getAttributeCode') @@ -892,8 +892,8 @@ public function testExecuteWithNewCustomerAndLocalizedException() ] ); - /** @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject $objectMock */ - $objectMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) + /** @var DataObject|MockObject $objectMock */ + $objectMock = $this->getMockBuilder(DataObject::class) ->disableOriginalConstructor() ->getMock(); $objectMock->expects($this->exactly(2)) @@ -906,9 +906,9 @@ public function testExecuteWithNewCustomerAndLocalizedException() ->with(['data' => $postValue]) ->willReturn($objectMock); - /** @var Form|\PHPUnit_Framework_MockObject_MockObject $formMock */ + /** @var Form|MockObject $formMock */ $customerFormMock = $this->getMockBuilder( - \Magento\Customer\Model\Metadata\Form::class + Form::class )->disableOriginalConstructor()->getMock(); $customerFormMock->expects($this->exactly(2)) ->method('extractData') @@ -933,7 +933,7 @@ public function testExecuteWithNewCustomerAndLocalizedException() )->willReturn($customerFormMock); $customerMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\CustomerInterface::class + CustomerInterface::class )->disableOriginalConstructor()->getMock(); $this->customerDataFactoryMock->expects($this->once()) @@ -943,7 +943,7 @@ public function testExecuteWithNewCustomerAndLocalizedException() $this->managementMock->expects($this->once()) ->method('createAccount') ->with($customerMock, null, '') - ->willThrowException(new \Magento\Framework\Exception\LocalizedException(__('Localized Exception'))); + ->willThrowException(new LocalizedException(__('Localized Exception'))); $customerMock->expects($this->never()) ->method('getId'); @@ -965,7 +965,7 @@ public function testExecuteWithNewCustomerAndLocalizedException() $this->messageManagerMock->expects($this->once()) ->method('addMessage') - ->with(new \Magento\Framework\Message\Error('Localized Exception')); + ->with(new Error('Localized Exception')); $this->sessionMock->expects($this->once()) ->method('setCustomerFormData') @@ -976,8 +976,8 @@ public function testExecuteWithNewCustomerAndLocalizedException() ] ); - /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ - $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) + /** @var Redirect|MockObject $redirectMock */ + $redirectMock = $this->getMockBuilder(Redirect::class) ->disableOriginalConstructor() ->getMock(); @@ -1015,9 +1015,9 @@ public function testExecuteWithNewCustomerAndException() 'dob' => '1996-03-12', ]; - /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ + /** @var AttributeMetadataInterface|MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\AttributeMetadataInterface::class + AttributeMetadataInterface::class )->disableOriginalConstructor()->getMock(); $attributeMock->expects($this->exactly(2)) ->method('getAttributeCode') @@ -1043,8 +1043,8 @@ public function testExecuteWithNewCustomerAndException() ] ); - /** @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject $objectMock */ - $objectMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) + /** @var DataObject|MockObject $objectMock */ + $objectMock = $this->getMockBuilder(DataObject::class) ->disableOriginalConstructor() ->getMock(); $objectMock->expects($this->exactly(2)) @@ -1058,7 +1058,7 @@ public function testExecuteWithNewCustomerAndException() ->willReturn($objectMock); $customerFormMock = $this->getMockBuilder( - \Magento\Customer\Model\Metadata\Form::class + Form::class )->disableOriginalConstructor()->getMock(); $customerFormMock->expects($this->exactly(2)) ->method('extractData') @@ -1082,9 +1082,9 @@ public function testExecuteWithNewCustomerAndException() Form::DONT_IGNORE_INVISIBLE )->willReturn($customerFormMock); - /** @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customerMock */ + /** @var CustomerInterface|MockObject $customerMock */ $customerMock = $this->getMockBuilder( - \Magento\Customer\Api\Data\CustomerInterface::class + CustomerInterface::class )->disableOriginalConstructor()->getMock(); $this->customerDataFactoryMock->expects($this->once()) @@ -1128,8 +1128,8 @@ public function testExecuteWithNewCustomerAndException() ] ); - /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $redirectMock */ - $redirectMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Redirect::class) + /** @var Redirect|MockObject $redirectMock */ + $redirectMock = $this->getMockBuilder(Redirect::class) ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Customer/Test/Unit/CustomerData/CustomerTest.php b/app/code/Magento/Customer/Test/Unit/CustomerData/CustomerTest.php new file mode 100644 index 0000000000000..735c526878b6b --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/CustomerData/CustomerTest.php @@ -0,0 +1,90 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Customer\Test\Unit\CustomerData; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Customer\CustomerData\Customer as CustomerData; +use Magento\Customer\Helper\Session\CurrentCustomer; +use Magento\Customer\Helper\View; +use Magento\Customer\Api\Data\CustomerInterface; +use PHPUnit\Framework\TestCase; + +class CustomerTest extends TestCase +{ + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var CustomerData + */ + private $customerData; + + /** + * @var CurrentCustomer + */ + private $currentCustomerMock; + + /** + * @var View + */ + private $customerViewHelperMock; + + /** + * Setup environment to test + */ + protected function setUp() + { + $this->currentCustomerMock = $this->createMock(CurrentCustomer::class); + $this->customerViewHelperMock = $this->createMock(View::class); + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->customerData = $this->objectManagerHelper->getObject( + CustomerData::class, + [ + 'currentCustomer' => $this->currentCustomerMock, + 'customerViewHelper' => $this->customerViewHelperMock + ] + ); + } + + /** + * Test getSectionData() without customer Id + */ + public function testGetSectionDataWithoutCustomerId() + { + $this->currentCustomerMock->expects($this->any())->method('getCustomerId')->willReturn(null); + $this->assertEquals([], $this->customerData->getSectionData()); + } + + /** + * Test getSectionData() with customer + */ + public function testGetSectionDataWithCustomer() + { + $this->currentCustomerMock->expects($this->any())->method('getCustomerId')->willReturn(1); + $customerMock = $this->createMock(CustomerInterface::class); + $customerMock->expects($this->any())->method('getFirstname')->willReturn('John'); + $customerMock->expects($this->any())->method('getWebsiteId')->willReturn(1); + $this->currentCustomerMock->expects($this->any())->method('getCustomer')->willReturn($customerMock); + $this->customerViewHelperMock->expects($this->any())->method('getCustomerName') + ->with($customerMock) + ->willReturn('John Adam'); + + $this->assertEquals( + [ + 'fullname' => 'John Adam', + 'firstname' => 'John', + 'websiteId' => 1, + ], + $this->customerData->getSectionData() + ); + } +} diff --git a/app/code/Magento/Customer/Test/Unit/CustomerData/JsLayoutDataProviderPoolTest.php b/app/code/Magento/Customer/Test/Unit/CustomerData/JsLayoutDataProviderPoolTest.php new file mode 100644 index 0000000000000..7b7f6ec9f841b --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/CustomerData/JsLayoutDataProviderPoolTest.php @@ -0,0 +1,84 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Customer\Test\Unit\CustomerData; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Tax\CustomerData\CheckoutTotalsJsLayoutDataProvider as CheckoutTotalsJs; +use Magento\Customer\CustomerData\JsLayoutDataProviderPool; +use PHPUnit\Framework\TestCase; + +class JsLayoutDataProviderPoolTest extends TestCase +{ + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var CheckoutTotalsJs + */ + private $checkoutTotalsJsLayoutDataProviderMock; + + /** + * @var JsLayoutDataProviderPool + */ + private $jsLayoutDataProviderPool; + + /** + * Setup environment to test + */ + protected function setUp() + { + $this->checkoutTotalsJsLayoutDataProviderMock = $this->createMock(CheckoutTotalsJs::class); + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->jsLayoutDataProviderPool = $this->objectManagerHelper->getObject( + JsLayoutDataProviderPool::class, + [ + 'jsLayoutDataProviders' => [ + 'checkout_totals' => $this->checkoutTotalsJsLayoutDataProviderMock + ] + ] + ); + } + + /** + * Test getData() function + */ + public function testGetData() + { + $checkoutTotalsJsData = [ + 'components' => [ + 'minicart_content' => [ + 'children' => [ + 'subtotal.container' => [ + 'children' => [ + 'subtotal' => [ + 'children' => [ + 'subtotal.totals' => [ + 'config' => [ + 'display_cart_subtotal_incl_tax' => 1, + 'display_cart_subtotal_excl_tax' => 1 + ] + ], + ], + ], + ], + ], + ], + ], + ] + ]; + $this->checkoutTotalsJsLayoutDataProviderMock->expects($this->any()) + ->method('getData') + ->willReturn($checkoutTotalsJsData); + + $this->assertEquals($checkoutTotalsJsData, $this->jsLayoutDataProviderPool->getData()); + } +} diff --git a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php index 8032399e14881..015213847e7ee 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php @@ -211,6 +211,7 @@ public function testSave() 'setFirstFailure', 'setLockExpires', 'save', + 'setGroupId' ] ); @@ -245,9 +246,15 @@ public function testSave() $this->customer->expects($this->atLeastOnce()) ->method('getId') ->willReturn($customerId); - $this->customer->expects($this->atLeastOnce()) + $this->customer->expects($this->at(4)) ->method('__toArray') ->willReturn([]); + $this->customer->expects($this->at(3)) + ->method('__toArray') + ->willReturn(['group_id' => 1]); + $customerModel->expects($this->once()) + ->method('setGroupId') + ->with(1); $this->customerRegistry->expects($this->atLeastOnce()) ->method('retrieve') ->with($customerId) diff --git a/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/GroupActionsTest.php b/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/GroupActionsTest.php new file mode 100644 index 0000000000000..02cacea5c2601 --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/GroupActionsTest.php @@ -0,0 +1,313 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Test\Unit\Ui\Component\Listing\Column; + +use Magento\Customer\Api\GroupManagementInterface; +use Magento\Customer\Ui\Component\Listing\Column\GroupActions; +use Magento\Framework\Escaper; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\UrlInterface; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\View\Element\UiComponentFactory; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Class GroupActionsTest + * + * Testing GroupAction grid column + */ +class GroupActionsTest extends TestCase +{ + /** + * @var int + */ + private const STUB_NOT_LOGGED_IN_CUSTOMER_GROUP_ID = 0; + + /** + * @var string + */ + private const STUB_NOT_LOGGED_IN_CUSTOMER_GROUP_NAME = 'Not Logged In'; + + /** + * @var int + */ + private const STUB_GENERAL_CUSTOMER_GROUP_ID = 1; + + /** + * @var string + */ + private const STUB_GENERAL_CUSTOMER_GROUP_NAME = 'General'; + + /** + * @var string + */ + private const STUB_GROUP_EDIT_URL = 'http://magento.com/customer/group/edit'; + + /** + * @var string + */ + private const STUB_GROUP_DELETE_URL = 'http://magento.com/customer/group/delete'; + + /** + * @var GroupActions + */ + private $component; + + /** + * @var ContextInterface|MockObject + */ + private $contextMock; + + /** + * @var UiComponentFactory|MockObject + */ + private $uiComponentFactoryMock; + + /** + * @var UrlInterface|MockObject + */ + private $urlBuilderMock; + + /** + * @var Escaper|MockObject + */ + private $escaperMock; + + /** + * @var GroupManagementInterface|MockObject + */ + private $groupManagementMock; + + /** + * Set Up + */ + public function setUp() + { + $objectManager = new ObjectManager($this); + + $this->contextMock = $this->getMockBuilder(ContextInterface::class)->getMockForAbstractClass(); + $this->uiComponentFactoryMock = $this->createMock(UiComponentFactory::class); + $this->escaperMock = $this->createMock(Escaper::class); + $this->groupManagementMock = $this->createMock(GroupManagementInterface::class); + $this->urlBuilderMock = $this->getMockForAbstractClass( + UrlInterface::class, + [], + '', + false + ); + + $this->component = $objectManager->getObject( + GroupActions::class, + [ + 'context' => $this->contextMock, + 'uiComponentFactory' => $this->uiComponentFactoryMock, + 'urlBuilder' => $this->urlBuilderMock, + 'escaper' => $this->escaperMock, + 'components' => [], + 'data' => [ + 'name' => 'name' + ], + 'groupManagement' => $this->groupManagementMock + ] + ); + } + + /** + * Test data source with a non default customer group + * + * @dataProvider customerGroupsDataProvider + * + * @param array $items + * @param bool $isDefaultGroup + * @param array $expected + */ + public function testPrepareDataSourceWithNonDefaultGroup(array $items, bool $isDefaultGroup, array $expected) + { + $dataSource = [ + 'data' => [ + 'items' => $items + ] + ]; + $expectedDataSource = [ + 'data' => [ + 'items' => $expected + ] + ]; + + $this->groupManagementMock->expects($this->any()) + ->method('isReadonly') + ->with(static::STUB_GENERAL_CUSTOMER_GROUP_ID) + ->willReturn($isDefaultGroup); + $this->escaperMock->expects($this->any()) + ->method('escapeHtml') + ->with(static::STUB_GENERAL_CUSTOMER_GROUP_NAME) + ->willReturn(static::STUB_GENERAL_CUSTOMER_GROUP_NAME); + $this->urlBuilderMock->expects($this->any()) + ->method('getUrl') + ->willReturnMap( + [ + [ + 'customer/group/edit', + [ + 'id' => static::STUB_GENERAL_CUSTOMER_GROUP_ID + ], + static::STUB_GROUP_EDIT_URL], + [ + 'customer/group/delete', + [ + 'id' => static::STUB_GENERAL_CUSTOMER_GROUP_ID + ], + static::STUB_GROUP_DELETE_URL + ] + ] + ); + + $dataSource = $this->component->prepareDataSource($dataSource); + $this->assertEquals($expectedDataSource, $dataSource); + } + + /** + * Test data source with a default customer group + * + * @dataProvider customerGroupsDataProvider + */ + public function testPrepareDataSourceWithDefaultGroup() + { + $isDefaultGroup = true; + $dataSource = [ + 'data' => [ + 'items' => [ + [ + 'customer_group_id' => static::STUB_GENERAL_CUSTOMER_GROUP_ID, + 'customer_group_code' => static::STUB_GENERAL_CUSTOMER_GROUP_NAME, + ], + [ + 'customer_group_id' => static::STUB_NOT_LOGGED_IN_CUSTOMER_GROUP_ID, + 'customer_group_code' => static::STUB_NOT_LOGGED_IN_CUSTOMER_GROUP_NAME, + ], + ] + ] + ]; + $expectedDataSource = [ + 'data' => [ + 'items' => [ + [ + 'customer_group_id' => static::STUB_GENERAL_CUSTOMER_GROUP_ID, + 'customer_group_code' => static::STUB_GENERAL_CUSTOMER_GROUP_NAME, + 'name' => [ + 'edit' => [ + 'href' => static::STUB_GROUP_EDIT_URL, + 'label' => __('Edit'), + '__disableTmpl' => true, + ] + ] + ], + [ + 'customer_group_id' => static::STUB_NOT_LOGGED_IN_CUSTOMER_GROUP_ID, + 'customer_group_code' => static::STUB_NOT_LOGGED_IN_CUSTOMER_GROUP_NAME, + 'name' => [ + 'edit' => [ + 'href' => static::STUB_GROUP_EDIT_URL, + 'label' => __('Edit'), + '__disableTmpl' => true, + ] + ] + ] + ] + ] + ]; + + $this->groupManagementMock->expects($this->any()) + ->method('isReadonly') + ->willReturn($isDefaultGroup); + $this->escaperMock->expects($this->any()) + ->method('escapeHtml') + ->willReturnMap( + [ + [ + static::STUB_GENERAL_CUSTOMER_GROUP_NAME, + null, + static::STUB_GENERAL_CUSTOMER_GROUP_NAME + ], + [ + static::STUB_NOT_LOGGED_IN_CUSTOMER_GROUP_NAME, + null, + static::STUB_NOT_LOGGED_IN_CUSTOMER_GROUP_NAME + ] + ] + ); + $this->urlBuilderMock->expects($this->any()) + ->method('getUrl') + ->willReturnMap( + [ + [ + 'customer/group/edit', + [ + 'id' => static::STUB_GENERAL_CUSTOMER_GROUP_ID + ], + static::STUB_GROUP_EDIT_URL + ], + [ + 'customer/group/edit', + [ + 'id' => static::STUB_NOT_LOGGED_IN_CUSTOMER_GROUP_ID + ], + static::STUB_GROUP_EDIT_URL + ] + ] + ); + + $dataSource = $this->component->prepareDataSource($dataSource); + $this->assertEquals($expectedDataSource, $dataSource); + } + + /** + * Providing customer group data + * + * @return array + */ + public function customerGroupsDataProvider(): array + { + return [ + [ + [ + [ + 'customer_group_id' => static::STUB_GENERAL_CUSTOMER_GROUP_ID, + 'customer_group_code' => static::STUB_GENERAL_CUSTOMER_GROUP_NAME, + ], + ], + false, + [ + [ + 'customer_group_id' => static::STUB_GENERAL_CUSTOMER_GROUP_ID, + 'customer_group_code' => static::STUB_GENERAL_CUSTOMER_GROUP_NAME, + 'name' => [ + 'edit' => [ + 'href' => static::STUB_GROUP_EDIT_URL, + 'label' => __('Edit'), + '__disableTmpl' => true, + ], + 'delete' => [ + 'href' => static::STUB_GROUP_DELETE_URL, + 'label' => __('Delete'), + 'post' => true, + '__disableTmpl' => true, + 'confirm' => [ + 'title' => __('Delete %1', 'General'), + 'message' => __( + 'Are you sure you want to delete a %1 record?', + 'General' + ) + ], + ] + ] + ] + ] + ] + ]; + } +} diff --git a/app/code/Magento/Customer/Ui/Component/Form/Field/DisableAutoGroupChange.php b/app/code/Magento/Customer/Ui/Component/Form/Field/DisableAutoGroupChange.php new file mode 100644 index 0000000000000..3404a0e92230c --- /dev/null +++ b/app/code/Magento/Customer/Ui/Component/Form/Field/DisableAutoGroupChange.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Ui\Component\Form\Field; + +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\View\Element\UiComponentFactory; +use Magento\Framework\View\Element\UiComponentInterface; +use Magento\Customer\Helper\Address as AddressHelper; + +/** + * Process setting to set Default Value for Disable Automatic Group Changes Based on VAT ID + * + * Class \Magento\Customer\Ui\Component\Form\Field\DisableAutoGroupChange + */ +class DisableAutoGroupChange extends \Magento\Ui\Component\Form\Field +{ + /** + * Yes value for Default Value for Disable Automatic Group Changes Based on VAT ID + */ + const DISABLE_AUTO_GROUP_CHANGE_YES = '1'; + + /** + * Address Helper + * + * @var AddressHelper + */ + private $addressHelper; + + /** + * Constructor + * + * @param ContextInterface $context + * @param UiComponentFactory $uiComponentFactory + * @param AddressHelper $addressHelper + * @param UiComponentInterface[] $components + * @param array $data + */ + public function __construct( + ContextInterface $context, + UiComponentFactory $uiComponentFactory, + AddressHelper $addressHelper, + array $components = [], + array $data = [] + ) { + $this->addressHelper = $addressHelper; + parent::__construct($context, $uiComponentFactory, $components, $data); + } + + /** + * Prepare component configuration + * + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function prepare() + { + parent::prepare(); + + if ($this->addressHelper->isDisableAutoGroupAssignDefaultValue()) { + $currentConfig = $this->getData('config'); + $currentConfig['default'] = self::DISABLE_AUTO_GROUP_CHANGE_YES; + $this->setData('config', $currentConfig); + } + } +} diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Column/GroupActions.php b/app/code/Magento/Customer/Ui/Component/Listing/Column/GroupActions.php index 12f6f2705125b..e5a536dc6ecd6 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Column/GroupActions.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Column/GroupActions.php @@ -8,6 +8,10 @@ namespace Magento\Customer\Ui\Component\Listing\Column; +use Magento\Customer\Api\GroupManagementInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\UrlInterface; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Framework\View\Element\UiComponentFactory; @@ -16,6 +20,8 @@ /** * Class GroupActions + * + * Customer Groups actions column */ class GroupActions extends Column { @@ -25,6 +31,11 @@ class GroupActions extends Column const URL_PATH_EDIT = 'customer/group/edit'; const URL_PATH_DELETE = 'customer/group/delete'; + /** + * @var GroupManagementInterface + */ + private $groupManagement; + /** * @var UrlInterface */ @@ -44,6 +55,7 @@ class GroupActions extends Column * @param Escaper $escaper * @param array $components * @param array $data + * @param GroupManagementInterface $groupManagement */ public function __construct( ContextInterface $context, @@ -51,10 +63,13 @@ public function __construct( UrlInterface $urlBuilder, Escaper $escaper, array $components = [], - array $data = [] + array $data = [], + GroupManagementInterface $groupManagement = null ) { $this->urlBuilder = $urlBuilder; $this->escaper = $escaper; + $this->groupManagement = $groupManagement ?: ObjectManager::getInstance()->get(GroupManagementInterface::class); + parent::__construct($context, $uiComponentFactory, $components, $data); } @@ -63,6 +78,8 @@ public function __construct( * * @param array $dataSource * @return array + * @throws LocalizedException + * @throws NoSuchEntityException */ public function prepareDataSource(array $dataSource) { @@ -83,29 +100,26 @@ public function prepareDataSource(array $dataSource) ], ]; - // hide delete action for 'NOT LOGGED IN' group - if ($item['customer_group_id'] == 0 && $item['customer_group_code']) { - continue; + if (!$this->groupManagement->isReadonly($item['customer_group_id'])) { + $item[$this->getData('name')]['delete'] = [ + 'href' => $this->urlBuilder->getUrl( + static::URL_PATH_DELETE, + [ + 'id' => $item['customer_group_id'] + ] + ), + 'label' => __('Delete'), + 'confirm' => [ + 'title' => __('Delete %1', $this->escaper->escapeHtml($title)), + 'message' => __( + 'Are you sure you want to delete a %1 record?', + $this->escaper->escapeHtml($title) + ) + ], + 'post' => true, + '__disableTmpl' => true + ]; } - - $item[$this->getData('name')]['delete'] = [ - 'href' => $this->urlBuilder->getUrl( - static::URL_PATH_DELETE, - [ - 'id' => $item['customer_group_id'] - ] - ), - 'label' => __('Delete'), - 'confirm' => [ - 'title' => __('Delete %1', $this->escaper->escapeJs($title)), - 'message' => __( - 'Are you sure you want to delete a %1 record?', - $this->escaper->escapeJs($title) - ) - ], - 'post' => true, - '__disableTmpl' => true - ]; } } } diff --git a/app/code/Magento/Customer/etc/di.xml b/app/code/Magento/Customer/etc/di.xml index 6086a61157ddc..be219a81fd990 100644 --- a/app/code/Magento/Customer/etc/di.xml +++ b/app/code/Magento/Customer/etc/di.xml @@ -473,4 +473,58 @@ <preference for="Magento\Customer\Api\AccountDelegationInterface" type="Magento\Customer\Model\Delegation\AccountDelegation" /> + <type name="Magento\Eav\Model\Config"> + <arguments> + <argument name="attributesForPreload" xsi:type="array"> + <item name="customer" xsi:type="array"> + <item name="confirmation" xsi:type="string">customer</item> + <item name="created_at" xsi:type="string">customer</item> + <item name="created_in" xsi:type="string">customer</item> + <item name="default_billing" xsi:type="string">customer</item> + <item name="default_shipping" xsi:type="string">customer</item> + <item name="disable_auto_group_change" xsi:type="string">customer</item> + <item name="dob" xsi:type="string">customer</item> + <item name="email" xsi:type="string">customer</item> + <item name="failures_num" xsi:type="string">customer</item> + <item name="firstname" xsi:type="string">customer</item> + <item name="first_failure" xsi:type="string">customer</item> + <item name="gender" xsi:type="string">customer</item> + <item name="group_id" xsi:type="string">customer</item> + <item name="lastname" xsi:type="string">customer</item> + <item name="lock_expires" xsi:type="string">customer</item> + <item name="middlename" xsi:type="string">customer</item> + <item name="password_hash" xsi:type="string">customer</item> + <item name="prefix" xsi:type="string">customer</item> + <item name="rp_token" xsi:type="string">customer</item> + <item name="rp_token_created_at" xsi:type="string">customer</item> + <item name="store_id" xsi:type="string">customer</item> + <item name="suffix" xsi:type="string">customer</item> + <item name="taxvat" xsi:type="string">customer</item> + <item name="updated_at" xsi:type="string">customer</item> + <item name="website_id" xsi:type="string">customer</item> + </item> + <item name="customer_address" xsi:type="array"> + <item name="city" xsi:type="string">customer_address</item> + <item name="company" xsi:type="string">customer_address</item> + <item name="country_id" xsi:type="string">customer_address</item> + <item name="fax" xsi:type="string">customer_address</item> + <item name="firstname" xsi:type="string">customer_address</item> + <item name="lastname" xsi:type="string">customer_address</item> + <item name="middlename" xsi:type="string">customer_address</item> + <item name="postcode" xsi:type="string">customer_address</item> + <item name="prefix" xsi:type="string">customer_address</item> + <item name="region" xsi:type="string">customer_address</item> + <item name="region_id" xsi:type="string">customer_address</item> + <item name="street" xsi:type="string">customer_address</item> + <item name="suffix" xsi:type="string">customer_address</item> + <item name="telephone" xsi:type="string">customer_address</item> + <item name="vat_id" xsi:type="string">customer_address</item> + <item name="vat_is_valid" xsi:type="string">customer_address</item> + <item name="vat_request_date" xsi:type="string">customer_address</item> + <item name="vat_request_id" xsi:type="string">customer_address</item> + <item name="vat_request_success" xsi:type="string">customer_address</item> + </item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Customer/registration.php b/app/code/Magento/Customer/registration.php index 744defc54ab27..4b88dbdab1d0e 100644 --- a/app/code/Magento/Customer/registration.php +++ b/app/code/Magento/Customer/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Customer', __DIR__); diff --git a/app/code/Magento/Customer/view/adminhtml/templates/tab/newsletter.phtml b/app/code/Magento/Customer/view/adminhtml/templates/tab/newsletter.phtml index 12d4902fb1892..d8bcc59689fac 100644 --- a/app/code/Magento/Customer/view/adminhtml/templates/tab/newsletter.phtml +++ b/app/code/Magento/Customer/view/adminhtml/templates/tab/newsletter.phtml @@ -4,6 +4,7 @@ * See COPYING.txt for license details. */ ?> +<?php /** @var \Magento\Customer\Block\Adminhtml\Edit\Tab\Newsletter $block */ ?> <div class="entry-edit"> <?= $block->getForm()->getHtml() ?> </div> diff --git a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml index 954b44ec19bbb..7caaeab4f39d6 100644 --- a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml +++ b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml @@ -152,8 +152,6 @@ <argument name="data" xsi:type="array"> <item name="type" xsi:type="string">group</item> <item name="config" xsi:type="array"> - <item name="label" xsi:type="string" translate="true">Group</item> - <item name="required" xsi:type="boolean">true</item> <item name="dataScope" xsi:type="boolean">false</item> <item name="validateWholeGroup" xsi:type="boolean">true</item> </item> @@ -166,10 +164,11 @@ </item> </argument> <settings> + <required>true</required> <dataType>number</dataType> </settings> </field> - <field name="disable_auto_group_change" formElement="checkbox"> + <field name="disable_auto_group_change" formElement="checkbox" class="Magento\Customer\Ui\Component\Form\Field\DisableAutoGroupChange"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="fieldGroup" xsi:type="string">group_id</item> diff --git a/app/code/Magento/Customer/view/frontend/email/account_new.html b/app/code/Magento/Customer/view/frontend/email/account_new.html index 6a60aee863eb4..7b77883e41f71 100644 --- a/app/code/Magento/Customer/view/frontend/email/account_new.html +++ b/app/code/Magento/Customer/view/frontend/email/account_new.html @@ -4,9 +4,11 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Welcome to %store_name" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Welcome to %store_name" store_name=$store.frontend_name}} @--> <!--@vars { -"var this.getUrl($store, 'customer/account/')":"Customer Account URL", +"var store.frontend_name":"Store Name", +"var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL", +"var this.getUrl($store,'customer/account/createPassword/',[_query:[id:$customer.id,token:$customer.rp_token],_nosid:1])":"Password Reset URL", "var customer.email":"Customer Email", "var customer.name":"Customer Name" } @--> @@ -14,7 +16,7 @@ {{template config_path="design/email/header_template"}} <p class="greeting">{{trans "%name," name=$customer.name}}</p> -<p>{{trans "Welcome to %store_name." store_name=$store.getFrontendName()}}</p> +<p>{{trans "Welcome to %store_name." store_name=$store.frontend_name}}</p> <p> {{trans 'To sign in to our site, use these credentials during checkout or on the <a href="%customer_url">My Account</a> page:' diff --git a/app/code/Magento/Customer/view/frontend/email/account_new_confirmation.html b/app/code/Magento/Customer/view/frontend/email/account_new_confirmation.html index 010087ace2d42..364fde0f5150c 100644 --- a/app/code/Magento/Customer/view/frontend/email/account_new_confirmation.html +++ b/app/code/Magento/Customer/view/frontend/email/account_new_confirmation.html @@ -4,9 +4,10 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Please confirm your %store_name account" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Please confirm your %store_name account" store_name=$store.frontend_name}} @--> <!--@vars { -"var this.getUrl($store, 'customer/account/confirm/', [_query:[id:$customer.id, key:$customer.confirmation, back_url:$back_url]])":"Account Confirmation URL", +"var store.frontend_name":"Store Name", +"var this.getUrl($store,'customer/account/confirm/',[_query:[id:$customer.id,key:$customer.confirmation,back_url:$back_url],_nosid:1])":"Account Confirmation URL", "var this.getUrl($store, 'customer/account/')":"Customer Account URL", "var customer.email":"Customer Email", "var customer.name":"Customer Name" diff --git a/app/code/Magento/Customer/view/frontend/email/account_new_confirmed.html b/app/code/Magento/Customer/view/frontend/email/account_new_confirmed.html index 931851b28ac21..34e1103fb2f9d 100644 --- a/app/code/Magento/Customer/view/frontend/email/account_new_confirmed.html +++ b/app/code/Magento/Customer/view/frontend/email/account_new_confirmed.html @@ -4,16 +4,18 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Welcome to %store_name" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Welcome to %store_name" store_name=$store.frontend_name}} @--> <!--@vars { -"var this.getUrl($store, 'customer/account/')":"Customer Account URL", +"var store.frontend_name":"Store Name", +"var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL", +"var this.getUrl($store,'customer/account/createPassword/',[_query:[id:$customer.id,token:$customer.rp_token],_nosid:1])":"Password Reset URL", "var customer.email":"Customer Email", "var customer.name":"Customer Name" } @--> {{template config_path="design/email/header_template"}} <p class="greeting">{{trans "%name," name=$customer.name}}</p> -<p>{{trans "Thank you for confirming your %store_name account." store_name=$store.getFrontendName()}}</p> +<p>{{trans "Thank you for confirming your %store_name account." store_name=$store.frontend_name}}</p> <p> {{trans 'To sign in to our site, use these credentials during checkout or on the <a href="%customer_url">My Account</a> page:' diff --git a/app/code/Magento/Customer/view/frontend/email/account_new_no_password.html b/app/code/Magento/Customer/view/frontend/email/account_new_no_password.html index 26e417d7da5a7..6d7d89067d8a2 100644 --- a/app/code/Magento/Customer/view/frontend/email/account_new_no_password.html +++ b/app/code/Magento/Customer/view/frontend/email/account_new_no_password.html @@ -4,16 +4,18 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Welcome to %store_name" store_name=$store.getFrontendName()}} @--> -<!--@vars { -"var this.getUrl($store, 'customer/account/')":"Customer Account URL", +<!--@subject {{trans "Welcome to %store_name" store_name=$store.frontend_name}} @--> +<!--@vars { +"var store.frontend_name":"Store Name", +"var this.getUrl($store, 'customer/account/')":"Customer Account URL", +"var this.getUrl($store,'customer/account/createPassword/',[_query:[id:$customer.id,token:$customer.rp_token],_nosid:1])":"Create Password URL", "var customer.email":"Customer Email", "var customer.name":"Customer Name" } @--> {{template config_path="design/email/header_template"}} <p class="greeting">{{trans "%name," name=$customer.name}}</p> -<p>{{trans "Welcome to %store_name." store_name=$store.getFrontendName()}}</p> +<p>{{trans "Welcome to %store_name." store_name=$store.frontend_name}}</p> <p> {{trans 'To sign in to our site and set a password, click on the <a href="%create_password_url">link</a>:' diff --git a/app/code/Magento/Customer/view/frontend/email/change_email.html b/app/code/Magento/Customer/view/frontend/email/change_email.html index f343433fe35e2..4853adf638066 100644 --- a/app/code/Magento/Customer/view/frontend/email/change_email.html +++ b/app/code/Magento/Customer/view/frontend/email/change_email.html @@ -4,19 +4,23 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Your %store_name email has been changed" store_name=$store.getFrontendName()}} @--> -<!--@vars {} @--> +<!--@subject {{trans "Your %store_name email has been changed" store_name=$store.frontend_name}} @--> +<!--@vars { +"var store.frontend_name":"Store Name", +"var store_email":"Store Email", +"var store_phone":"Store Phone" +} @--> {{template config_path="design/email/header_template"}} <p class="greeting">{{trans "Hello,"}}</p> <br> <p> - {{trans "We have received a request to change the following information associated with your account at %store_name: email." store_name=$store.getFrontendName()}} + {{trans "We have received a request to change the following information associated with your account at %store_name: email." store_name=$store.frontend_name}} {{trans 'If you have not authorized this action, please contact us immediately at <a href="mailto:%store_email">%store_email</a>' store_email=$store_email |raw}}{{depend store_phone}} {{trans 'or call us at <a href="tel:%store_phone">%store_phone</a>' store_phone=$store_phone |raw}}{{/depend}}. </p> <br> -<p>{{trans "Thanks,<br>%store_name" store_name=$store.getFrontendName() |raw}}</p> +<p>{{trans "Thanks,<br>%store_name" store_name=$store.frontend_name |raw}}</p> {{template config_path="design/email/footer_template"}} diff --git a/app/code/Magento/Customer/view/frontend/email/change_email_and_password.html b/app/code/Magento/Customer/view/frontend/email/change_email_and_password.html index 0876e75beacad..49867bdedc9e0 100644 --- a/app/code/Magento/Customer/view/frontend/email/change_email_and_password.html +++ b/app/code/Magento/Customer/view/frontend/email/change_email_and_password.html @@ -4,19 +4,23 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Your %store_name email and password has been changed" store_name=$store.getFrontendName()}} @--> -<!--@vars {} @--> +<!--@subject {{trans "Your %store_name email and password has been changed" store_name=$store.frontend_name}} @--> +<!--@vars { +"var store.frontend_name":"Store Name", +"var store_email":"Store Email", +"var store_phone":"Store Phone" +} @--> {{template config_path="design/email/header_template"}} <p class="greeting">{{trans "Hello,"}}</p> <br> <p> - {{trans "We have received a request to change the following information associated with your account at %store_name: email, password." store_name=$store.getFrontendName()}} + {{trans "We have received a request to change the following information associated with your account at %store_name: email, password." store_name=$store.frontend_name}} {{trans 'If you have not authorized this action, please contact us immediately at <a href="mailto:%store_email">%store_email</a>' store_email=$store_email |raw}}{{depend store_phone}} {{trans 'or call us at <a href="tel:%store_phone">%store_phone</a>' store_phone=$store_phone |raw}}{{/depend}}. </p> <br> -<p>{{trans "Thanks,<br>%store_name" store_name=$store.getFrontendName() |raw}}</p> +<p>{{trans "Thanks,<br>%store_name" store_name=$store.frontend_name |raw}}</p> {{template config_path="design/email/footer_template"}} diff --git a/app/code/Magento/Customer/view/frontend/email/password_new.html b/app/code/Magento/Customer/view/frontend/email/password_new.html index 1d2468374c6f3..975c8f7254976 100644 --- a/app/code/Magento/Customer/view/frontend/email/password_new.html +++ b/app/code/Magento/Customer/view/frontend/email/password_new.html @@ -4,9 +4,11 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Reset your %store_name password" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Reset your %store_name password" store_name=$store.frontend_name}} @--> <!--@vars { -"var this.getUrl(store, 'customer/account/')":"Customer Account URL", +"var store.frontend_name":"Store Name", +"var this.getUrl($store, 'customer/account/')":"Customer Account URL", +"var this.getUrl($store,'customer/account/createPassword',[_query:[id:$customer.id,token:$customer.rp_token],_nosid:1])":"Password Reset URL", "var customer.name":"Customer Name" } @--> {{template config_path="design/email/header_template"}} diff --git a/app/code/Magento/Customer/view/frontend/email/password_reset.html b/app/code/Magento/Customer/view/frontend/email/password_reset.html index bfa5330cbf5b0..79015117c2280 100644 --- a/app/code/Magento/Customer/view/frontend/email/password_reset.html +++ b/app/code/Magento/Customer/view/frontend/email/password_reset.html @@ -4,9 +4,12 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Your %store_name password has been changed" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Your %store_name password has been changed" store_name=$store.frontend_name}} @--> <!--@vars { -"var customer.name":"Customer Name" +"var customer.name":"Customer Name", +"var store.frontend_name":"Store Name", +"var store_email":"Store Email", +"var store_phone":"Store Phone" } @--> {{template config_path="design/email/header_template"}} @@ -14,11 +17,11 @@ <br> <p> - {{trans "We have received a request to change the following information associated with your account at %store_name: password." store_name=$store.getFrontendName()}} + {{trans "We have received a request to change the following information associated with your account at %store_name: password." store_name=$store.frontend_name}} {{trans 'If you have not authorized this action, please contact us immediately at <a href="mailto:%store_email">%store_email</a>' store_email=$store_email |raw}}{{depend store_phone}} {{trans 'or call us at <a href="tel:%store_phone">%store_phone</a>' store_phone=$store_phone |raw}}{{/depend}}. </p> <br> -<p>{{trans "Thanks,<br>%store_name" store_name=$store.getFrontendName() |raw}}</p> +<p>{{trans "Thanks,<br>%store_name" store_name=$store.frontend_name |raw}}</p> {{template config_path="design/email/footer_template"}} diff --git a/app/code/Magento/Customer/view/frontend/email/password_reset_confirmation.html b/app/code/Magento/Customer/view/frontend/email/password_reset_confirmation.html index 59e7f16adfd51..5dc0e2dfafee9 100644 --- a/app/code/Magento/Customer/view/frontend/email/password_reset_confirmation.html +++ b/app/code/Magento/Customer/view/frontend/email/password_reset_confirmation.html @@ -4,10 +4,11 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Reset your %store_name password" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Reset your %store_name password" store_name=$store.frontend_name}} @--> <!--@vars { +"var store.frontend_name":"Store Name", "var customer.name":"Customer Name", -"var this.getUrl($store, 'customer/account/createPassword/', [_query:[id:$customer.id, token:$customer.rp_token]])":"Reset Password URL" +"var this.getUrl($store,'customer/account/createPassword/',[_query:[token:$customer.rp_token],_nosid:1])":"Reset Password URL" } @--> {{template config_path="design/email/header_template"}} diff --git a/app/code/Magento/Customer/view/frontend/templates/form/edit.phtml b/app/code/Magento/Customer/view/frontend/templates/form/edit.phtml index 2718f03909be2..e2b6792439576 100644 --- a/app/code/Magento/Customer/view/frontend/templates/form/edit.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/form/edit.phtml @@ -67,7 +67,7 @@ </div> </div> </div> - <div class="field confirm password required" data-container="confirm-password"> + <div class="field confirmation password required" data-container="confirm-password"> <label class="label" for="password-confirmation"><span><?= $block->escapeHtml(__('Confirm New Password')) ?></span></label> <div class="control"> <input type="password" class="input-text" name="password_confirmation" id="password-confirmation" @@ -93,7 +93,7 @@ ], function($){ var dataForm = $('#form-validate'); var ignore = <?= /* @noEscape */ $_dob->isEnabled() ? '\'input[id$="full"]\'' : 'null' ?>; - + dataForm.mage('validation', { <?php if ($_dob->isEnabled()) : ?> errorPlacement: function(error, element) { diff --git a/app/code/Magento/Customer/view/frontend/templates/widget/telephone.phtml b/app/code/Magento/Customer/view/frontend/templates/widget/telephone.phtml index c444a15705909..4c3432233189b 100644 --- a/app/code/Magento/Customer/view/frontend/templates/widget/telephone.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/widget/telephone.phtml @@ -14,18 +14,14 @@ </span> </label> <div class="control"> - <?php - $_validationClass = $block->escapeHtmlAttr( - $this->helper(\Magento\Customer\Helper\Address::class) - ->getAttributeValidationClass('telephone') - ); - ?> <input type="text" name="telephone" id="telephone" value="<?= $block->escapeHtmlAttr($block->getTelephone()) ?>" title="<?= $block->escapeHtmlAttr(__('Phone Number')) ?>" - class="input-text <?= $_validationClass ?: '' ?>" + class="input-text <?= $block->escapeHtmlAttr( + $block->getAttributeValidationClass('telephone') + ) ?>" > </div> </div> diff --git a/app/code/Magento/Customer/view/frontend/web/js/customer-data.js b/app/code/Magento/Customer/view/frontend/web/js/customer-data.js index de3ff10bb057b..770ea47d754d3 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/customer-data.js +++ b/app/code/Magento/Customer/view/frontend/web/js/customer-data.js @@ -214,6 +214,11 @@ define([ this.reload(storageInvalidation.keys(), false); } } + + if (!_.isEmpty($.cookieStorage.get('section_data_clean'))) { + this.reload(sectionConfig.getSectionNames(), true); + $.cookieStorage.set('section_data_clean', ''); + } }, /** diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CreateCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CreateCustomerAddress.php index 388b6dc2ea943..37230df202a6f 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CreateCustomerAddress.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/CreateCustomerAddress.php @@ -10,9 +10,10 @@ use Magento\Customer\Api\AddressRepositoryInterface; use Magento\Customer\Api\Data\AddressInterface; use Magento\Customer\Api\Data\AddressInterfaceFactory; +use Magento\Directory\Helper\Data as DirectoryData; +use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; -use Magento\Framework\Api\DataObjectHelper; /** * Create customer address @@ -38,23 +39,30 @@ class CreateCustomerAddress * @var DataObjectHelper */ private $dataObjectHelper; + /** + * @var DirectoryData + */ + private $directoryData; /** * @param GetAllowedAddressAttributes $getAllowedAddressAttributes * @param AddressInterfaceFactory $addressFactory * @param AddressRepositoryInterface $addressRepository * @param DataObjectHelper $dataObjectHelper + * @param DirectoryData $directoryData */ public function __construct( GetAllowedAddressAttributes $getAllowedAddressAttributes, AddressInterfaceFactory $addressFactory, AddressRepositoryInterface $addressRepository, - DataObjectHelper $dataObjectHelper + DataObjectHelper $dataObjectHelper, + DirectoryData $directoryData ) { $this->getAllowedAddressAttributes = $getAllowedAddressAttributes; $this->addressFactory = $addressFactory; $this->addressRepository = $addressRepository; $this->dataObjectHelper = $dataObjectHelper; + $this->directoryData = $directoryData; } /** @@ -67,6 +75,10 @@ public function __construct( */ public function execute(int $customerId, array $data): AddressInterface { + // It is needed because AddressInterface has country_id field. + if (isset($data['country_code'])) { + $data['country_id'] = $data['country_code']; + } $this->validateData($data); /** @var AddressInterface $address */ @@ -98,6 +110,13 @@ public function validateData(array $addressData): void $attributes = $this->getAllowedAddressAttributes->execute(); $errorInput = []; + //Add error for empty postcode with country with no optional ZIP + if (!$this->directoryData->isZipCodeOptional($addressData['country_id']) + && (!isset($addressData['postcode']) || empty($addressData['postcode'])) + ) { + $errorInput[] = 'postcode'; + } + foreach ($attributes as $attributeName => $attributeInfo) { if ($attributeInfo->getIsRequired() && (!isset($addressData[$attributeName]) || empty($addressData[$attributeName])) diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/ExtractCustomerAddressData.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/ExtractCustomerAddressData.php index 8741bff7aa88d..5a302f4c3df27 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/ExtractCustomerAddressData.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/ExtractCustomerAddressData.php @@ -105,6 +105,7 @@ public function execute(AddressInterface $address): array foreach ($addressData[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES] as $attribute) { $isArray = false; if (is_array($attribute['value'])) { + // @ignoreCoverageStart $isArray = true; foreach ($attribute['value'] as $attributeValue) { if (is_array($attributeValue)) { @@ -116,6 +117,7 @@ public function execute(AddressInterface $address): array $customAttributes[$attribute['attribute_code']] = implode(',', $attribute['value']); continue; } + // @ignoreCoverageEnd } if ($isArray) { continue; @@ -127,6 +129,10 @@ public function execute(AddressInterface $address): array $addressData['customer_id'] = null; + if (isset($addressData['country_id'])) { + $addressData['country_code'] = $addressData['country_id']; + } + return $addressData; } } diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/UpdateCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/UpdateCustomerAddress.php index 65745a20bc8eb..26e53c7c3a0a8 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/Address/UpdateCustomerAddress.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/Address/UpdateCustomerAddress.php @@ -66,6 +66,9 @@ public function __construct( */ public function execute(AddressInterface $address, array $data): void { + if (isset($data['country_code'])) { + $data['country_id'] = $data['country_code']; + } $this->validateData($data); $filteredData = array_diff_key($data, array_flip($this->restrictedKeys)); diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/ChangeSubscriptionStatus.php b/app/code/Magento/CustomerGraphQl/Model/Customer/ChangeSubscriptionStatus.php deleted file mode 100644 index 1acb418e7bba6..0000000000000 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/ChangeSubscriptionStatus.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\CustomerGraphQl\Model\Customer; - -use Magento\Newsletter\Model\SubscriberFactory; - -/** - * Change subscription status. Subscribe OR unsubscribe if required - */ -class ChangeSubscriptionStatus -{ - /** - * @var SubscriberFactory - */ - private $subscriberFactory; - - /** - * @param SubscriberFactory $subscriberFactory - */ - public function __construct( - SubscriberFactory $subscriberFactory - ) { - $this->subscriberFactory = $subscriberFactory; - } - - /** - * Change subscription status. Subscribe OR unsubscribe if required - * - * @param int $customerId - * @param bool $subscriptionStatus - * @return void - */ - public function execute(int $customerId, bool $subscriptionStatus): void - { - $subscriber = $this->subscriberFactory->create()->loadByCustomerId($customerId); - - if ($subscriptionStatus === true && !$subscriber->isSubscribed()) { - $this->subscriberFactory->create()->subscribeCustomerById($customerId); - } elseif ($subscriptionStatus === false && $subscriber->isSubscribed()) { - $this->subscriberFactory->create()->unsubscribeCustomerById($customerId); - } - } -} diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/CreateCustomerAccount.php b/app/code/Magento/CustomerGraphQl/Model/Customer/CreateCustomerAccount.php index 969c205329f2b..a631b7ba86194 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/CreateCustomerAccount.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/CreateCustomerAccount.php @@ -14,6 +14,7 @@ use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\Reflection\DataObjectProcessor; +use Magento\Newsletter\Model\SubscriptionManagerInterface; use Magento\Store\Api\Data\StoreInterface; /** @@ -36,11 +37,6 @@ class CreateCustomerAccount */ private $accountManagement; - /** - * @var ChangeSubscriptionStatus - */ - private $changeSubscriptionStatus; - /** * @var ValidateCustomerData */ @@ -51,30 +47,35 @@ class CreateCustomerAccount */ private $dataObjectProcessor; + /** + * @var SubscriptionManagerInterface + */ + private $subscriptionManager; + /** * CreateCustomerAccount constructor. * * @param DataObjectHelper $dataObjectHelper * @param CustomerInterfaceFactory $customerFactory * @param AccountManagementInterface $accountManagement - * @param ChangeSubscriptionStatus $changeSubscriptionStatus * @param DataObjectProcessor $dataObjectProcessor * @param ValidateCustomerData $validateCustomerData + * @param SubscriptionManagerInterface $subscriptionManager */ public function __construct( DataObjectHelper $dataObjectHelper, CustomerInterfaceFactory $customerFactory, AccountManagementInterface $accountManagement, - ChangeSubscriptionStatus $changeSubscriptionStatus, DataObjectProcessor $dataObjectProcessor, - ValidateCustomerData $validateCustomerData + ValidateCustomerData $validateCustomerData, + SubscriptionManagerInterface $subscriptionManager ) { $this->dataObjectHelper = $dataObjectHelper; $this->customerFactory = $customerFactory; $this->accountManagement = $accountManagement; - $this->changeSubscriptionStatus = $changeSubscriptionStatus; $this->validateCustomerData = $validateCustomerData; $this->dataObjectProcessor = $dataObjectProcessor; + $this->subscriptionManager = $subscriptionManager; } /** @@ -94,7 +95,11 @@ public function execute(array $data, StoreInterface $store): CustomerInterface } if (isset($data['is_subscribed'])) { - $this->changeSubscriptionStatus->execute((int)$customer->getId(), (bool)$data['is_subscribed']); + if ((bool)$data['is_subscribed']) { + $this->subscriptionManager->subscribeCustomer((int)$customer->getId(), (int)$store->getId()); + } else { + $this->subscriptionManager->unsubscribeCustomer((int)$customer->getId(), (int)$store->getId()); + } } return $customer; } diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerAccount.php b/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerAccount.php index f7bf26513dc2e..d82b8c6f941fa 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerAccount.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/UpdateCustomerAccount.php @@ -14,6 +14,7 @@ use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\Api\DataObjectHelper; +use Magento\Newsletter\Model\SubscriptionManagerInterface; use Magento\Store\Api\Data\StoreInterface; /** @@ -38,11 +39,6 @@ class UpdateCustomerAccount */ private $dataObjectHelper; - /** - * @var ChangeSubscriptionStatus - */ - private $changeSubscriptionStatus; - /** * @var ValidateCustomerData */ @@ -53,28 +49,33 @@ class UpdateCustomerAccount */ private $restrictedKeys; + /** + * @var SubscriptionManagerInterface + */ + private $subscriptionManager; + /** * @param SaveCustomer $saveCustomer * @param CheckCustomerPassword $checkCustomerPassword * @param DataObjectHelper $dataObjectHelper - * @param ChangeSubscriptionStatus $changeSubscriptionStatus * @param ValidateCustomerData $validateCustomerData + * @param SubscriptionManagerInterface $subscriptionManager * @param array $restrictedKeys */ public function __construct( SaveCustomer $saveCustomer, CheckCustomerPassword $checkCustomerPassword, DataObjectHelper $dataObjectHelper, - ChangeSubscriptionStatus $changeSubscriptionStatus, ValidateCustomerData $validateCustomerData, + SubscriptionManagerInterface $subscriptionManager, array $restrictedKeys = [] ) { $this->saveCustomer = $saveCustomer; $this->checkCustomerPassword = $checkCustomerPassword; $this->dataObjectHelper = $dataObjectHelper; $this->restrictedKeys = $restrictedKeys; - $this->changeSubscriptionStatus = $changeSubscriptionStatus; $this->validateCustomerData = $validateCustomerData; + $this->subscriptionManager = $subscriptionManager; } /** @@ -112,7 +113,11 @@ public function execute(CustomerInterface $customer, array $data, StoreInterface $this->saveCustomer->execute($customer); if (isset($data['is_subscribed'])) { - $this->changeSubscriptionStatus->execute((int)$customer->getId(), (bool)$data['is_subscribed']); + if ((bool)$data['is_subscribed']) { + $this->subscriptionManager->subscribeCustomer((int)$customer->getId(), (int)$store->getId()); + } else { + $this->subscriptionManager->unsubscribeCustomer((int)$customer->getId(), (int)$store->getId()); + } } } } diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData.php b/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData.php index 794cb0048592d..3861ce324ea7d 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/ValidateCustomerData.php @@ -8,9 +8,10 @@ namespace Magento\CustomerGraphQl\Model\Customer; use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Validator\EmailAddress as EmailAddressValidator; /** - * Class ValidateCustomerData + * Customer data validation used during customer account creation and updating */ class ValidateCustomerData { @@ -21,14 +22,23 @@ class ValidateCustomerData */ private $getAllowedCustomerAttributes; + /** + * @var EmailAddressValidator + */ + private $emailAddressValidator; + /** * ValidateCustomerData constructor. * * @param GetAllowedCustomerAttributes $getAllowedCustomerAttributes + * @param EmailAddressValidator $emailAddressValidator */ - public function __construct(GetAllowedCustomerAttributes $getAllowedCustomerAttributes) - { + public function __construct( + GetAllowedCustomerAttributes $getAllowedCustomerAttributes, + EmailAddressValidator $emailAddressValidator + ) { $this->getAllowedCustomerAttributes = $getAllowedCustomerAttributes; + $this->emailAddressValidator = $emailAddressValidator; } /** @@ -59,5 +69,11 @@ public function execute(array $customerData): void __('Required parameters are missing: %1', [implode(', ', $errorInput)]) ); } + + if (isset($customerData['email']) && !$this->emailAddressValidator->isValid($customerData['email'])) { + throw new GraphQlInputException( + __('"%1" is not a valid email address.', $customerData['email']) + ); + } } } diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/DeleteCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/DeleteCustomerAddress.php index da588b98b5dbc..c26b3710ef561 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/DeleteCustomerAddress.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/DeleteCustomerAddress.php @@ -58,10 +58,6 @@ public function resolve( throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); } - if (empty($args['id'])) { - throw new GraphQlInputException(__('Address "id" value should be specified')); - } - $address = $this->getCustomerAddress->execute((int)$args['id'], $context->getUserId()); $this->deleteCustomerAddress->execute($address); return true; diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php index 4e49891b5870a..3e69723d4e771 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsSubscribed.php @@ -7,6 +7,7 @@ namespace Magento\CustomerGraphQl\Model\Resolver; +use Magento\Customer\Api\Data\CustomerInterface; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\GraphQl\Config\Element\Field; @@ -45,10 +46,12 @@ public function resolve( if (!isset($value['model'])) { throw new LocalizedException(__('"model" value should be specified')); } - /** @var Customer $customer */ + /** @var CustomerInterface $customer */ $customer = $value['model']; + $customerId = (int)$customer->getId(); + $websiteId = (int)$customer->getWebsiteId(); + $status = $this->subscriberFactory->create()->loadByCustomer($customerId, $websiteId)->isSubscribed(); - $status = $this->subscriberFactory->create()->loadByCustomerId((int)$customer->getId())->isSubscribed(); return (bool)$status; } } diff --git a/app/code/Magento/CustomerGraphQl/composer.json b/app/code/Magento/CustomerGraphQl/composer.json index 911624da8fe57..70a98f728b696 100644 --- a/app/code/Magento/CustomerGraphQl/composer.json +++ b/app/code/Magento/CustomerGraphQl/composer.json @@ -4,7 +4,6 @@ "type": "magento2-module", "require": { "php": "~7.1.3||~7.2.0||~7.3.0", - "magento/module-customer": "*", "magento/module-authorization": "*", "magento/module-customer": "*", "magento/module-eav": "*", @@ -12,7 +11,8 @@ "magento/module-newsletter": "*", "magento/module-integration": "*", "magento/module-store": "*", - "magento/framework": "*" + "magento/framework": "*", + "magento/module-directory": "*" }, "license": [ "OSL-3.0", diff --git a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls index 793ff0954ee94..9aa1fdaa841e4 100644 --- a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls @@ -28,7 +28,8 @@ input CustomerAddressInput { city: String @doc(description: "The city or town") region: CustomerAddressRegionInput @doc(description: "An object containing the region name, region code, and region ID") postcode: String @doc(description: "The customer's ZIP or postal code") - country_id: CountryCodeEnum @doc(description: "The customer's country") + country_id: CountryCodeEnum @doc(description: "Deprecated: use `country_code` instead.") + country_code: CountryCodeEnum @doc(description: "The customer's country") default_shipping: Boolean @doc(description: "Indicates whether the address is the default shipping address") default_billing: Boolean @doc(description: "Indicates whether the address is the default billing address") fax: String @doc(description: "The fax number") @@ -60,7 +61,7 @@ input CustomerInput { middlename: String @doc(description: "The customer's middle name") lastname: String @doc(description: "The customer's family name") suffix: String @doc(description: "A value such as Sr., Jr., or III") - email: String! @doc(description: "The customer's email address. Required") + email: String @doc(description: "The customer's email address. Required for customer creation") dob: String @doc(description: "Deprecated: Use `date_of_birth` instead") date_of_birth: String @doc(description: "The customer's date of birth") taxvat: String @doc(description: "The customer's Tax/VAT number (for corporate customers)") @@ -102,7 +103,8 @@ type CustomerAddress @doc(description: "CustomerAddress contains detailed inform customer_id: Int @doc(description: "The customer ID") @deprecated(reason: "customer_id is not needed as part of CustomerAddress, address ID (id) is unique identifier for the addresses.") region: CustomerAddressRegion @doc(description: "An object containing the region name, region code, and region ID") region_id: Int @deprecated(reason: "Region ID is excessive on storefront and region code should suffice for all scenarios") - country_id: String @doc(description: "The customer's country") + country_id: String @doc(description: "The customer's country") @deprecated(reason: "Use `country_code` instead.") + country_code: CountryCodeEnum @doc(description: "The customer's country") street: [String] @doc(description: "An array of strings that define the street number and name") company: String @doc(description: "The customer's company") telephone: String @doc(description: "The telephone number") diff --git a/app/code/Magento/CustomerImportExport/registration.php b/app/code/Magento/CustomerImportExport/registration.php index 1cfb0ed8899ad..68a23fe6be57c 100644 --- a/app/code/Magento/CustomerImportExport/registration.php +++ b/app/code/Magento/CustomerImportExport/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CustomerImportExport', __DIR__); diff --git a/app/code/Magento/Deploy/registration.php b/app/code/Magento/Deploy/registration.php index 138d03fd303cf..6cd92601dff88 100644 --- a/app/code/Magento/Deploy/registration.php +++ b/app/code/Magento/Deploy/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Deploy', __DIR__); diff --git a/app/code/Magento/Developer/Console/Command/TablesWhitelistGenerateCommand.php b/app/code/Magento/Developer/Console/Command/TablesWhitelistGenerateCommand.php index 2155efa017093..b35a3e68b0f20 100644 --- a/app/code/Magento/Developer/Console/Command/TablesWhitelistGenerateCommand.php +++ b/app/code/Magento/Developer/Console/Command/TablesWhitelistGenerateCommand.php @@ -10,6 +10,7 @@ use Magento\Developer\Model\Setup\Declaration\Schema\WhitelistGenerator; use Magento\Framework\Config\FileResolverByModule; use Magento\Framework\Exception\ConfigurationMismatchException; +use Magento\Framework\Console\Cli; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -80,11 +81,12 @@ protected function execute(InputInterface $input, OutputInterface $output) : int $this->whitelistGenerator->generate($moduleName); } catch (ConfigurationMismatchException $e) { $output->writeln($e->getMessage()); - return \Magento\Framework\Console\Cli::RETURN_FAILURE; + return Cli::RETURN_FAILURE; } catch (\Exception $e) { - return \Magento\Framework\Console\Cli::RETURN_FAILURE; + $output->writeln($e->getMessage()); + return Cli::RETURN_FAILURE; } - return \Magento\Framework\Console\Cli::RETURN_SUCCESS; + return Cli::RETURN_SUCCESS; } } diff --git a/app/code/Magento/Developer/Test/Unit/Console/Command/TablesWhitelistGenerateCommandTest.php b/app/code/Magento/Developer/Test/Unit/Console/Command/TablesWhitelistGenerateCommandTest.php new file mode 100644 index 0000000000000..8e547332c0b44 --- /dev/null +++ b/app/code/Magento/Developer/Test/Unit/Console/Command/TablesWhitelistGenerateCommandTest.php @@ -0,0 +1,158 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Developer\Test\Unit\Console\Command; + +use Magento\Developer\Console\Command\TablesWhitelistGenerateCommand as GenerateCommand; +use Magento\Developer\Model\Setup\Declaration\Schema\WhitelistGenerator; +use Magento\Framework\Console\Cli; +use Magento\Framework\Exception\ConfigurationMismatchException as ConfigException; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Tester\CommandTester; + +/** + * Class TablesWhitelistGenerateCommandTest + * + * @package Magento\Developer\Test\Unit\Console\Command + */ +class TablesWhitelistGenerateCommandTest extends TestCase +{ + // Exception Messages! + const CONFIG_EXCEPTION_MESSAGE = 'Configuration Exception Message'; + const EXCEPTION_MESSAGE = 'General Exception Message'; + + /** @var WhitelistGenerator|MockObject $whitelistGenerator */ + private $whitelistGenerator; + + /** @var GenerateCommand $instance */ + private $instance; + + protected function setUp() + { + $this->whitelistGenerator = $this->getMockBuilder(WhitelistGenerator::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->instance = new GenerateCommand($this->whitelistGenerator); + } + + /** + * Test case for success scenario + * + * @param string $arguments + * @param string $expected + * + * @dataProvider successDataProvider + */ + public function testCommandSuccess(string $arguments, string $expected) + { + $this->whitelistGenerator->expects($this->once()) + ->method('generate') + ->with($arguments); + + $commandTest = $this->execute($arguments); + $this->assertEquals($expected, $commandTest->getStatusCode()); + $this->assertEquals('', $commandTest->getDisplay()); + } + + /** + * Test case for failure scenario + * + * @param string $arguments + * @param string $expected + * @param \Exception|ConfigException $exception + * @param string $output + * + * @dataProvider failureDataProvider + */ + public function testCommandFailure(string $arguments, string $expected, $exception, string $output) + { + $this->whitelistGenerator->expects($this->once()) + ->method('generate') + ->with($arguments) + ->willReturnCallback( + function () use ($exception) { + throw $exception; + } + ); + + $commandTest = $this->execute($arguments); + $this->assertEquals($expected, $commandTest->getStatusCode()); + $this->assertEquals($output . PHP_EOL, $commandTest->getDisplay()); + } + + /** + * Data provider for success test case + * + * @return array + */ + public function successDataProvider() + { + return [ + [ + 'all', + Cli::RETURN_SUCCESS, + + ], + [ + 'Module_Name', + Cli::RETURN_SUCCESS + ] + ]; + } + + /** + * Data provider for failure test case + * + * @return array + */ + public function failureDataProvider() + { + return [ + [ + 'all', + Cli::RETURN_FAILURE, + new ConfigException(__('Configuration Exception Message')), + self::CONFIG_EXCEPTION_MESSAGE + ], + [ + 'Module_Name', + Cli::RETURN_FAILURE, + new ConfigException(__('Configuration Exception Message')), + self::CONFIG_EXCEPTION_MESSAGE + ], + [ + 'all', + Cli::RETURN_FAILURE, + new \Exception(self::EXCEPTION_MESSAGE), + self::EXCEPTION_MESSAGE + ], + [ + 'Module_Name', + Cli::RETURN_FAILURE, + new \Exception(self::EXCEPTION_MESSAGE), + self::EXCEPTION_MESSAGE + ] + ]; + } + + /** + * Execute command test class for symphony + * + * @param string $arguments + * + * @return CommandTester + */ + private function execute(string $arguments) + { + $commandTest = new CommandTester($this->instance); + $commandTest->execute(['--' . GenerateCommand::MODULE_NAME_KEY => $arguments]); + + return $commandTest; + } +} diff --git a/app/code/Magento/Developer/etc/adminhtml/system.xml b/app/code/Magento/Developer/etc/adminhtml/system.xml index c64abd6eae725..197dc6f981acf 100644 --- a/app/code/Magento/Developer/etc/adminhtml/system.xml +++ b/app/code/Magento/Developer/etc/adminhtml/system.xml @@ -11,7 +11,7 @@ <label>Frontend Development Workflow</label> <field id="type" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> <label>Workflow type</label> - <comment>Not available in production mode</comment> + <comment>Not available in production mode.</comment> <source_model>Magento\Developer\Model\Config\Source\WorkflowType</source_model> <frontend_model>Magento\Developer\Block\Adminhtml\System\Config\WorkflowType</frontend_model> <backend_model>Magento\Developer\Model\Config\Backend\WorkflowType</backend_model> diff --git a/app/code/Magento/Developer/registration.php b/app/code/Magento/Developer/registration.php index 1d3fb6148ce1f..e99b1ed24b104 100644 --- a/app/code/Magento/Developer/registration.php +++ b/app/code/Magento/Developer/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Developer', __DIR__); diff --git a/app/code/Magento/Dhl/Model/Carrier.php b/app/code/Magento/Dhl/Model/Carrier.php index 0890466e8a40f..ad76f5070b35b 100644 --- a/app/code/Magento/Dhl/Model/Carrier.php +++ b/app/code/Magento/Dhl/Model/Carrier.php @@ -940,7 +940,7 @@ protected function _getDimension($dimension, $configWeightUnit = false) ); } - return sprintf('%.3f', $dimension); + return round($dimension, 3); } /** diff --git a/app/code/Magento/Dhl/Test/Unit/Model/_files/countries.xml b/app/code/Magento/Dhl/Test/Unit/Model/_files/countries.xml index 792465ce45942..d71bb69c99c9b 100644 --- a/app/code/Magento/Dhl/Test/Unit/Model/_files/countries.xml +++ b/app/code/Magento/Dhl/Test/Unit/Model/_files/countries.xml @@ -216,7 +216,7 @@ <name>Botswana</name> </BW> <BY> - <currency>BYR</currency> + <currency>BYN</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> <region>AP</region> diff --git a/app/code/Magento/Dhl/etc/adminhtml/system.xml b/app/code/Magento/Dhl/etc/adminhtml/system.xml index ea3da2ca031a5..93a16821e385d 100644 --- a/app/code/Magento/Dhl/etc/adminhtml/system.xml +++ b/app/code/Magento/Dhl/etc/adminhtml/system.xml @@ -88,7 +88,7 @@ </field> <field id="ready_time" translate="label comment" type="text" sortOrder="180" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Ready time</label> - <comment>Package ready time after order submission (in hours)</comment> + <comment>Package ready time after order submission (in hours).</comment> </field> <field id="specificerrmsg" translate="label" type="textarea" sortOrder="800" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Displayed Error Message</label> diff --git a/app/code/Magento/Dhl/etc/config.xml b/app/code/Magento/Dhl/etc/config.xml index b46152fb0ecad..3408447e70650 100644 --- a/app/code/Magento/Dhl/etc/config.xml +++ b/app/code/Magento/Dhl/etc/config.xml @@ -32,7 +32,7 @@ <specificerrmsg>This shipping method is currently unavailable. If you would like to ship using this shipping method, please contact us.</specificerrmsg> <divide_order_weight>1</divide_order_weight> <unit_of_measure>K</unit_of_measure> - <size>R</size> + <size>0</size> <handling_type>F</handling_type> <handling_action>O</handling_action> <shipment_days>Mon,Tue,Wed,Thu,Fri</shipment_days> diff --git a/app/code/Magento/Dhl/etc/countries.xml b/app/code/Magento/Dhl/etc/countries.xml index 792465ce45942..d71bb69c99c9b 100644 --- a/app/code/Magento/Dhl/etc/countries.xml +++ b/app/code/Magento/Dhl/etc/countries.xml @@ -216,7 +216,7 @@ <name>Botswana</name> </BW> <BY> - <currency>BYR</currency> + <currency>BYN</currency> <weight_unit>KG</weight_unit> <measure_unit>CM</measure_unit> <region>AP</region> diff --git a/app/code/Magento/Dhl/registration.php b/app/code/Magento/Dhl/registration.php index 4d69e96f1ff8a..a8a0b58a5c6f3 100644 --- a/app/code/Magento/Dhl/registration.php +++ b/app/code/Magento/Dhl/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Dhl', __DIR__); diff --git a/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php b/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php index 8b9e0b1be7df2..8e27b9905ed6f 100644 --- a/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php +++ b/app/code/Magento/Directory/Model/Currency/Import/CurrencyConverterApi.php @@ -7,43 +7,57 @@ namespace Magento\Directory\Model\Currency\Import; +use Magento\Directory\Model\CurrencyFactory; +use Magento\Store\Model\ScopeInterface; +use Magento\Framework\App\Config\ScopeConfigInterface as ScopeConfig; +use Magento\Framework\HTTP\ZendClient; +use Magento\Framework\HTTP\ZendClientFactory; +use Exception; + /** - * Currency rate import model (From http://free.currencyconverterapi.com/) - * - * Class \Magento\Directory\Model\Currency\Import\CurrencyConverterApi + * Currency rate converter (free.currconv.com). */ class CurrencyConverterApi extends AbstractImport { /** * @var string */ - const CURRENCY_CONVERTER_URL = 'http://free.currencyconverterapi.com/api/v3/convert?q={{CURRENCY_FROM}}_{{CURRENCY_TO}}&compact=ultra&apiKey={{API_KEY}}'; //@codingStandardsIgnoreLine + public const CURRENCY_CONVERTER_URL = 'https://free.currconv.com/api/v7/convert?apiKey={{ACCESS_KEY}}' + . '&q={{CURRENCY_RATES}}&compact=ultra'; /** * Http Client Factory * - * @var \Magento\Framework\HTTP\ZendClientFactory + * @var ZendClientFactory */ private $httpClientFactory; /** * Core scope config * - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfig */ private $scopeConfig; /** - * Initialize dependencies - * - * @param \Magento\Directory\Model\CurrencyFactory $currencyFactory - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param \Magento\Framework\HTTP\ZendClientFactory $httpClientFactory + * @var string + */ + private $currencyConverterServiceHost = ''; + + /** + * @var string + */ + private $serviceUrl = ''; + + /** + * @param CurrencyFactory $currencyFactory + * @param ScopeConfig $scopeConfig + * @param ZendClientFactory $httpClientFactory */ public function __construct( - \Magento\Directory\Model\CurrencyFactory $currencyFactory, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\Framework\HTTP\ZendClientFactory $httpClientFactory + CurrencyFactory $currencyFactory, + ScopeConfig $scopeConfig, + ZendClientFactory $httpClientFactory ) { parent::__construct($currencyFactory); $this->scopeConfig = $scopeConfig; @@ -77,30 +91,39 @@ public function fetchRates() * @param array $currenciesTo * @return array */ - private function convertBatch($data, $currencyFrom, $currenciesTo) + private function convertBatch(array $data, string $currencyFrom, array $currenciesTo): array { - $apiKey = $this->scopeConfig->getValue( - 'currency/currencyconverterapi/api_key', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ); - if (!$apiKey) { - $this->_messages[] = __('No API Key was specified.'); + $url = $this->getServiceURL($currencyFrom, $currenciesTo); + if (empty($url)) { + $data[$currencyFrom] = $this->makeEmptyResponse($currenciesTo); return $data; } + // phpcs:ignore Magento2.Functions.DiscouragedFunction + set_time_limit(0); + try { + $response = $this->getServiceResponse($url); + } finally { + ini_restore('max_execution_time'); + } + + if (!$this->validateResponse($response)) { + $data[$currencyFrom] = $this->makeEmptyResponse($currenciesTo); + return $data; + } + foreach ($currenciesTo as $to) { - //phpcs:ignore Magento2.Functions.DiscouragedFunction - set_time_limit(0); - try { - $url = str_replace('{{CURRENCY_FROM}}', $currencyFrom, self::CURRENCY_CONVERTER_URL); - $url = str_replace('{{CURRENCY_TO}}', $to, $url); - $url = str_replace('{{API_KEY}}', $apiKey, $url); - if ($currencyFrom == $to) { - $data[$currencyFrom][$to] = $this->_numberFormat(1); + if ($currencyFrom === $to) { + $data[$currencyFrom][$to] = $this->_numberFormat(1); + } else { + if (!isset($response[$currencyFrom . '_' . $to])) { + $serviceHost = $this->getServiceHost($url); + $this->_messages[] = __('We can\'t retrieve a rate from %1 for %2.', $serviceHost, $to); + $data[$currencyFrom][$to] = null; } else { - $data[$currencyFrom][$to] = $this->getCurrencyRate($currencyFrom, $to, $url); + $data[$currencyFrom][$to] = $this->_numberFormat( + (double)$response[$currencyFrom . '_' . $to] + ); } - } finally { - ini_restore('max_execution_time'); } } @@ -108,33 +131,52 @@ private function convertBatch($data, $currencyFrom, $currenciesTo) } /** - * Get currency rate from api + * Get currency converter service host. * - * @param string $currencyFrom - * @param string $to * @param string $url - * @return double + * @return string */ - private function getCurrencyRate($currencyFrom, $to, $url) + private function getServiceHost(string $url): string { - $rate = null; - $response = $this->getServiceResponse($url); - if (empty($response)) { - $this->_messages[] = __('We can\'t retrieve a rate from %1 for %2.', $url, $to); - $rate = null; - } else { - if (isset($response['error']) && $response['error']) { - if (!in_array($response['error'], $this->_messages)) { - $this->_messages[] = $response['error']; - } - $rate = null; - } else { - $rate = $this->_numberFormat( - (double)$response[$currencyFrom . '_' . $to] - ); + if (!$this->currencyConverterServiceHost) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $this->currencyConverterServiceHost = parse_url($url, PHP_URL_SCHEME) . '://' + // phpcs:ignore Magento2.Functions.DiscouragedFunction + . parse_url($url, PHP_URL_HOST); + } + return $this->currencyConverterServiceHost; + } + + /** + * Return service URL. + * + * @param string $currencyFrom + * @param array $currenciesTo + * @return string + */ + private function getServiceURL(string $currencyFrom, array $currenciesTo): string + { + if (!$this->serviceUrl) { + // Get access key + $accessKey = $this->scopeConfig + ->getValue('currency/currencyconverterapi/api_key', ScopeInterface::SCOPE_STORE); + if (empty($accessKey)) { + $this->_messages[] = __('No API Key was specified or an invalid API Key was specified.'); + return ''; } + // Get currency rates request + $currencyQueryParts = []; + foreach ($currenciesTo as $currencyTo) { + $currencyQueryParts[] = sprintf('%s_%s', $currencyFrom, $currencyTo); + } + $currencyRates = implode(',', $currencyQueryParts); + $this->serviceUrl = str_replace( + ['{{ACCESS_KEY}}', '{{CURRENCY_RATES}}'], + [$accessKey, $currencyRates], + self::CURRENCY_CONVERTER_URL + ); } - return $rate; + return $this->serviceUrl; } /** @@ -144,9 +186,9 @@ private function getCurrencyRate($currencyFrom, $to, $url) * @param int $retry * @return array */ - private function getServiceResponse($url, $retry = 0) + private function getServiceResponse($url, $retry = 0): array { - /** @var \Magento\Framework\HTTP\ZendClient $httpClient */ + /** @var ZendClient $httpClient */ $httpClient = $this->httpClientFactory->create(); $response = []; @@ -157,15 +199,15 @@ private function getServiceResponse($url, $retry = 0) [ 'timeout' => $this->scopeConfig->getValue( 'currency/currencyconverterapi/timeout', - \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ScopeInterface::SCOPE_STORE ), ] )->request( 'GET' )->getBody(); - $response = json_decode($jsonResponse, true); - } catch (\Exception $e) { + $response = json_decode($jsonResponse, true) ?: []; + } catch (Exception $e) { if ($retry == 0) { $response = $this->getServiceResponse($url, 1); } @@ -173,6 +215,32 @@ private function getServiceResponse($url, $retry = 0) return $response; } + /** + * Validate rates response. + * + * @param array $response + * @return bool + */ + private function validateResponse(array $response): bool + { + if (!isset($response['error'])) { + return true; + } + $this->_messages[] = $response['error'] ?: __('Currency rates can\'t be retrieved.'); + return false; + } + + /** + * Make empty rates for provided currencies. + * + * @param array $currenciesTo + * @return array + */ + private function makeEmptyResponse(array $currenciesTo): array + { + return array_fill_keys($currenciesTo, null); + } + /** * @inheritdoc */ diff --git a/app/code/Magento/Directory/Model/Currency/Import/FixerIo.php b/app/code/Magento/Directory/Model/Currency/Import/FixerIo.php index af49d6daaf379..29cdbabb9b993 100644 --- a/app/code/Magento/Directory/Model/Currency/Import/FixerIo.php +++ b/app/code/Magento/Directory/Model/Currency/Import/FixerIo.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Directory\Model\Currency\Import; use Magento\Store\Model\ScopeInterface; @@ -32,6 +34,11 @@ class FixerIo extends AbstractImport */ private $scopeConfig; + /** + * @var string + */ + private $currencyConverterServiceHost = ''; + /** * Initialize dependencies * @@ -73,6 +80,7 @@ public function fetchRates() */ protected function _convert($currencyFrom, $currencyTo) { + return 1; } /** @@ -98,7 +106,7 @@ private function convertBatch(array $data, string $currencyFrom, array $currenci [$accessKey, $currencyFrom, $currenciesStr], self::CURRENCY_CONVERTER_URL ); - + // phpcs:ignore Magento2.Functions.DiscouragedFunction set_time_limit(0); try { $response = $this->getServiceResponse($url); @@ -116,7 +124,8 @@ private function convertBatch(array $data, string $currencyFrom, array $currenci $data[$currencyFrom][$currencyTo] = $this->_numberFormat(1); } else { if (empty($response['rates'][$currencyTo])) { - $this->_messages[] = __('We can\'t retrieve a rate from %1 for %2.', $url, $currencyTo); + $serviceHost = $this->getServiceHost($url); + $this->_messages[] = __('We can\'t retrieve a rate from %1 for %2.', $serviceHost, $currencyTo); $data[$currencyFrom][$currencyTo] = null; } else { $data[$currencyFrom][$currencyTo] = $this->_numberFormat( @@ -198,4 +207,21 @@ private function makeEmptyResponse(array $currenciesTo): array { return array_fill_keys($currenciesTo, null); } + + /** + * Get currency converter service host. + * + * @param string $url + * @return string + */ + private function getServiceHost(string $url): string + { + if (!$this->currencyConverterServiceHost) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $this->currencyConverterServiceHost = parse_url($url, PHP_URL_SCHEME) . '://' + // phpcs:ignore Magento2.Functions.DiscouragedFunction + . parse_url($url, PHP_URL_HOST); + } + return $this->currencyConverterServiceHost; + } } diff --git a/app/code/Magento/Directory/Model/ResourceModel/Currency.php b/app/code/Magento/Directory/Model/ResourceModel/Currency.php index 5339b0c9eb5bd..5db880c00343a 100644 --- a/app/code/Magento/Directory/Model/ResourceModel/Currency.php +++ b/app/code/Magento/Directory/Model/ResourceModel/Currency.php @@ -138,7 +138,7 @@ public function getAnyRate($currencyFrom, $currencyTo) */ public function saveRates($rates) { - if (is_array($rates) && sizeof($rates) > 0) { + if (is_array($rates) && count($rates) > 0) { $connection = $this->getConnection(); $data = []; foreach ($rates as $currencyCode => $rate) { @@ -176,7 +176,7 @@ public function getConfigCurrencies($model, $path) $result = []; $rowSet = $connection->fetchAll($select, $bind); foreach ($rowSet as $row) { - $result = array_merge($result, explode(',', $row['value'])); + $result[] = explode(',', $row['value']); } sort($result); diff --git a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForColombia.php b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForColombia.php new file mode 100644 index 0000000000000..38a9759828588 --- /dev/null +++ b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForColombia.php @@ -0,0 +1,115 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Directory\Setup\Patch\Data; + +use Magento\Directory\Setup\DataInstaller; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; + +/** + * Class AddDataForColombia + */ +class AddDataForColombia implements DataPatchInterface +{ + /** + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** + * @var \Magento\Directory\Setup\DataInstallerFactory + */ + private $dataInstallerFactory; + + /** + * @param ModuleDataSetupInterface $moduleDataSetup + * @param \Magento\Directory\Setup\DataInstallerFactory $dataInstallerFactory + */ + public function __construct( + ModuleDataSetupInterface $moduleDataSetup, + \Magento\Directory\Setup\DataInstallerFactory $dataInstallerFactory + ) { + $this->moduleDataSetup = $moduleDataSetup; + $this->dataInstallerFactory = $dataInstallerFactory; + } + + /** + * @inheritdoc + */ + public function apply() + { + /** @var DataInstaller $dataInstaller */ + $dataInstaller = $this->dataInstallerFactory->create(); + $dataInstaller->addCountryRegions( + $this->moduleDataSetup->getConnection(), + $this->getDataForColombia() + ); + } + + /** + * Colombia states data. + * + * @return array + */ + private function getDataForColombia() + { + return [ + ['CO', 'CO-AMA', 'Amazonas'], + ['CO', 'CO-ANT', 'Antioquia'], + ['CO', 'CO-ARA', 'Arauca'], + ['CO', 'CO-ATL', 'Atlántico'], + ['CO', 'CO-BOL', 'Bolívar'], + ['CO', 'CO-BOY', 'Boyacá'], + ['CO', 'CO-CAL', 'Caldas'], + ['CO', 'CO-CAQ', 'Caquetá'], + ['CO', 'CO-CAS', 'Casanare'], + ['CO', 'CO-CAU', 'Cauca'], + ['CO', 'CO-CES', 'Cesar'], + ['CO', 'CO-CHO', 'Chocó'], + ['CO', 'CO-COR', 'Córdoba'], + ['CO', 'CO-CUN', 'Cundinamarca'], + ['CO', 'CO-GUA', 'Guainía'], + ['CO', 'CO-GUV', 'Guaviare'], + ['CO', 'CO-HUL', 'Huila'], + ['CO', 'CO-LAG', 'La Guajira'], + ['CO', 'CO-MAG', 'Magdalena'], + ['CO', 'CO-MET', 'Meta'], + ['CO', 'CO-NAR', 'Nariño'], + ['CO', 'CO-NSA', 'Norte de Santander'], + ['CO', 'CO-PUT', 'Putumayo'], + ['CO', 'CO-QUI', 'Quindío'], + ['CO', 'CO-RIS', 'Risaralda'], + ['CO', 'CO-SAP', 'San Andrés y Providencia'], + ['CO', 'CO-SAN', 'Santander'], + ['CO', 'CO-SUC', 'Sucre'], + ['CO', 'CO-TOL', 'Tolima'], + ['CO', 'CO-VAC', 'Valle del Cauca'], + ['CO', 'CO-VAU', 'Vaupés'], + ['CO', 'CO-VID', 'Vichada'], + ]; + } + + /** + * @inheritdoc + */ + public static function getDependencies() + { + return [ + InitializeDirectoryData::class, + ]; + } + + /** + * @inheritdoc + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/Directory/Setup/Patch/Data/AddDataForPoland.php b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForPoland.php new file mode 100644 index 0000000000000..c5ab4cd186286 --- /dev/null +++ b/app/code/Magento/Directory/Setup/Patch/Data/AddDataForPoland.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See PLPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Directory\Setup\Patch\Data; + +use Magento\Directory\Setup\DataInstaller; +use Magento\Directory\Setup\DataInstallerFactory; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; + +/** + * Add Poland States + */ +class AddDataForPoland implements DataPatchInterface +{ + /** + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** + * @var DataInstallerFactory + */ + private $dataInstallerFactory; + + /** + * @param ModuleDataSetupInterface $moduleDataSetup + * @param DataInstallerFactory $dataInstallerFactory + */ + public function __construct( + ModuleDataSetupInterface $moduleDataSetup, + DataInstallerFactory $dataInstallerFactory + ) { + $this->moduleDataSetup = $moduleDataSetup; + $this->dataInstallerFactory = $dataInstallerFactory; + } + + /** + * @inheritdoc + */ + public function apply() + { + /** @var DataInstaller $dataInstaller */ + $dataInstaller = $this->dataInstallerFactory->create(); + $dataInstaller->addCountryRegions( + $this->moduleDataSetup->getConnection(), + $this->getDataForPoland() + ); + } + + /** + * Poland states data. + * + * @return array + */ + private function getDataForPoland() + { + return [ + ['PL', 'PL-02', 'dolnośląskie'], + ['PL', 'PL-04', 'kujawsko-pomorskie'], + ['PL', 'PL-06', 'lubelskie'], + ['PL', 'PL-08', 'lubuskie'], + ['PL', 'PL-10', 'łódzkie'], + ['PL', 'PL-12', 'małopolskie'], + ['PL', 'PL-14', 'mazowieckie'], + ['PL', 'PL-16', 'opolskie'], + ['PL', 'PL-18', 'podkarpackie'], + ['PL', 'PL-20', 'podlaskie'], + ['PL', 'PL-22', 'pomorskie'], + ['PL', 'PL-24', 'śląskie'], + ['PL', 'PL-26', 'świętokrzyskie'], + ['PL', 'PL-28', 'warmińsko-mazurskie'], + ['PL', 'PL-30', 'wielkopolskie'], + ['PL', 'PL-32', 'zachodniopomorskie'], + ]; + } + + /** + * @inheritdoc + */ + public static function getDependencies() + { + return [ + InitializeDirectoryData::class, + ]; + } + + /** + * @inheritdoc + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/Directory/Test/Mftf/Data/CurrencyConfigData.xml b/app/code/Magento/Directory/Test/Mftf/Data/CurrencyConfigData.xml new file mode 100644 index 0000000000000..fb21ee42fe2fc --- /dev/null +++ b/app/code/Magento/Directory/Test/Mftf/Data/CurrencyConfigData.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="BaseCurrencyRUBConfigData"> + <data key="path">currency/options/base</data> + <data key="label">Russian Ruble</data> + <data key="value">RUB</data> + </entity> +</entities> diff --git a/app/code/Magento/Directory/Test/Mftf/Page/AdminCurrencySetupPage.xml b/app/code/Magento/Directory/Test/Mftf/Page/AdminCurrencySetupPage.xml new file mode 100644 index 0000000000000..03c2b0f856d19 --- /dev/null +++ b/app/code/Magento/Directory/Test/Mftf/Page/AdminCurrencySetupPage.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="AdminCurrencySetupPage" url="admin/system_config/edit/section/currency/" area="admin" module="Magento_Directory"> + <section name="AdminScheduledImportSettingsSection"/> + </page> +</pages> diff --git a/app/code/Magento/Directory/Test/Mftf/Section/AdminScheduledImportSettingsSection.xml b/app/code/Magento/Directory/Test/Mftf/Section/AdminScheduledImportSettingsSection.xml new file mode 100644 index 0000000000000..4c4b0d6c9541e --- /dev/null +++ b/app/code/Magento/Directory/Test/Mftf/Section/AdminScheduledImportSettingsSection.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="AdminScheduledImportSettingsSection"> + <element name="head" type="button" selector="#currency_import-head"/> + <element name="enabled" type="input" selector="#currency_import_enabled"/> + <element name="service" type="input" selector="#currency_import_service"/> + </section> +</sections> diff --git a/app/code/Magento/Directory/Test/Mftf/Test/AdminScheduledImportSettingsHiddenTest.xml b/app/code/Magento/Directory/Test/Mftf/Test/AdminScheduledImportSettingsHiddenTest.xml new file mode 100644 index 0000000000000..0320b6f422cd6 --- /dev/null +++ b/app/code/Magento/Directory/Test/Mftf/Test/AdminScheduledImportSettingsHiddenTest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminScheduledImportSettingsHiddenTest"> + <annotations> + <features value="Directory"/> + <title value="Scheduled import settings hidden" /> + <description value="Scheduled Import Settings' should hide fields when 'Enabled' is 'No'"/> + <severity value="MINOR"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <magentoCLI command="config:set currency/import/enabled 1" stepKey="enableCurrencyImport"/> + </before> + + <after> + <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI command="config:set currency/import/enabled 0" stepKey="disableCurrencyImport"/> + </after> + + <amOnPage url="{{AdminCurrencySetupPage.url}}" stepKey="openCurrencyOptionsPage" /> + <conditionalClick dependentSelector="{{AdminScheduledImportSettingsSection.enabled}}" visible="false" selector="{{AdminScheduledImportSettingsSection.head}}" stepKey="openCollapsibleBlock"/> + <see selector="{{AdminScheduledImportSettingsSection.service}}" userInput="Fixer.io" stepKey="seeServiceFixerIo"/> + <selectOption selector="{{AdminScheduledImportSettingsSection.enabled}}" userInput="0" stepKey="disableCurrencyImportOption"/> + <dontSeeElement selector="{{AdminScheduledImportSettingsSection.service}}" stepKey="dontSeeServiceFixerIo"/> + </test> +</tests> diff --git a/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/CurrencyConverterApiTest.php b/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/CurrencyConverterApiTest.php new file mode 100644 index 0000000000000..797f7b73f33be --- /dev/null +++ b/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/CurrencyConverterApiTest.php @@ -0,0 +1,193 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Directory\Test\Unit\Model\Currency\Import; + +use Magento\Directory\Model\Currency; +use Magento\Directory\Model\Currency\Import\CurrencyConverterApi; +use Magento\Directory\Model\CurrencyFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\DataObject; +use Magento\Framework\HTTP\ZendClient; +use Magento\Framework\HTTP\ZendClientFactory; +use PHPUnit_Framework_MockObject_MockObject as MockObject; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use PHPUnit\Framework\TestCase; + +/** + * CurrencyConverterApi converter test. + */ +class CurrencyConverterApiTest extends TestCase +{ + /** + * @var CurrencyConverterApi + */ + private $model; + + /** + * @var CurrencyFactory|MockObject + */ + private $currencyFactory; + + /** + * @var ZendClientFactory|MockObject + */ + private $httpClientFactory; + + /** + * @var ScopeConfigInterface|MockObject + */ + private $scopeConfig; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->currencyFactory = $this->getMockBuilder(CurrencyFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->httpClientFactory = $this->getMockBuilder(ZendClientFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->scopeConfig = $this->getMockBuilder(ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + + $objectManagerHelper = new ObjectManagerHelper($this); + $this->model = $objectManagerHelper->getObject( + CurrencyConverterApi::class, + [ + 'currencyFactory' => $this->currencyFactory, + 'scopeConfig' => $this->scopeConfig, + 'httpClientFactory' => $this->httpClientFactory, + ] + ); + } + + /** + * Prepare CurrencyFactory mock. + */ + private function prepareCurrencyFactoryMock(): void + { + $currencyFromList = ['USD']; + $currencyToList = ['EUR', 'UAH']; + + /** @var Currency|MockObject $currency */ + $currency = $this->getMockBuilder(Currency::class)->disableOriginalConstructor()->getMock(); + $currency->expects($this->once())->method('getConfigBaseCurrencies')->willReturn($currencyFromList); + $currency->expects($this->once())->method('getConfigAllowCurrencies')->willReturn($currencyToList); + + $this->currencyFactory->expects($this->atLeastOnce())->method('create')->willReturn($currency); + } + + /** + * Prepare FetchRates test. + * + * @param string $responseBody + */ + private function prepareFetchRatesTest(string $responseBody): void + { + $this->prepareCurrencyFactoryMock(); + + $this->scopeConfig->method('getValue') + ->withConsecutive( + ['currency/currencyconverterapi/api_key', 'store'], + ['currency/currencyconverterapi/timeout', 'store'] + ) + ->willReturnOnConsecutiveCalls('api_key', 100); + + /** @var ZendClient|MockObject $httpClient */ + $httpClient = $this->getMockBuilder(ZendClient::class) + ->disableOriginalConstructor() + ->getMock(); + /** @var DataObject|MockObject $currencyMock */ + $httpResponse = $this->getMockBuilder(DataObject::class) + ->disableOriginalConstructor() + ->setMethods(['getBody']) + ->getMock(); + + $this->httpClientFactory->expects($this->once())->method('create')->willReturn($httpClient); + $httpClient->expects($this->once())->method('setUri')->willReturnSelf(); + $httpClient->expects($this->once())->method('setConfig')->willReturnSelf(); + $httpClient->expects($this->once())->method('request')->willReturn($httpResponse); + $httpResponse->expects($this->once())->method('getBody')->willReturn($responseBody); + } + + /** + * Test Fetch Rates + * + * @return void + */ + public function testFetchRates(): void + { + $expectedCurrencyRateList = ['USD' => ['EUR' => 0.891285, 'UAH' => 26.16]]; + $responseBody = '{"USD_EUR":0.891285,"USD_UAH":26.16,"USD_USD":1}'; + $this->prepareFetchRatesTest($responseBody); + + self::assertEquals($expectedCurrencyRateList, $this->model->fetchRates()); + } + + /** + * Test FetchRates when Service Response is empty. + */ + public function testFetchRatesWhenServiceResponseIsEmpty(): void + { + $responseBody = ''; + $expectedCurrencyRateList = ['USD' => ['EUR' => null, 'UAH' => null]]; + $cantRetrieveCurrencyMessage = "We can't retrieve a rate from " + . "https://free.currconv.com for %s."; + $this->prepareFetchRatesTest($responseBody); + + self::assertEquals($expectedCurrencyRateList, $this->model->fetchRates()); + + $messages = $this->model->getMessages(); + self::assertEquals(sprintf($cantRetrieveCurrencyMessage, 'EUR'), (string) $messages[0]); + self::assertEquals(sprintf($cantRetrieveCurrencyMessage, 'UAH'), (string) $messages[1]); + } + + /** + * Test FetchRates when Service Response has error. + */ + public function testFetchRatesWhenServiceResponseHasError(): void + { + $serviceErrorMessage = 'Service error'; + $responseBody = sprintf('{"error":"%s"}', $serviceErrorMessage); + $expectedCurrencyRateList = ['USD' => ['EUR' => null, 'UAH' => null]]; + $this->prepareFetchRatesTest($responseBody); + + self::assertEquals($expectedCurrencyRateList, $this->model->fetchRates()); + + $messages = $this->model->getMessages(); + self::assertEquals($serviceErrorMessage, (string) $messages[0]); + } + + /** + * Test FetchRates when Service URL is empty. + */ + public function testFetchRatesWhenServiceUrlIsEmpty(): void + { + $this->prepareCurrencyFactoryMock(); + + $this->scopeConfig->method('getValue') + ->withConsecutive( + ['currency/currencyconverterapi/api_key', 'store'], + ['currency/currencyconverterapi/timeout', 'store'] + ) + ->willReturnOnConsecutiveCalls('', 100); + + $expectedCurrencyRateList = ['USD' => ['EUR' => null, 'UAH' => null]]; + self::assertEquals($expectedCurrencyRateList, $this->model->fetchRates()); + + $noApiKeyErrorMessage = 'No API Key was specified or an invalid API Key was specified.'; + $messages = $this->model->getMessages(); + self::assertEquals($noApiKeyErrorMessage, (string) $messages[0]); + } +} diff --git a/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/FixerIoTest.php b/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/FixerIoTest.php index 7efcf0d62712a..3147ac6ca2b91 100644 --- a/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/FixerIoTest.php +++ b/app/code/Magento/Directory/Test/Unit/Model/Currency/Import/FixerIoTest.php @@ -74,7 +74,7 @@ public function testFetchRates(): void $responseBody = '{"success":"true","base":"USD","date":"2015-10-07","rates":{"EUR":0.9022}}'; $expectedCurrencyRateList = ['USD' => ['EUR' => 0.9022, 'UAH' => null]]; $message = "We can't retrieve a rate from " - . "http://data.fixer.io/api/latest?access_key=api_key&base=USD&symbols=EUR,UAH for UAH."; + . "http://data.fixer.io for UAH."; $this->scopeConfig->method('getValue') ->withConsecutive( diff --git a/app/code/Magento/Directory/etc/adminhtml/system.xml b/app/code/Magento/Directory/etc/adminhtml/system.xml index 474b8357dfe1f..5f97a5e8d90d6 100644 --- a/app/code/Magento/Directory/etc/adminhtml/system.xml +++ b/app/code/Magento/Directory/etc/adminhtml/system.xml @@ -48,7 +48,7 @@ </group> <group id="currencyconverterapi" translate="label" sortOrder="45" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Currency Converter API</label> - <field id="api_key" translate="label" type="obscure" sortOrder="0" showInDefault="1" showInWebsite="0" showInStore="0"> + <field id="api_key" translate="label" type="obscure" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0"> <label>API Key</label> <config_path>currency/currencyconverterapi/api_key</config_path> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> diff --git a/app/code/Magento/Directory/etc/config.xml b/app/code/Magento/Directory/etc/config.xml index 2ff0b484fe979..32099ff9d8af5 100644 --- a/app/code/Magento/Directory/etc/config.xml +++ b/app/code/Magento/Directory/etc/config.xml @@ -9,7 +9,7 @@ <default> <system> <currency> - <installed>AZN,AZM,AFN,ALL,DZD,AOA,ARS,AMD,AWG,AUD,BSD,BHD,BDT,BBD,BYR,BZD,BMD,BTN,BOB,BAM,BWP,BRL,GBP,BND,BGN,BUK,BIF,KHR,CAD,CVE,CZK,KYD,CLP,CNY,COP,KMF,CDF,CRC,HRK,CUP,DKK,DJF,DOP,XCD,EGP,SVC,GQE,ERN,EEK,ETB,EUR,FKP,FJD,GMD,GEK,GEL,GHS,GIP,GTQ,GNF,GYD,HTG,HNL,HKD,HUF,ISK,INR,IDR,IRR,IQD,ILS,JMD,JPY,JOD,KZT,KES,KWD,KGS,LAK,LVL,LBP,LSL,LRD,LYD,LTL,MOP,MKD,MGA,MWK,MYR,MVR,LSM,MRO,MUR,MXN,MDL,MNT,MAD,MZN,MMK,NAD,NPR,ANG,TRL,TRY,NZD,NIC,NGN,KPW,NOK,OMR,PKR,PAB,PGK,PYG,PEN,PHP,PLN,QAR,RHD,RON,ROL,RUB,RWF,SHP,STD,SAR,RSD,SCR,SLL,SGD,SKK,SBD,SOS,ZAR,KRW,LKR,SDG,SRD,SZL,SEK,CHF,SYP,TWD,TJS,TZS,THB,TOP,TTD,TND,TMM,USD,UGX,UAH,AED,UYU,UZS,VUV,VEB,VEF,VND,CHE,CHW,XOF,XPF,WST,YER,ZMK,ZWD</installed> + <installed>AZN,AZM,AFN,ALL,DZD,AOA,ARS,AMD,AWG,AUD,BSD,BHD,BDT,BBD,BYN,BZD,BMD,BTN,BOB,BAM,BWP,BRL,GBP,BND,BGN,BUK,BIF,KHR,CAD,CVE,CZK,KYD,CLP,CNY,COP,KMF,CDF,CRC,HRK,CUP,DKK,DJF,DOP,XCD,EGP,SVC,GQE,ERN,EEK,ETB,EUR,FKP,FJD,GMD,GEK,GEL,GHS,GIP,GTQ,GNF,GYD,HTG,HNL,HKD,HUF,ISK,INR,IDR,IRR,IQD,ILS,JMD,JPY,JOD,KZT,KES,KWD,KGS,LAK,LVL,LBP,LSL,LRD,LYD,LTL,MOP,MKD,MGA,MWK,MYR,MVR,LSM,MRO,MUR,MXN,MDL,MNT,MAD,MZN,MMK,NAD,NPR,ANG,TRL,TRY,NZD,NIC,NGN,KPW,NOK,OMR,PKR,PAB,PGK,PYG,PEN,PHP,PLN,QAR,RHD,RON,ROL,RUB,RWF,SHP,STD,SAR,RSD,SCR,SLL,SGD,SKK,SBD,SOS,ZAR,KRW,LKR,SDG,SRD,SZL,SEK,CHF,SYP,TWD,TJS,TZS,THB,TOP,TTD,TND,TMM,USD,UGX,UAH,AED,UYU,UZS,VUV,VEB,VEF,VND,CHE,CHW,XOF,XPF,WST,YER,ZMK,ZWD</installed> </currency> </system> <currency> diff --git a/app/code/Magento/Directory/etc/db_schema.xml b/app/code/Magento/Directory/etc/db_schema.xml index 163e972423b98..a9fb2c536a3fd 100644 --- a/app/code/Magento/Directory/etc/db_schema.xml +++ b/app/code/Magento/Directory/etc/db_schema.xml @@ -45,7 +45,7 @@ </table> <table name="directory_country_region_name" resource="default" engine="innodb" comment="Directory Country Region Name"> - <column xsi:type="varchar" name="locale" nullable="false" length="8" comment="Locale"/> + <column xsi:type="varchar" name="locale" nullable="false" length="16" comment="Locale"/> <column xsi:type="int" name="region_id" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Region ID"/> <column xsi:type="varchar" name="name" nullable="true" length="255" comment="Region Name"/> diff --git a/app/code/Magento/Directory/etc/di.xml b/app/code/Magento/Directory/etc/di.xml index 50cd65cc5045c..fb2c526ac730b 100644 --- a/app/code/Magento/Directory/etc/di.xml +++ b/app/code/Magento/Directory/etc/di.xml @@ -35,6 +35,7 @@ <item name="DE" xsi:type="string">DE</item> <item name="AT" xsi:type="string">AT</item> <item name="FI" xsi:type="string">FI</item> + <item name="BE" xsi:type="string">BE</item> </argument> </arguments> </type> diff --git a/app/code/Magento/Directory/registration.php b/app/code/Magento/Directory/registration.php index 17dece4a9d79d..142b6a3114384 100644 --- a/app/code/Magento/Directory/registration.php +++ b/app/code/Magento/Directory/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Directory', __DIR__); diff --git a/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AddDownloadableProductLinkActionGroup.xml b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AddDownloadableProductLinkActionGroup.xml new file mode 100644 index 0000000000000..d132b3bc609ae --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AddDownloadableProductLinkActionGroup.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="AddDownloadableProductLinkActionGroup"> + <annotations> + <description>Clicks on 'Add Link', under the 'Links' section. Fills in the provided Link details including Unlimited Downloads.</description> + </annotations> + <arguments> + <argument name="link" defaultValue="downloadableLink"/> + <argument name="index" type="string" defaultValue="1"/> + </arguments> + + <click selector="{{AdminProductDownloadableSection.linksAddLinkButton}}" stepKey="clickLinkAddLinkButton"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <fillField userInput="{{link.title}}" selector="{{AdminProductDownloadableSection.addLinkTitleInput(index)}}" stepKey="fillDownloadableLinkTitle"/> + <fillField userInput="{{link.price}}" selector="{{AdminProductDownloadableSection.addLinkPriceInput(index)}}" stepKey="fillDownloadableLinkPrice"/> + <selectOption userInput="{{link.file_type}}" selector="{{AdminProductDownloadableSection.addLinkFileTypeSelector(index)}}" stepKey="selectDownloadableLinkFileType"/> + <selectOption userInput="{{link.sample_type}}" selector="{{AdminProductDownloadableSection.addLinkSampleTypeSelector(index)}}" stepKey="selectDownloadableLinkSampleType"/> + <selectOption userInput="{{link.shareable}}" selector="{{AdminProductDownloadableSection.addLinkShareableSelector(index)}}" stepKey="selectDownloadableLinkShareable"/> + <checkOption selector="{{AdminProductDownloadableSection.addLinkIsUnlimitedDownloads(index)}}" stepKey="checkDownloadableLinkUnlimited"/> + <fillField userInput="{{link.file}}" selector="{{AdminProductDownloadableSection.addLinkFileUrlInput(index)}}" stepKey="fillDownloadableLinkFileUrl"/> + <attachFile userInput="{{link.sample}}" selector="{{AdminProductDownloadableSection.addLinkSampleUploadFile(index)}}" stepKey="attachDownloadableLinkUploadSample"/> + <waitForPageLoad stepKey="waitForPageLoadAfterFillingOutForm" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AddDownloadableProductLinkWithMaxDownloadsActionGroup.xml b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AddDownloadableProductLinkWithMaxDownloadsActionGroup.xml new file mode 100644 index 0000000000000..fb21724130042 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AddDownloadableProductLinkWithMaxDownloadsActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddDownloadableProductLinkWithMaxDownloadsActionGroup"> + <annotations> + <description>Clicks on 'Add Link'. Fills in the provided Link details including a Max Downloads limit.</description> + </annotations> + <arguments> + <argument name="link" defaultValue="downloadableLinkWithMaxDownloads"/> + </arguments> + + <click selector="{{AdminProductDownloadableSection.linksAddLinkButton}}" stepKey="clickLinkAddLinkButton"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <fillField userInput="{{link.title}}" selector="{{AdminProductDownloadableSection.addLinkTitleInput('0')}}" stepKey="fillDownloadableLinkTitle"/> + <fillField userInput="{{link.price}}" selector="{{AdminProductDownloadableSection.addLinkPriceInput('0')}}" stepKey="fillDownloadableLinkPrice"/> + <selectOption userInput="{{link.file_type}}" selector="{{AdminProductDownloadableSection.addLinkFileTypeSelector('0')}}" stepKey="selectDownloadableLinkFileType"/> + <selectOption userInput="{{link.sample_type}}" selector="{{AdminProductDownloadableSection.addLinkSampleTypeSelector('0')}}" stepKey="selectDownloadableLinkSampleType"/> + <selectOption userInput="{{link.shareable}}" selector="{{AdminProductDownloadableSection.addLinkShareableSelector('0')}}" stepKey="selectDownloadableLinkShareable"/> + <fillField userInput="{{link.max_downloads}}" selector="{{AdminProductDownloadableSection.addLinkMaxDownloadsInput('0')}}" stepKey="fillDownloadableLinkMaxDownloads"/> + <attachFile userInput="{{link.file}}" selector="{{AdminProductDownloadableSection.addLinkFileUploadFile('0')}}" stepKey="fillDownloadableLinkUploadFile"/> + <fillField userInput="{{link.sample}}" selector="{{AdminProductDownloadableSection.addLinkSampleUrlInput('0')}}" stepKey="fillDownloadableLinkSampleUrl"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AddDownloadableSampleFileActionGroup.xml b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AddDownloadableSampleFileActionGroup.xml new file mode 100644 index 0000000000000..b5b37d0a41b98 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AddDownloadableSampleFileActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddDownloadableSampleFileActionGroup"> + <annotations> + <description>Clicks on 'Add Link' under the 'Samples' section. Fills in the provided Downloadable Sample File details.</description> + </annotations> + <arguments> + <argument name="sample" defaultValue="downloadableSampleFile"/> + </arguments> + + <click selector="{{AdminProductDownloadableSection.samplesAddLinkButton}}" stepKey="clickSampleAddLinkButton"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <fillField userInput="{{sample.title}}" selector="{{AdminProductDownloadableSection.addSampleTitleInput('0')}}" stepKey="fillDownloadableSampleTitle"/> + <selectOption userInput="{{sample.file_type}}" selector="{{AdminProductDownloadableSection.addSampleFileTypeSelector('0')}}" stepKey="selectDownloadableSampleFileType"/> + <attachFile userInput="{{sample.file}}" selector="{{AdminProductDownloadableSection.addSampleFileUploadFile('0')}}" stepKey="selectDownloadableSampleUpload"/> + <waitForAjaxLoad stepKey="waitForSampleFileUpload"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AddDownloadableSampleUrlActionGroup.xml b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AddDownloadableSampleUrlActionGroup.xml new file mode 100644 index 0000000000000..5002722feb51d --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AddDownloadableSampleUrlActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddDownloadableSampleUrlActionGroup"> + <annotations> + <description>Clicks on 'Add Link' under the 'Samples' section. Fills in the provided Downloadable Sample URL details.</description> + </annotations> + <arguments> + <argument name="sample" defaultValue="downloadableSampleUrl"/> + </arguments> + + <click selector="{{AdminProductDownloadableSection.samplesAddLinkButton}}" stepKey="clickSampleAddLinkButton2"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <fillField userInput="{{sample.title}}" selector="{{AdminProductDownloadableSection.addSampleTitleInput('1')}}" stepKey="fillDownloadableSampleTitle"/> + <selectOption userInput="{{sample.file_type}}" selector="{{AdminProductDownloadableSection.addSampleFileTypeSelector('1')}}" stepKey="selectDownloadableSampleFileType"/> + <fillField userInput="{{sample.file}}" selector="{{AdminProductDownloadableSection.addSampleFileUrlInput('1')}}" stepKey="fillDownloadableSampleFileUrl"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AdminDownloadableProductActionGroup.xml b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AdminDownloadableProductActionGroup.xml deleted file mode 100644 index 2d2cdd969ac9d..0000000000000 --- a/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/AdminDownloadableProductActionGroup.xml +++ /dev/null @@ -1,102 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Fill main fields in product form--> - <actionGroup name="fillMainDownloadableProductForm"> - <annotations> - <description>Fills the Name, SKU, Price and Quantity on the Product creation/edit page.</description> - </annotations> - <arguments> - <argument name="product" defaultValue="DownloadableProduct"/> - </arguments> - - <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{product.name}}" stepKey="fillProductSku"/> - <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{product.sku}}" stepKey="fillProductName"/> - <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{product.price}}" stepKey="fillProductPrice"/> - <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{product.quantity}}" stepKey="fillProductQty"/> - </actionGroup> - - <!--Add a downloadable link that has max downloads--> - <actionGroup name="addDownloadableProductLinkWithMaxDownloads"> - <annotations> - <description>Clicks on 'Add Link'. Fills in the provided Link details including a Max Downloads limit.</description> - </annotations> - <arguments> - <argument name="link" defaultValue="downloadableLinkWithMaxDownloads"/> - </arguments> - - <click selector="{{AdminProductDownloadableSection.linksAddLinkButton}}" stepKey="clickLinkAddLinkButton"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <fillField userInput="{{link.title}}" selector="{{AdminProductDownloadableSection.addLinkTitleInput('0')}}" stepKey="fillDownloadableLinkTitle"/> - <fillField userInput="{{link.price}}" selector="{{AdminProductDownloadableSection.addLinkPriceInput('0')}}" stepKey="fillDownloadableLinkPrice"/> - <selectOption userInput="{{link.file_type}}" selector="{{AdminProductDownloadableSection.addLinkFileTypeSelector('0')}}" stepKey="selectDownloadableLinkFileType"/> - <selectOption userInput="{{link.sample_type}}" selector="{{AdminProductDownloadableSection.addLinkSampleTypeSelector('0')}}" stepKey="selectDownloadableLinkSampleType"/> - <selectOption userInput="{{link.shareable}}" selector="{{AdminProductDownloadableSection.addLinkShareableSelector('0')}}" stepKey="selectDownloadableLinkShareable"/> - <fillField userInput="{{link.max_downloads}}" selector="{{AdminProductDownloadableSection.addLinkMaxDownloadsInput('0')}}" stepKey="fillDownloadableLinkMaxDownloads"/> - <attachFile userInput="{{link.file}}" selector="{{AdminProductDownloadableSection.addLinkFileUploadFile('0')}}" stepKey="fillDownloadableLinkUploadFile"/> - <fillField userInput="{{link.sample}}" selector="{{AdminProductDownloadableSection.addLinkSampleUrlInput('0')}}" stepKey="fillDownloadableLinkSampleUrl"/> - </actionGroup> - - <!--Add a downloadable link with unlimited downloads--> - <actionGroup name="addDownloadableProductLink"> - <annotations> - <description>Clicks on 'Add Link', under the 'Links' section. Fills in the provided Link details including Unlimited Downloads.</description> - </annotations> - <arguments> - <argument name="link" defaultValue="downloadableLink"/> - <argument name="index" type="string" defaultValue="1"/> - </arguments> - - <click selector="{{AdminProductDownloadableSection.linksAddLinkButton}}" stepKey="clickLinkAddLinkButton"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <fillField userInput="{{link.title}}" selector="{{AdminProductDownloadableSection.addLinkTitleInput(index)}}" stepKey="fillDownloadableLinkTitle"/> - <fillField userInput="{{link.price}}" selector="{{AdminProductDownloadableSection.addLinkPriceInput(index)}}" stepKey="fillDownloadableLinkPrice"/> - <selectOption userInput="{{link.file_type}}" selector="{{AdminProductDownloadableSection.addLinkFileTypeSelector(index)}}" stepKey="selectDownloadableLinkFileType"/> - <selectOption userInput="{{link.sample_type}}" selector="{{AdminProductDownloadableSection.addLinkSampleTypeSelector(index)}}" stepKey="selectDownloadableLinkSampleType"/> - <selectOption userInput="{{link.shareable}}" selector="{{AdminProductDownloadableSection.addLinkShareableSelector(index)}}" stepKey="selectDownloadableLinkShareable"/> - <checkOption selector="{{AdminProductDownloadableSection.addLinkIsUnlimitedDownloads(index)}}" stepKey="checkDownloadableLinkUnlimited"/> - <fillField userInput="{{link.file}}" selector="{{AdminProductDownloadableSection.addLinkFileUrlInput(index)}}" stepKey="fillDownloadableLinkFileUrl"/> - <attachFile userInput="{{link.sample}}" selector="{{AdminProductDownloadableSection.addLinkSampleUploadFile(index)}}" stepKey="attachDownloadableLinkUploadSample"/> - <waitForPageLoad stepKey="waitForPageLoadAfterFillingOutForm" /> - </actionGroup> - - <!--Add a downloadable sample file--> - <actionGroup name="addDownloadableSampleFile"> - <annotations> - <description>Clicks on 'Add Link' under the 'Samples' section. Fills in the provided Downloadable Sample File details.</description> - </annotations> - <arguments> - <argument name="sample" defaultValue="downloadableSampleFile"/> - </arguments> - - <click selector="{{AdminProductDownloadableSection.samplesAddLinkButton}}" stepKey="clickSampleAddLinkButton"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <fillField userInput="{{sample.title}}" selector="{{AdminProductDownloadableSection.addSampleTitleInput('0')}}" stepKey="fillDownloadableSampleTitle"/> - <selectOption userInput="{{sample.file_type}}" selector="{{AdminProductDownloadableSection.addSampleFileTypeSelector('0')}}" stepKey="selectDownloadableSampleFileType"/> - <attachFile userInput="{{sample.file}}" selector="{{AdminProductDownloadableSection.addSampleFileUploadFile('0')}}" stepKey="selectDownloadableSampleUpload"/> - <waitForAjaxLoad stepKey="waitForSampleFileUpload"/> - </actionGroup> - - <!--Add a downloadable sample URL--> - <actionGroup name="addDownloadableSampleUrl"> - <annotations> - <description>Clicks on 'Add Link' under the 'Samples' section. Fills in the provided Downloadable Sample URL details.</description> - </annotations> - <arguments> - <argument name="sample" defaultValue="downloadableSampleUrl"/> - </arguments> - - <click selector="{{AdminProductDownloadableSection.samplesAddLinkButton}}" stepKey="clickSampleAddLinkButton2"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <fillField userInput="{{sample.title}}" selector="{{AdminProductDownloadableSection.addSampleTitleInput('1')}}" stepKey="fillDownloadableSampleTitle"/> - <selectOption userInput="{{sample.file_type}}" selector="{{AdminProductDownloadableSection.addSampleFileTypeSelector('1')}}" stepKey="selectDownloadableSampleFileType"/> - <fillField userInput="{{sample.file}}" selector="{{AdminProductDownloadableSection.addSampleFileUrlInput('1')}}" stepKey="fillDownloadableSampleFileUrl"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/FillMainDownloadableProductFormActionGroup.xml b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/FillMainDownloadableProductFormActionGroup.xml new file mode 100644 index 0000000000000..f18267459c4be --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/FillMainDownloadableProductFormActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!--Fill main fields in product form--> + <actionGroup name="FillMainDownloadableProductFormActionGroup"> + <annotations> + <description>Fills the Name, SKU, Price and Quantity on the Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="product" defaultValue="DownloadableProduct"/> + </arguments> + + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{product.name}}" stepKey="fillProductSku"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{product.sku}}" stepKey="fillProductName"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{product.price}}" stepKey="fillProductPrice"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{product.quantity}}" stepKey="fillProductQty"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultImageDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultImageDownloadableProductTest.xml index a7ce96ddf1fde..e5633707824dd 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultImageDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultImageDownloadableProductTest.xml @@ -23,7 +23,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove static.magento.com"/> @@ -33,15 +33,15 @@ <!-- Create product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="adminProductIndexPageAdd"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProductPage"> <argument name="product" value="DownloadableProduct"/> </actionGroup> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillMainProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillMainProductForm"> <argument name="product" value="DownloadableProduct"/> </actionGroup> <!-- Add image to product --> - <actionGroup ref="addProductImage" stepKey="addImageForProduct"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImageForProduct"> <argument name="image" value="MagentoLogo"/> </actionGroup> @@ -51,26 +51,26 @@ <fillField userInput="{{downloadableData.link_title}}" selector="{{AdminProductDownloadableSection.linksTitleInput}}" stepKey="fillDownloadableLinkTitle"/> <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkLinksPurchasedSeparately"/> <fillField userInput="{{downloadableData.sample_title}}" selector="{{AdminProductDownloadableSection.samplesTitleInput}}" stepKey="fillDownloadableSampleTitle"/> - <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addDownloadableLinkWithMaxDownloads"> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addDownloadableLinkWithMaxDownloads"> <argument name="link" value="downloadableLinkWithMaxDownloads"/> </actionGroup> - <actionGroup ref="addDownloadableProductLink" stepKey="addDownloadableLink"> + <actionGroup ref="AddDownloadableProductLinkActionGroup" stepKey="addDownloadableLink"> <argument name="link" value="downloadableLink"/> </actionGroup> <!--Save product--> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Assert product image in admin product form --> - <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertProductImageAdminProductPage"/> + <actionGroup ref="AssertProductImageAdminProductPageActionGroup" stepKey="assertProductImageAdminProductPage"/> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="DownloadableProduct"/> </actionGroup> <!-- Assert product image in storefront product page --> - <actionGroup ref="assertProductImageStorefrontProductPage" stepKey="assertProductImageStorefrontProductPage"> + <actionGroup ref="AssertProductImageStorefrontProductPageActionGroup" stepKey="assertProductImageStorefrontProductPage"> <argument name="product" value="DownloadableProduct"/> <argument name="image" value="MagentoLogo"/> </actionGroup> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml index d95ddaf12470d..c78c237935a90 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminAddDefaultVideoDownloadableProductTest.xml @@ -26,10 +26,10 @@ </after> <!-- Create a downloadable product --> <!-- Replacing steps in base AdminAddDefaultVideoSimpleProductTest --> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProductPage"> <argument name="product" value="DownloadableProduct"/> </actionGroup> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillMainProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillMainProductForm"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -40,15 +40,15 @@ <fillField userInput="{{downloadableData.link_title}}" selector="{{AdminProductDownloadableSection.linksTitleInput}}" stepKey="fillLinkTitle" after="checkOptionIsDownloadable"/> <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately" after="fillLinkTitle"/> <fillField userInput="{{downloadableData.sample_title}}" selector="{{AdminProductDownloadableSection.samplesTitleInput}}" stepKey="fillSampleTitle" after="checkOptionPurchaseSeparately"/> - <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addDownloadableProductLinkWithMaxDownloads" after="fillSampleTitle"> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addDownloadableProductLinkWithMaxDownloads" after="fillSampleTitle"> <argument name="link" value="downloadableLinkWithMaxDownloads"/> </actionGroup> - <actionGroup ref="addDownloadableProductLink" stepKey="addDownloadableProductLink" before="saveProductForm"> + <actionGroup ref="AddDownloadableProductLinkActionGroup" stepKey="addDownloadableProductLink" before="saveProductForm"> <argument name="link" value="downloadableLink"/> </actionGroup> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="DownloadableProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateAndEditDownloadableProductSettingsTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateAndEditDownloadableProductSettingsTest.xml index 2a7f5b437115b..ebd36dddc0b6c 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateAndEditDownloadableProductSettingsTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateAndEditDownloadableProductSettingsTest.xml @@ -24,7 +24,7 @@ </before> <after> <!-- Delete created downloadable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -33,12 +33,12 @@ </after> <!-- Create new downloadable product --> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="createDownloadableProduct"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createDownloadableProduct"> <argument name="productType" value="downloadable"/> </actionGroup> <!-- Fill all main fields --> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillMainProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillMainProductForm"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -53,7 +53,7 @@ </actionGroup> <!-- Save product form --> - <actionGroup ref="saveProductForm" stepKey="clickSaveButton"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveButton"/> <!-- Open product page --> <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openStorefrontProductPage"> @@ -93,7 +93,7 @@ <actionGroup ref="AdminSwitchProductGiftMessageStatusActionGroup" stepKey="disableGiftMessageSettings"/> <!-- Save product form --> - <actionGroup ref="saveProductForm" stepKey="clickSaveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveProduct"/> <!-- Verify Url Key after changing --> <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openProductPage"> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml index 55740af4d834f..d3933ae4fae7d 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml @@ -19,11 +19,11 @@ <group value="catalog"/> <group value="mtf_migrated"/> </annotations> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="openProductFillForm"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"> <argument name="productType" value="downloadable"/> </actionGroup> <!-- Fill form for Virtual Product Type --> - <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="_defaultProduct"/> </actionGroup> <see selector="{{AdminProductGridSection.productGridCell('1', 'Type')}}" userInput="Simple Product" stepKey="seeProductTypeInGrid"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml index 4f07334640cf3..e0a38e7db7552 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductAndAssignItToCustomStoreTest.xml @@ -33,7 +33,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Delete created downloadable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -50,12 +50,12 @@ <!-- Create Downloadable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="createProduct"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createProduct"> <argument name="productType" value="downloadable"/> </actionGroup> <!-- Fill downloadable product values --> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillDownloadableProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillDownloadableProductForm"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -69,12 +69,12 @@ <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately"/> <!-- Add downloadable link --> - <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addDownloadableProductLink"> <argument name="link" value="downloadableLinkWithMaxDownloads"/> </actionGroup> <!-- Save product --> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Switch default store view on store view created below --> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> @@ -84,7 +84,7 @@ </actionGroup> <!-- Assert product in custom store --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="DownloadableProduct"/> </actionGroup> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithCustomOptionsTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithCustomOptionsTest.xml index 54a2ff606f384..fb5983bb58b6d 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithCustomOptionsTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithCustomOptionsTest.xml @@ -33,7 +33,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Delete created downloadable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -44,12 +44,12 @@ <!-- Create Downloadable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="createProduct"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createProduct"> <argument name="productType" value="downloadable"/> </actionGroup> <!-- Fill downloadable product values --> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillDownloadableProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillDownloadableProductForm"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -81,12 +81,12 @@ <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately"/> <!-- Add downloadable link --> - <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addDownloadableProductLink"> <argument name="link" value="downloadableLinkWithMaxDownloads"/> </actionGroup> <!-- Save product --> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Go to storefront category page --> <amOnPage url="$$createCategory.name$$.html" stepKey="amOnCategoryPage"/> @@ -98,7 +98,7 @@ </actionGroup> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="DownloadableProduct"/> </actionGroup> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml index 8194e600673cb..18dedca393178 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithDefaultSetLinksTest.xml @@ -23,7 +23,9 @@ <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <!-- Create category --> <createData entity="SimpleSubCategory" stepKey="createCategory"/> - + <!-- Reindex and clear page cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" arguments="full_page" stepKey="flushCache"/> <!-- Login as admin --> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> </before> @@ -33,7 +35,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Delete created downloadable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -44,12 +46,12 @@ <!-- Create downloadable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="createProduct"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createProduct"> <argument name="productType" value="downloadable"/> </actionGroup> <!-- Fill downloadable product values --> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillDownloadableProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillDownloadableProductForm"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -63,22 +65,24 @@ <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately"/> <!-- Add first downloadable link --> - <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addFirstDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addFirstDownloadableProductLink"> <argument name="link" value="downloadableLinkWithMaxDownloads"/> </actionGroup> <!-- Add second downloadable link --> - <actionGroup ref="addDownloadableProductLink" stepKey="addSecondDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkActionGroup" stepKey="addSecondDownloadableProductLink"> <argument name="link" value="downloadableLink"/> </actionGroup> <!-- Save product --> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> <!-- Find downloadable product in grid --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> <waitForPageLoad stepKey="waitForAdminProductPageLoad"/> - <actionGroup ref="filterProductGridBySku" stepKey="findCreatedConfigurableProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedConfigurableProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProduct"/> @@ -99,7 +103,7 @@ </actionGroup> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="DownloadableProduct"/> </actionGroup> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithGroupPriceTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithGroupPriceTest.xml index 06cf31b763f1c..64920432f2e01 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithGroupPriceTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithGroupPriceTest.xml @@ -33,7 +33,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Delete created downloadable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="ApiDownloadableProduct"/> </actionGroup> @@ -44,7 +44,7 @@ <!-- Create downloadable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="createProduct"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createProduct"> <argument name="productType" value="downloadable"/> </actionGroup> @@ -55,7 +55,7 @@ </actionGroup> <!-- Fill downloadable product values --> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillDownloadableProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillDownloadableProductForm"> <argument name="product" value="ApiDownloadableProduct"/> </actionGroup> @@ -69,22 +69,22 @@ <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately"/> <!-- Add first downloadable link --> - <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addFirstDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addFirstDownloadableProductLink"> <argument name="link" value="downloadableLinkWithMaxDownloads"/> </actionGroup> <!-- Add second downloadable link --> - <actionGroup ref="addDownloadableProductLink" stepKey="addSecondDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkActionGroup" stepKey="addSecondDownloadableProductLink"> <argument name="link" value="downloadableLink"/> </actionGroup> <!-- Save product --> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Find downloadable product in grid --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> <waitForPageLoad stepKey="waitForAdminProductPageLoad"/> - <actionGroup ref="filterProductGridBySku" stepKey="findCreatedConfigurableProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedConfigurableProduct"> <argument name="product" value="ApiDownloadableProduct"/> </actionGroup> <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProduct"/> @@ -96,7 +96,7 @@ <seeElement selector="{{AdminProductDownloadableSection.addLinkTitleInput('1')}}" stepKey="seeSecondLinkTitle"/> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="ApiDownloadableProduct"/> </actionGroup> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml index f2e4bdfb4890f..9ad20385519d1 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithInvalidDomainLinkUrlTest.xml @@ -21,23 +21,23 @@ <before> <remove keyForRemoval="addDownloadableDomain" /> </before> - <actionGroup ref="addDownloadableProductLink" stepKey="addDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkActionGroup" stepKey="addDownloadableProductLink"> <argument name="link" value="downloadableLink"/> <argument name="index" value="0"/> </actionGroup> - <actionGroup ref="SaveProductFormNoSuccessCheck" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormNoSuccessCheckActionGroup" stepKey="saveProduct"/> <see selector="{{AdminProductMessagesSection.errorMessage}}" userInput="Link URL's domain is not in list of downloadable_domains in env.php." stepKey="seeLinkUrlInvalidMessage" after="saveProduct" /> <magentoCLI stepKey="addDownloadableDomain2" command="downloadable:domains:add static.magento.com" after="seeLinkUrlInvalidMessage" /> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillDownloadableProductFormAgain" after="addDownloadableDomain2"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillDownloadableProductFormAgain" after="addDownloadableDomain2"> <argument name="product" value="DownloadableProduct"/> </actionGroup> <checkOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkIsDownloadable" after="fillDownloadableProductFormAgain"/> <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkIsLinksPurchasedSeparately" after="checkIsDownloadable"/> - <actionGroup ref="addDownloadableProductLink" stepKey="addDownloadableProductLinkAgain" after="checkIsLinksPurchasedSeparately"> + <actionGroup ref="AddDownloadableProductLinkActionGroup" stepKey="addDownloadableProductLinkAgain" after="checkIsLinksPurchasedSeparately"> <argument name="link" value="downloadableLink"/> <argument name="index" value="0"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProductAfterAddingDomainToWhitelist" after="addDownloadableProductLinkAgain" /> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductAfterAddingDomainToWhitelist" after="addDownloadableProductLinkAgain" /> <scrollTo selector="{{StorefrontDownloadableProductSection.downloadableLinkByTitle(downloadableLink.title)}}" stepKey="scrollToLinks"/> <click selector="{{StorefrontDownloadableProductSection.downloadableLinkByTitle(downloadableLink.title)}}" stepKey="selectProductLink"/> <see selector="{{CheckoutCartProductSection.ProductPriceByName(DownloadableProduct.name)}}" userInput="$52.99" stepKey="assertProductPriceInCart"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml index e43b8f94c7a3d..3b315d030cc8c 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithLinkTest.xml @@ -33,7 +33,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Delete created downloadable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -44,12 +44,12 @@ <!-- Create downloadable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="createProduct"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createProduct"> <argument name="productType" value="downloadable"/> </actionGroup> <!-- Fill downloadable product values --> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillDownloadableProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillDownloadableProductForm"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -63,12 +63,12 @@ <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately"/> <!-- Add downloadable link --> - <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addDownloadableProductLink"> <argument name="link" value="downloadableLinkWithMaxDownloads"/> </actionGroup> <!-- Save product --> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Assert product in storefront category page --> <amOnPage url="$$createCategory.name$$.html" stepKey="amOnCategoryPage"/> @@ -78,7 +78,7 @@ </actionGroup> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="DownloadableProduct"/> </actionGroup> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithManageStockTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithManageStockTest.xml index fb6a48254fa8d..5ab683034bad8 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithManageStockTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithManageStockTest.xml @@ -33,7 +33,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Delete created downloadable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -44,12 +44,12 @@ <!-- Create downloadable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="createProduct"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createProduct"> <argument name="productType" value="downloadable"/> </actionGroup> <!-- Fill downloadable product values --> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillDownloadableProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillDownloadableProductForm"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -76,22 +76,22 @@ <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately"/> <!-- Add first downloadable link --> - <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addFirstDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addFirstDownloadableProductLink"> <argument name="link" value="downloadableLinkWithMaxDownloads"/> </actionGroup> <!-- Add second downloadable link --> - <actionGroup ref="addDownloadableProductLink" stepKey="addSecondDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkActionGroup" stepKey="addSecondDownloadableProductLink"> <argument name="link" value="downloadableLink"/> </actionGroup> <!-- Save product --> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Find downloadable product in grid --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> <waitForPageLoad stepKey="waitForAdminProductPageLoad"/> - <actionGroup ref="filterProductGridBySku" stepKey="findCreatedConfigurableProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedConfigurableProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProduct"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithOutOfStockStatusTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithOutOfStockStatusTest.xml index 5e3fe6836f7e9..3348603563ff1 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithOutOfStockStatusTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithOutOfStockStatusTest.xml @@ -33,7 +33,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Delete created downloadable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="DownloadableProductOutOfStock"/> </actionGroup> @@ -44,12 +44,12 @@ <!-- Create Downloadable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="createProduct"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createProduct"> <argument name="productType" value="downloadable"/> </actionGroup> <!-- Fill downloadable product values --> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillDownloadableProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillDownloadableProductForm"> <argument name="product" value="DownloadableProductOutOfStock"/> </actionGroup> @@ -63,17 +63,17 @@ <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately"/> <!-- Add first downloadable link --> - <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addFirstDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addFirstDownloadableProductLink"> <argument name="link" value="downloadableLinkWithMaxDownloads"/> </actionGroup> <!-- Add second downloadable link --> - <actionGroup ref="addDownloadableProductLink" stepKey="addSecondDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkActionGroup" stepKey="addSecondDownloadableProductLink"> <argument name="link" value="downloadableLink"/> </actionGroup> <!-- Save product --> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Assert product is out of stock --> <amOnPage url="{{DownloadableProductOutOfStock.urlKey}}.html" stepKey="navigateToProductPage"/> @@ -83,7 +83,7 @@ <!-- Find downloadable product in grid --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> <waitForPageLoad stepKey="waitForAdminProductPageLoad"/> - <actionGroup ref="filterProductGridBySku" stepKey="findCreatedConfigurableProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedConfigurableProduct"> <argument name="product" value="DownloadableProductOutOfStock"/> </actionGroup> <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProduct"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithSpecialPriceTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithSpecialPriceTest.xml index fb59d51831bae..0ac14680cff46 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithSpecialPriceTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithSpecialPriceTest.xml @@ -33,7 +33,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Delete created downloadable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="ApiDownloadableProduct"/> </actionGroup> @@ -44,7 +44,7 @@ <!-- Create downloadable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="createProduct"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createProduct"> <argument name="productType" value="downloadable"/> </actionGroup> @@ -54,7 +54,7 @@ </actionGroup> <!-- Fill downloadable product values --> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillDownloadableProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillDownloadableProductForm"> <argument name="product" value="ApiDownloadableProduct"/> </actionGroup> @@ -68,22 +68,22 @@ <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately"/> <!-- Add first downloadable link --> - <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addFirstDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addFirstDownloadableProductLink"> <argument name="link" value="downloadableLinkWithMaxDownloads"/> </actionGroup> <!-- Add second downloadable link --> - <actionGroup ref="addDownloadableProductLink" stepKey="addSecondDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkActionGroup" stepKey="addSecondDownloadableProductLink"> <argument name="link" value="downloadableLink"/> </actionGroup> <!-- Save product --> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Find downloadable product in grid --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> <waitForPageLoad stepKey="waitForAdminProductPageLoad"/> - <actionGroup ref="filterProductGridBySku" stepKey="findCreatedConfigurableProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedConfigurableProduct"> <argument name="product" value="ApiDownloadableProduct"/> </actionGroup> <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProduct"/> @@ -95,7 +95,7 @@ <seeElement selector="{{AdminProductDownloadableSection.addLinkTitleInput('1')}}" stepKey="seeSecondLinkTitle"/> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="ApiDownloadableProduct"/> </actionGroup> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutFillingQuantityAndStockTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutFillingQuantityAndStockTest.xml index af9487e3e6a23..c80c42bc53ed9 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutFillingQuantityAndStockTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutFillingQuantityAndStockTest.xml @@ -33,7 +33,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Delete created downloadable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -44,12 +44,12 @@ <!-- Create downloadable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="createProduct"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createProduct"> <argument name="productType" value="downloadable"/> </actionGroup> <!-- Fill downloadable product values --> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillDownloadableProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillDownloadableProductForm"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -65,22 +65,22 @@ <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately"/> <!-- Add first downloadable link --> - <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addFirstDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addFirstDownloadableProductLink"> <argument name="link" value="downloadableLinkWithMaxDownloads"/> </actionGroup> <!-- Add second downloadable link --> - <actionGroup ref="addDownloadableProductLink" stepKey="addSecondDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkActionGroup" stepKey="addSecondDownloadableProductLink"> <argument name="link" value="downloadableLink"/> </actionGroup> <!-- Save product --> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Find downloadable product in grid --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> <waitForPageLoad stepKey="waitForAdminProductPageLoad"/> - <actionGroup ref="filterProductGridBySku" stepKey="findCreatedConfigurableProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedConfigurableProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProduct"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutTaxClassIdTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutTaxClassIdTest.xml index dd7e3331a0ed2..27a27f22c87ff 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutTaxClassIdTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductWithoutTaxClassIdTest.xml @@ -33,7 +33,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Delete created downloadable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -44,12 +44,12 @@ <!-- Create downloadable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="createProduct"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createProduct"> <argument name="productType" value="downloadable"/> </actionGroup> <!-- Fill downloadable product values --> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillDownloadableProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillDownloadableProductForm"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -66,22 +66,22 @@ <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately"/> <!-- Add first downloadable link --> - <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addFirstDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addFirstDownloadableProductLink"> <argument name="link" value="downloadableLinkWithMaxDownloads"/> </actionGroup> <!-- Add second downloadable link --> - <actionGroup ref="addDownloadableProductLink" stepKey="addSecondDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkActionGroup" stepKey="addSecondDownloadableProductLink"> <argument name="link" value="downloadableLink"/> </actionGroup> <!-- Save product --> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Find downloadable product in grid --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> <waitForPageLoad stepKey="waitForAdminProductPageLoad"/> - <actionGroup ref="filterProductGridBySku" stepKey="findCreatedConfigurableProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedConfigurableProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProduct"/> @@ -93,7 +93,7 @@ <seeInField selector="{{AdminProductDownloadableSection.addLinkTitleInput('1')}}" userInput="{{downloadableLink.title}}" stepKey="seeSecondLinkTitle"/> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="DownloadableProduct"/> </actionGroup> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDeleteDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDeleteDownloadableProductTest.xml index 07124ea4846be..0d93bac16569f 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDeleteDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDeleteDownloadableProductTest.xml @@ -36,7 +36,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteDownloadableProductFilteredBySkuAndName"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteDownloadableProductFilteredBySkuAndName"> <argument name="product" value="$$createDownloadableProduct$$"/> </actionGroup> <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="A total of 1 record(s) have been deleted." stepKey="deleteMessage"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableProductSetEditContentTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableProductSetEditContentTest.xml index 72cdf589bec91..44c27c17adcd9 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableProductSetEditContentTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableProductSetEditContentTest.xml @@ -21,16 +21,16 @@ </annotations> <after> <!-- Delete downloadable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> </after> <!-- Create product --> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillProductForm"> <argument name="product" value="DownloadableProduct"/> </actionGroup> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableSetEditRelatedProductsTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableSetEditRelatedProductsTest.xml index f70769cdfe834..a09076b7dc06e 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableSetEditRelatedProductsTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminDownloadableSetEditRelatedProductsTest.xml @@ -21,16 +21,16 @@ <before></before> <after> <!-- Delete downloadable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> </after> <!-- Create product --> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillProductForm"> <argument name="product" value="DownloadableProduct"/> </actionGroup> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml index 20c1acaf8d612..4a13a3f60e385 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml @@ -23,7 +23,7 @@ <click selector="{{AdminProductDownloadableSection.sectionHeader}}" stepKey="openDownloadableSection" after="waitForSimpleProductPageLoad"/> <uncheckOption selector="{{AdminProductDownloadableSection.isDownloadableProduct}}" stepKey="checkOptionIsDownloadable" after="openDownloadableSection"/> <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has weight" stepKey="selectWeightForProduct" after="checkOptionIsDownloadable"/> - <actionGroup ref="saveProductForm" stepKey="saveDownloadableProductForm" after="selectWeightForProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveDownloadableProductForm" after="selectWeightForProduct"/> </test> <test name="AdminSimpleProductTypeSwitchingToDownloadableProductTest"> <annotations> @@ -59,14 +59,14 @@ <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="This item has no weight" stepKey="selectNoWeightForProduct"/> <actionGroup ref="AdminAddDownloadableLinkInformationActionGroup" stepKey="addDownloadableLinkInformation"/> <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately"/> - <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addDownloadableProductLink"> <argument name="link" value="downloadableLinkWithMaxDownloads"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveDownloadableProductForm"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveDownloadableProductForm"/> <!--Assert downloadable product on Admin product page grid--> <comment userInput="Assert configurable product in Admin product page grid" stepKey="commentAssertDownloadableProductOnAdmin"/> <amOnPage url="{{AdminCatalogProductPage.url}}" stepKey="goToCatalogProductPage"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGridBySku"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterProductGridBySku"> <argument name="sku" value="$$createProduct.sku$$"/> </actionGroup> <see selector="{{AdminProductGridSection.productGridCell('1', 'Name')}}" userInput="$$createProduct.name$$" stepKey="seeDownloadableProductNameInGrid"/> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml index 3597c12e82df0..e4a3be9494268 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultImageDownloadableProductTest.xml @@ -30,15 +30,15 @@ <!-- Create product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="adminProductIndexPageAdd"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProductPage"> <argument name="product" value="DownloadableProduct"/> </actionGroup> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillMainProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillMainProductForm"> <argument name="product" value="DownloadableProduct"/> </actionGroup> <!-- Add image to product --> - <actionGroup ref="addProductImage" stepKey="addImageForProduct"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImageForProduct"> <argument name="image" value="MagentoLogo"/> </actionGroup> @@ -48,30 +48,30 @@ <fillField userInput="{{downloadableData.link_title}}" selector="{{AdminProductDownloadableSection.linksTitleInput}}" stepKey="fillDownloadableLinkTitle"/> <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkLinksPurchasedSeparately"/> <fillField userInput="{{downloadableData.sample_title}}" selector="{{AdminProductDownloadableSection.samplesTitleInput}}" stepKey="fillDownloadableSampleTitle"/> - <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addDownloadableLinkWithMaxDownloads"> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addDownloadableLinkWithMaxDownloads"> <argument name="link" value="downloadableLinkWithMaxDownloads"/> </actionGroup> - <actionGroup ref="addDownloadableProductLink" stepKey="addDownloadableLink"> + <actionGroup ref="AddDownloadableProductLinkActionGroup" stepKey="addDownloadableLink"> <argument name="link" value="downloadableLink"/> </actionGroup> <!--Save product--> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Remove image from product --> - <actionGroup ref="removeProductImage" stepKey="removeProductImage"/> + <actionGroup ref="RemoveProductImageActionGroup" stepKey="removeProductImage"/> - <actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductFormAfterRemove"/> <!-- Assert product image not in admin product form --> - <actionGroup ref="assertProductImageNotInAdminProductPage" stepKey="assertProductImageNotInAdminProductPage"/> + <actionGroup ref="AssertProductImageNotInAdminProductPageActionGroup" stepKey="assertProductImageNotInAdminProductPage"/> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPageAfterRemove"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPageAfterRemove"> <argument name="product" value="DownloadableProduct"/> </actionGroup> <!-- Assert product image not in storefront product page --> - <actionGroup ref="assertProductImageNotInStorefrontProductPage" stepKey="assertProductImageNotInStorefrontProductPage"> + <actionGroup ref="AssertProductImageNotInStorefrontProductPageActionGroup" stepKey="assertProductImageNotInStorefrontProductPage"> <argument name="product" value="DownloadableProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml index 0d98862d9a5e7..5792fd3cc7eb7 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminRemoveDefaultVideoDownloadableProductTest.xml @@ -27,10 +27,10 @@ <!-- Create a downloadable product --> <!-- Replacing steps in base AdminRemoveDefaultVideoSimpleProductTest --> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProductPage"> <argument name="product" value="DownloadableProduct"/> </actionGroup> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillMainProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillMainProductForm"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -41,15 +41,15 @@ <fillField userInput="{{downloadableData.link_title}}" selector="{{AdminProductDownloadableSection.linksTitleInput}}" stepKey="fillLinkTitle" after="checkOptionIsDownloadable"/> <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkOptionPurchaseSeparately" after="fillLinkTitle"/> <fillField userInput="{{downloadableData.sample_title}}" selector="{{AdminProductDownloadableSection.samplesTitleInput}}" stepKey="fillSampleTitle" after="checkOptionPurchaseSeparately"/> - <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addDownloadableProductLinkWithMaxDownloads" after="fillSampleTitle"> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addDownloadableProductLinkWithMaxDownloads" after="fillSampleTitle"> <argument name="link" value="downloadableLinkWithMaxDownloads"/> </actionGroup> - <actionGroup ref="addDownloadableProductLink" stepKey="addDownloadableProductLink" before="saveProductForm"> + <actionGroup ref="AddDownloadableProductLinkActionGroup" stepKey="addDownloadableProductLink" before="saveProductForm"> <argument name="link" value="downloadableLink"/> </actionGroup> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="DownloadableProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/EditDownloadableProductWithSeparateLinksFromCartTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/EditDownloadableProductWithSeparateLinksFromCartTest.xml index 274dd39468a2b..6535bf11d43c4 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/EditDownloadableProductWithSeparateLinksFromCartTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/EditDownloadableProductWithSeparateLinksFromCartTest.xml @@ -30,12 +30,12 @@ <!-- Create downloadable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="createProduct"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createProduct"> <argument name="productType" value="downloadable"/> </actionGroup> <!-- Fill downloadable product values --> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillDownloadableProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillDownloadableProductForm"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -51,17 +51,17 @@ stepKey="checkOptionPurchaseSeparately"/> <!-- Add first downloadable link --> - <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addFirstDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addFirstDownloadableProductLink"> <argument name="link" value="downloadableLinkWithMaxDownloads"/> </actionGroup> <!-- Add second downloadable link --> - <actionGroup ref="addDownloadableProductLink" stepKey="addSecondDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkActionGroup" stepKey="addSecondDownloadableProductLink"> <argument name="link" value="downloadableLink"/> </actionGroup> <!-- Save product --> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> </before> <after> <!-- Remove downloadable domains --> @@ -71,7 +71,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Delete created downloadable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/EndToEndB2CAdminTest.xml index 75a66cec91692..307ce0f42905f 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -11,10 +11,10 @@ <!--Create Downloadable Product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitProductPageDownloadable" after="seeSimpleProductInGrid"/> <waitForPageLoad stepKey="waitForProductPageLoadDownloadable" after="visitProductPageDownloadable"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateDownloadableProduct" after="waitForProductPageLoadDownloadable"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateDownloadableProduct" after="waitForProductPageLoadDownloadable"> <argument name="product" value="DownloadableProduct"/> </actionGroup> - <actionGroup ref="fillMainDownloadableProductForm" stepKey="fillMainProductFormDownloadable" after="goToCreateDownloadableProduct"> + <actionGroup ref="FillMainDownloadableProductFormActionGroup" stepKey="fillMainProductFormDownloadable" after="goToCreateDownloadableProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -25,34 +25,34 @@ <fillField userInput="{{downloadableData.sample_title}}" selector="{{AdminProductDownloadableSection.samplesTitleInput}}" stepKey="fillDownloadableSampleTitle" after="checkLinksPurchasedSeparately"/> <!-- Link 1 --> - <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addDownloadableLinkWithMaxDownloads" after="fillDownloadableSampleTitle"> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addDownloadableLinkWithMaxDownloads" after="fillDownloadableSampleTitle"> <argument name="link" value="downloadableLinkWithMaxDownloads"/> </actionGroup> <!-- Link 2 --> - <actionGroup ref="addDownloadableProductLink" stepKey="addDownloadableLink" after="addDownloadableLinkWithMaxDownloads"> + <actionGroup ref="AddDownloadableProductLinkActionGroup" stepKey="addDownloadableLink" after="addDownloadableLinkWithMaxDownloads"> <argument name="link" value="downloadableLink"/> </actionGroup> <!-- Sample 1 --> - <actionGroup ref="addDownloadableSampleFile" stepKey="addDownloadSampleFile" after="addDownloadableLink"> + <actionGroup ref="AddDownloadableSampleFileActionGroup" stepKey="addDownloadSampleFile" after="addDownloadableLink"> <argument name="sample" value="downloadableSampleFile"/> </actionGroup> <!-- Sample 2 --> - <actionGroup ref="addDownloadableSampleUrl" stepKey="addDownloadableSampleUrl" after="addDownloadSampleFile"> + <actionGroup ref="AddDownloadableSampleUrlActionGroup" stepKey="addDownloadableSampleUrl" after="addDownloadSampleFile"> <argument name="sample" value="downloadableSampleUrl"/> </actionGroup> <!--Save Product--> - <actionGroup ref="saveProductForm" stepKey="saveDownloadableProduct" after="addDownloadableSampleUrl"/> - <actionGroup ref="viewProductInAdminGrid" stepKey="viewDownloadableProductInGrid" after="saveDownloadableProduct"> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveDownloadableProduct" after="addDownloadableSampleUrl"/> + <actionGroup ref="ViewProductInAdminGridActionGroup" stepKey="viewDownloadableProductInGrid" after="saveDownloadableProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> <!--@TODO Move cleanup to "after" when MQE-830 is resolved--> <comment userInput="Clean up downloadable product" stepKey="cleanUpDownloadableProduct" after="deleteSimpleProduct"/> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteDownloadableProduct" after="cleanUpDownloadableProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteDownloadableProduct" after="cleanUpDownloadableProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/ManualSelectAllDownloadableLinksDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/ManualSelectAllDownloadableLinksDownloadableProductTest.xml index a86fd544d24d6..3ab64be9ad2ca 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/ManualSelectAllDownloadableLinksDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/ManualSelectAllDownloadableLinksDownloadableProductTest.xml @@ -30,12 +30,12 @@ <!-- Create downloadable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="createProduct"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createProduct"> <argument name="productType" value="downloadable"/> </actionGroup> <!-- Fill downloadable product values --> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillDownloadableProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillDownloadableProductForm"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -51,17 +51,17 @@ stepKey="checkOptionPurchaseSeparately"/> <!-- Add first downloadable link --> - <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addFirstDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addFirstDownloadableProductLink"> <argument name="link" value="downloadableLinkWithMaxDownloads"/> </actionGroup> <!-- Add second downloadable link --> - <actionGroup ref="addDownloadableProductLink" stepKey="addSecondDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkActionGroup" stepKey="addSecondDownloadableProductLink"> <argument name="link" value="downloadableLink"/> </actionGroup> <!-- Save product --> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> </before> <after> <!-- Remove downloadable domains --> @@ -71,7 +71,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Delete created downloadable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml index 94fca6f507637..55c673146021d 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/NewProductsListWidgetDownloadableProductTest.xml @@ -46,7 +46,7 @@ <fillField userInput="This Is A Title" selector="{{AdminProductDownloadableSection.linksTitleInput}}" stepKey="fillDownloadableLinkTitle"/> <checkOption selector="{{AdminProductDownloadableSection.isLinksPurchasedSeparately}}" stepKey="checkLinksPurchasedSeparately"/> <fillField userInput="This Is Another Title" selector="{{AdminProductDownloadableSection.samplesTitleInput}}" stepKey="fillDownloadableSampleTitle"/> - <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addDownloadableLinkWithMaxDownloads"> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addDownloadableLinkWithMaxDownloads"> <argument name="link" value="downloadableLinkWithMaxDownloads"/> </actionGroup> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> @@ -59,4 +59,4 @@ <waitForPageLoad stepKey="waitForCmsPage"/> <see selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" userInput="{{_defaultProduct.name}}" stepKey="seeProductName"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/SelectAllDownloadableLinksDownloadableProductTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/SelectAllDownloadableLinksDownloadableProductTest.xml index f9ca6fea09cf0..3f77eff56193d 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/SelectAllDownloadableLinksDownloadableProductTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/SelectAllDownloadableLinksDownloadableProductTest.xml @@ -30,12 +30,12 @@ <!-- Create downloadable product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="createProduct"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createProduct"> <argument name="productType" value="downloadable"/> </actionGroup> <!-- Fill downloadable product values --> - <actionGroup ref="fillMainProductFormNoWeight" stepKey="fillDownloadableProductForm"> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillDownloadableProductForm"> <argument name="product" value="DownloadableProduct"/> </actionGroup> @@ -51,17 +51,17 @@ stepKey="checkOptionPurchaseSeparately"/> <!-- Add first downloadable link --> - <actionGroup ref="addDownloadableProductLinkWithMaxDownloads" stepKey="addFirstDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkWithMaxDownloadsActionGroup" stepKey="addFirstDownloadableProductLink"> <argument name="link" value="downloadableLinkWithMaxDownloads"/> </actionGroup> <!-- Add second downloadable link --> - <actionGroup ref="addDownloadableProductLink" stepKey="addSecondDownloadableProductLink"> + <actionGroup ref="AddDownloadableProductLinkActionGroup" stepKey="addSecondDownloadableProductLink"> <argument name="link" value="downloadableLink"/> </actionGroup> <!-- Save product --> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> </before> <after> <!-- Remove downloadable domains --> @@ -71,7 +71,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Delete created downloadable product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="DownloadableProduct"/> </actionGroup> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/StorefrontAdvanceCatalogSearchDownloadableBySkuWithHyphenTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/StorefrontAdvanceCatalogSearchDownloadableBySkuWithHyphenTest.xml index 7c415a8edccc4..e4a5bd732a83c 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/StorefrontAdvanceCatalogSearchDownloadableBySkuWithHyphenTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/StorefrontAdvanceCatalogSearchDownloadableBySkuWithHyphenTest.xml @@ -20,7 +20,7 @@ <group value="SearchEngineMysql"/> </annotations> <before> - <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add example.com static.magento.com"/> + <magentoCLI command="downloadable:domains:add example.com static.magento.com" before="product" stepKey="addDownloadableDomain"/> <createData entity="ApiDownloadableProduct" stepKey="product"/> <createData entity="ApiDownloadableLink" stepKey="addDownloadableLink1"> <requiredEntity createDataKey="product"/> @@ -30,7 +30,7 @@ </createData> </before> <after> - <magentoCLI stepKey="removeDownloadableDomain" command="downloadable:domains:remove example.com static.magento.com"/> + <magentoCLI command="downloadable:domains:remove example.com static.magento.com" stepKey="removeDownloadableDomain"/> </after> </test> </tests> diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/VerifyDisableDownloadableProductSamplesAreNotAccessibleTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/VerifyDisableDownloadableProductSamplesAreNotAccessibleTest.xml index ba2e3453a6d99..b0f7edaeaa3a9 100644 --- a/app/code/Magento/Downloadable/Test/Mftf/Test/VerifyDisableDownloadableProductSamplesAreNotAccessibleTest.xml +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/VerifyDisableDownloadableProductSamplesAreNotAccessibleTest.xml @@ -87,13 +87,13 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!-- Open Downloadable product from precondition --> - <actionGroup ref="goToProductPageViaID" stepKey="openProductEditPage"> + <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="openProductEditPage"> <argument name="productId" value="$createProduct.id$"/> </actionGroup> <!-- Change status of product to "Disable" and save it --> - <actionGroup ref="AdminSetProductDisabled" stepKey="disableProduct"/> - <actionGroup ref="saveProductForm" stepKey="clickSaveProduct"/> + <actionGroup ref="AdminSetProductDisabledActionGroup" stepKey="disableProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveProduct"/> <!-- Assert product is disable on Storefront --> <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="openCategoryPage"> diff --git a/app/code/Magento/Downloadable/Test/Unit/Helper/DataTest.php b/app/code/Magento/Downloadable/Test/Unit/Helper/DataTest.php new file mode 100644 index 0000000000000..7c59ef7d7ec64 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Unit/Helper/DataTest.php @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Downloadable\Test\Unit\Helper; + +use Magento\Downloadable\Model\Link; +use Magento\Store\Model\ScopeInterface; +use PHPUnit\Framework\TestCase; +use Magento\Downloadable\Helper\Data; +use Magento\Framework\App\Helper\Context; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class DataTest extends TestCase +{ + /** + * @var Data + */ + private $helper; + + /** + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfigMock; + + /** + * @var Context|\PHPUnit_Framework_MockObject_MockObject + */ + private $contextMock; + + /** + * Setup environment for test + */ + protected function setUp() + { + $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + $this->contextMock = $this->createMock(Context::class); + $this->contextMock->method('getScopeConfig')->willReturn($this->scopeConfigMock); + + $objectManager = new ObjectManagerHelper($this); + $this->helper = $objectManager->getObject( + Data::class, + ['context' => $this->contextMock] + ); + } + + /** + * Test getIsShareable() with data provider + * + * @param int $linkShareable + * @param bool $config + * @param bool $expectedResult + * @dataProvider getIsShareableDataProvider + */ + public function testGetIsShareable($linkShareable, $config, $expectedResult) + { + $this->scopeConfigMock->method('isSetFlag') + ->with(Link::XML_PATH_CONFIG_IS_SHAREABLE, ScopeInterface::SCOPE_STORE) + ->willReturn($config); + + $linkMock = $this->createMock(Link::class); + $linkMock->method('getIsShareable')->willReturn($linkShareable); + + $this->assertEquals($expectedResult, $this->helper->getIsShareable($linkMock)); + } + + /** + * Data provider for getIsShareable() + * + * @return array + */ + public function getIsShareableDataProvider() + { + return [ + 'link shareable yes' => [Link::LINK_SHAREABLE_YES, true, true], + 'link shareable no' => [Link::LINK_SHAREABLE_NO, true, false], + 'link shareable config true' => [Link::LINK_SHAREABLE_CONFIG, true, true], + 'link shareable config false' => [Link::LINK_SHAREABLE_CONFIG, false, false], + ]; + } +} diff --git a/app/code/Magento/Downloadable/Test/Unit/Helper/DownloadTest.php b/app/code/Magento/Downloadable/Test/Unit/Helper/DownloadTest.php index 9551cfe982bd5..f90c3f27b27c7 100644 --- a/app/code/Magento/Downloadable/Test/Unit/Helper/DownloadTest.php +++ b/app/code/Magento/Downloadable/Test/Unit/Helper/DownloadTest.php @@ -17,6 +17,9 @@ */ class DownloadTest extends \PHPUnit\Framework\TestCase { + /** @var array Result of get_headers() function */ + public static $headers; + /** @var DownloadHelper */ protected $_helper; @@ -230,6 +233,7 @@ protected function _setupUrlMocks($size = self::FILE_SIZE, $url = self::URL, $ad $this->returnValue($this->_handleMock) ); + self::$headers = ['200 OK']; $this->_helper->setResource($url, DownloadHelper::LINK_TYPE_URL); } diff --git a/app/code/Magento/Downloadable/Test/Unit/Observer/SetHasDownloadableProductsObserverTest.php b/app/code/Magento/Downloadable/Test/Unit/Observer/SetHasDownloadableProductsObserverTest.php new file mode 100644 index 0000000000000..53d9878f46ea2 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Unit/Observer/SetHasDownloadableProductsObserverTest.php @@ -0,0 +1,184 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Downloadable\Test\Unit\Observer; + +use Magento\Catalog\Model\Product\Type as ProductType; +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Downloadable\Model\Product\Type as DownloadableProductType; +use Magento\Downloadable\Observer\SetHasDownloadableProductsObserver; +use Magento\Framework\DataObject; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Item; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class SetHasDownloadableProductsObserverTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var CheckoutSession|MockObject + */ + private $checkoutSessionMock; + + /** + * @var SetHasDownloadableProductsObserver + */ + private $setHasDownloadableProductsObserver; + + /** + * @var Order|MockObject + */ + private $orderMock; + + /** + * Setup environment for test + */ + protected function setUp() + { + $this->objectManager = new ObjectManager($this); + + $this->orderMock = $this->createPartialMock(Order::class, ['getAllItems']); + + $this->checkoutSessionMock = $this->createPartialMock( + CheckoutSession::class, + [ + 'getHasDownloadableProducts', + 'setHasDownloadableProducts' + ] + ); + + $this->setHasDownloadableProductsObserver = $this->objectManager->getObject( + SetHasDownloadableProductsObserver::class, + [ + 'checkoutSession' => $this->checkoutSessionMock + ] + ); + } + + /** + * Test execute with session has downloadable products + */ + public function testExecuteWithSessionHasDownloadableProducts() + { + $event = new DataObject(['item' => $this->orderMock]); + $observer = new Observer(['event' => $event]); + + $this->checkoutSessionMock->method('getHasDownloadableProducts')->willReturn(true); + $this->orderMock->method('getAllItems')->willReturn([]); + + $this->checkoutSessionMock->expects($this->never()) + ->method('setHasDownloadableProducts')->with(true); + + $this->setHasDownloadableProductsObserver->execute($observer); + } + + /** + * Test execute with session has no downloadable products with the data provider + * + * @dataProvider executeWithSessionNoDownloadableProductsDataProvider + */ + public function testExecuteWithSessionNoDownloadableProducts($allItems, $expectedCall) + { + $event = new DataObject(['order' => $this->orderMock]); + $observer = new Observer(['event' => $event]); + + $allOrderItemsMock = []; + foreach ($allItems as $item) { + $allOrderItemsMock[] = $this->createOrderItem(...$item); + } + + $this->checkoutSessionMock->method('getHasDownloadableProducts')->willReturn(false); + + $this->orderMock->method('getAllItems')->willReturn($allOrderItemsMock); + + $this->checkoutSessionMock->expects($expectedCall) + ->method('setHasDownloadableProducts')->with(true); + + $this->setHasDownloadableProductsObserver->execute($observer); + } + + /** + * Create Order Item Mock + * + * @param string $productType + * @param string $realProductType + * @param string $isDownloadable + * @return Item|MockObject + */ + private function createOrderItem( + $productType = DownloadableProductType::TYPE_DOWNLOADABLE, + $realProductType = DownloadableProductType::TYPE_DOWNLOADABLE, + $isDownloadable = '1' + ) { + $item = $this->createPartialMock( + Item::class, + ['getProductType', 'getRealProductType', 'getProductOptionByCode'] + ); + + $item->expects($this->any()) + ->method('getProductType') + ->willReturn($productType); + $item->expects($this->any()) + ->method('getRealProductType') + ->willReturn($realProductType); + $item->expects($this->any()) + ->method('getProductOptionByCode') + ->with('is_downloadable') + ->willReturn($isDownloadable); + + return $item; + } + + /** + * Data Provider for test execute with session has no downloadable product + * + * @return array + */ + public function executeWithSessionNoDownloadableProductsDataProvider() + { + return [ + 'Order has one item is downloadable product' => [ + [ + [ + DownloadableProductType::TYPE_DOWNLOADABLE, + DownloadableProductType::TYPE_DOWNLOADABLE, + '1' + ], + [ + ProductType::TYPE_SIMPLE, + ProductType::TYPE_SIMPLE, + '1' + ] + ], + $this->once() + ], + 'Order has all items are simple product' => [ + [ + [ + ProductType::TYPE_SIMPLE, + ProductType::TYPE_SIMPLE, + '0' + ], + [ + ProductType::TYPE_SIMPLE, + ProductType::TYPE_SIMPLE, + '0' + ] + ], + $this->never() + ], + ]; + } +} diff --git a/app/code/Magento/Downloadable/Test/Unit/Ui/DataProvider/Product/Form/Modifier/Data/SamplesTest.php b/app/code/Magento/Downloadable/Test/Unit/Ui/DataProvider/Product/Form/Modifier/Data/SamplesTest.php new file mode 100644 index 0000000000000..2f5b47a1d86b5 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Unit/Ui/DataProvider/Product/Form/Modifier/Data/SamplesTest.php @@ -0,0 +1,158 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Downloadable\Test\Unit\Ui\DataProvider\Product\Form\Modifier\Data; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Downloadable\Ui\DataProvider\Product\Form\Modifier\Data\Samples; +use \Magento\Framework\Escaper; +use Magento\Downloadable\Model\Product\Type; +use Magento\Catalog\Model\Locator\LocatorInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Downloadable\Helper\File as DownloadableFile; +use Magento\Framework\UrlInterface; +use Magento\Catalog\Api\Data\ProductInterface; + +/** + * Test class to cover Sample Modifier + * + * Class \Magento\Downloadable\Test\Unit\Ui\DataProvider\Product\Form\Modifier\Data\SampleTest + */ +class SamplesTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var LocatorInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $locatorMock; + + /** + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfigMock; + + /** + * @var Escaper|\PHPUnit_Framework_MockObject_MockObject + */ + private $escaperMock; + + /** + * @var DownloadableFile|\PHPUnit_Framework_MockObject_MockObject + */ + private $downloadableFileMock; + + /** + * @var UrlInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $urlBuilderMock; + + /** + * @var ProductInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $productMock; + + /** + * @var Samples + */ + private $samples; + + /** + * @return void + */ + protected function setUp() + { + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->productMock = $this->getMockBuilder(ProductInterface::class) + ->setMethods(['getSamplesTitle', 'getId', 'getTypeId']) + ->getMockForAbstractClass(); + $this->locatorMock = $this->createMock(LocatorInterface::class); + $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + $this->escaperMock = $this->createMock(Escaper::class); + $this->downloadableFileMock = $this->createMock(DownloadableFile::class); + $this->urlBuilderMock = $this->createMock(UrlInterface::class); + $this->samples = $this->objectManagerHelper->getObject( + Samples::class, + [ + 'escaper' => $this->escaperMock, + 'locator' => $this->locatorMock, + 'scopeConfig' => $this->scopeConfigMock, + 'downloadableFile' => $this->downloadableFileMock, + 'urlBuilder' => $this->urlBuilderMock + ] + ); + } + + /** + * Test getSamplesTitle() + * + * @param int|null $id + * @param string $typeId + * @param \PHPUnit\Framework\MockObject\Matcher\InvokedCount $expectedGetTitle + * @param \PHPUnit\Framework\MockObject\Matcher\InvokedCount $expectedGetValue + * @return void + * @dataProvider getSamplesTitleDataProvider + */ + public function testGetSamplesTitle($id, $typeId, $expectedGetTitle, $expectedGetValue) + { + $title = 'My Title'; + $this->locatorMock->expects($this->any()) + ->method('getProduct') + ->willReturn($this->productMock); + $this->productMock->expects($this->once()) + ->method('getId') + ->willReturn($id); + $this->productMock->expects($this->any()) + ->method('getTypeId') + ->willReturn($typeId); + $this->productMock->expects($expectedGetTitle) + ->method('getSamplesTitle') + ->willReturn($title); + $this->scopeConfigMock->expects($expectedGetValue) + ->method('getValue') + ->willReturn($title); + + /* Assert Result */ + $this->assertEquals($title, $this->samples->getSamplesTitle()); + } + + /** + * @return array + */ + public function getSamplesTitleDataProvider() + { + return [ + [ + 'id' => 1, + 'typeId' => Type::TYPE_DOWNLOADABLE, + 'expectedGetTitle' => $this->once(), + 'expectedGetValue' => $this->never(), + ], + [ + 'id' => null, + 'typeId' => Type::TYPE_DOWNLOADABLE, + 'expectedGetTitle' => $this->never(), + 'expectedGetValue' => $this->once(), + ], + [ + 'id' => 1, + 'typeId' => 'someType', + 'expectedGetTitle' => $this->never(), + 'expectedGetValue' => $this->once(), + ], + [ + 'id' => null, + 'typeId' => 'someType', + 'expectedGetTitle' => $this->never(), + 'expectedGetValue' => $this->once(), + ], + ]; + } +} diff --git a/app/code/Magento/Downloadable/Test/Unit/_files/download_mock.php b/app/code/Magento/Downloadable/Test/Unit/_files/download_mock.php index e634f0ffa341d..7ab3bc939f4d0 100644 --- a/app/code/Magento/Downloadable/Test/Unit/_files/download_mock.php +++ b/app/code/Magento/Downloadable/Test/Unit/_files/download_mock.php @@ -22,3 +22,13 @@ function mime_content_type() { return DownloadTest::$mimeContentType; } + +/** + * Override standard function + * + * @return array + */ +function get_headers() +{ + return DownloadTest::$headers; +} diff --git a/app/code/Magento/Downloadable/etc/di.xml b/app/code/Magento/Downloadable/etc/di.xml index a932e5598f8ae..3dc592958588c 100644 --- a/app/code/Magento/Downloadable/etc/di.xml +++ b/app/code/Magento/Downloadable/etc/di.xml @@ -174,4 +174,16 @@ </argument> </arguments> </type> + <type name="Magento\Eav\Model\Config"> + <arguments> + <argument name="attributesForPreload" xsi:type="array"> + <item name="catalog_product" xsi:type="array"> + <item name="links_exist" xsi:type="string">catalog_product</item> + <item name="links_purchased_separately" xsi:type="string">catalog_product</item> + <item name="links_title" xsi:type="string">catalog_product</item> + <item name="samples_title" xsi:type="string">catalog_product</item> + </item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Downloadable/registration.php b/app/code/Magento/Downloadable/registration.php index c50856603fa50..a99b7129eebc8 100644 --- a/app/code/Magento/Downloadable/registration.php +++ b/app/code/Magento/Downloadable/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Downloadable', __DIR__); diff --git a/app/code/Magento/Downloadable/view/adminhtml/templates/product/composite/fieldset/downloadable.phtml b/app/code/Magento/Downloadable/view/adminhtml/templates/product/composite/fieldset/downloadable.phtml index 8d471a1e49e7f..17d5b73f49506 100644 --- a/app/code/Magento/Downloadable/view/adminhtml/templates/product/composite/fieldset/downloadable.phtml +++ b/app/code/Magento/Downloadable/view/adminhtml/templates/product/composite/fieldset/downloadable.phtml @@ -18,14 +18,14 @@ </legend><br /> <?php $_links = $block->getLinks(); ?> <?php $_isRequired = $block->getLinkSelectionRequired(); ?> - <div class="field admin__field link <?php if ($_isRequired) { echo ' required _required'; } ?>"> + <div class="field admin__field link<?php if ($_isRequired) { echo ' _required'; } ?>"> <label class="label admin__field-label"><span><?= $block->escapeHtml($block->getLinksTitle()) ?></span></label> <div class="control admin__field-control" id="downloadable-links-list"> <?php foreach ($_links as $_link) : ?> <div class="nested admin__field-option"> <?php if ($_linksPurchasedSeparately) : ?> <input type="checkbox" - class="admin__control-checkbox checkbox<?php if ($_isRequired) :?> validate-one-required-by-name<?php endif; ?> product downloadable link" + class="admin__control-checkbox checkbox<?php if ($_isRequired) :?> required-entry<?php endif; ?> product downloadable link" name="links[]" id="links_<?= $block->escapeHtmlAttr($_link->getId()) ?>" value="<?= $block->escapeHtmlAttr($_link->getId()) ?>" <?= $block->escapeHtml($block->getLinkCheckedValue($_link)) ?> @@ -81,7 +81,7 @@ require(['prototype'], function(){ } } //]]> - + }); </script> <?php endif;?> diff --git a/app/code/Magento/DownloadableGraphQl/etc/schema.graphqls b/app/code/Magento/DownloadableGraphQl/etc/schema.graphqls index ecadd031ab58d..2226f1acd8501 100644 --- a/app/code/Magento/DownloadableGraphQl/etc/schema.graphqls +++ b/app/code/Magento/DownloadableGraphQl/etc/schema.graphqls @@ -48,8 +48,8 @@ type DownloadableProductLinks @doc(description: "DownloadableProductLinks define sort_order: Int @doc(description: "A number indicating the sort order") price: Float @doc(description: "The price of the downloadable product") sample_url: String @doc(description: "URL to the downloadable sample") - is_shareable: Boolean @deprecated(reason: "This information shoud not be exposed on frontend") - number_of_downloads: Int @deprecated(reason: "This information shoud not be exposed on frontend") + is_shareable: Boolean @deprecated(reason: "This information should not be exposed on frontend") + number_of_downloads: Int @deprecated(reason: "This information should not be exposed on frontend") link_type: DownloadableFileTypeEnum @deprecated(reason: "`sample_url` serves to get the downloadable sample") sample_type: DownloadableFileTypeEnum @deprecated(reason: "`sample_url` serves to get the downloadable sample") sample_file: String @deprecated(reason: "`sample_url` serves to get the downloadable sample") diff --git a/app/code/Magento/DownloadableImportExport/Test/Unit/Helper/DataTest.php b/app/code/Magento/DownloadableImportExport/Test/Unit/Helper/DataTest.php new file mode 100644 index 0000000000000..ce42514e52263 --- /dev/null +++ b/app/code/Magento/DownloadableImportExport/Test/Unit/Helper/DataTest.php @@ -0,0 +1,279 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\DownloadableImportExport\Test\Unit\Helper; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\DownloadableImportExport\Helper\Data as HelperData; +use Magento\DownloadableImportExport\Model\Import\Product\Type\Downloadable; +use PHPUnit\Framework\TestCase; + +class DataTest extends TestCase +{ + /** + * @var HelperData + */ + private $helper; + + /** + * Setup environment for test + */ + protected function setUp() + { + $objectManagerHelper = new ObjectManagerHelper($this); + $this->helper = $objectManagerHelper->getObject(HelperData::class); + } + + /** + * Test isRowDownloadableEmptyOptions with dataProvider + * + * @param array $rowData + * @param bool $expected + * @dataProvider isRowDownloadableEmptyOptionsDataProvider + */ + public function testIsRowDownloadableEmptyOptions($rowData, $expected) + { + $this->assertEquals($expected, $this->helper->isRowDownloadableEmptyOptions($rowData)); + } + + /** + * Data Provider to test isRowDownloadableEmptyOptions + * + * @return array + */ + public function isRowDownloadableEmptyOptionsDataProvider() + { + return [ + 'Data set include downloadable link and sample' => [ + [ + Downloadable::COL_DOWNLOADABLE_LINKS => 'https://magento2.com/download_link', + Downloadable::COL_DOWNLOADABLE_SAMPLES => 'https://magento2.com/sample_link' + ], + false + ], + 'Data set with empty' => [ + [ + Downloadable::COL_DOWNLOADABLE_LINKS => '', + Downloadable::COL_DOWNLOADABLE_SAMPLES => '' + ], + true + ] + ]; + } + + /** + * Test isRowDownloadableNoValid with dataProvider + * + * @param array $rowData + * @param bool $expected + * @dataProvider isRowDownloadableNoValidDataProvider + */ + public function isRowDownloadableNoValid($rowData, $expected) + { + $this->assertEquals($expected, $this->helper->isRowDownloadableNoValid($rowData)); + } + + /** + * Data Provider to test isRowDownloadableEmptyOptions + * + * @return array + */ + public function isRowDownloadableNoValidDataProvider() + { + return [ + 'Data set include downloadable link and sample' => [ + [ + Downloadable::COL_DOWNLOADABLE_LINKS => 'https://magento2.com/download_link', + Downloadable::COL_DOWNLOADABLE_SAMPLES => 'https://magento2.com/sample_link' + ], + true + ], + 'Data set with empty' => [ + [ + Downloadable::COL_DOWNLOADABLE_LINKS => '', + Downloadable::COL_DOWNLOADABLE_SAMPLES => '' + ], + false + ] + ]; + } + + /** + * Test fillExistOptions with dataProvider + * + * @param array $base + * @param array $option + * @param array $existingOptions + * @param array $expected + * @dataProvider fillExistOptionsDataProvider + */ + public function testFillExistOptions($base, $option, $existingOptions, $expected) + { + $this->assertEquals($expected, $this->helper->fillExistOptions($base, $option, $existingOptions)); + } + + /** + * Data Provider to test fillExistOptions + * + * @return array + */ + public function fillExistOptionsDataProvider() + { + return [ + 'Data set 1' => [ + [], + [ + 'product_id' => 1, + 'sample_type' => 'sample_type1', + 'sample_url' => 'sample_url1', + 'sample_file' => 'sample_file1', + 'link_file' => 'link_file1', + 'link_type' => 'link_type1', + 'link_url' => 'link_url1' + ], + [ + [ + 'product_id' => 1, + 'sample_type' => 'sample_type1', + 'sample_url' => 'sample_url1', + 'sample_file' => 'sample_file1', + 'link_file' => 'link_file1', + 'link_type' => 'link_type1', + 'link_url' => 'link_url1' + ], + [ + 'product_id' => 2, + 'sample_type' => 'sample_type2', + 'sample_url' => 'sample_url2', + 'sample_file' => 'sample_file2', + 'link_file' => 'link_file2', + 'link_type' => 'link_type2', + 'link_url' => 'link_url2' + ] + ], + [ + 'product_id' => 1, + 'sample_type' => 'sample_type1', + 'sample_url' => 'sample_url1', + 'sample_file' => 'sample_file1', + 'link_file' => 'link_file1', + 'link_type' => 'link_type1', + 'link_url' => 'link_url1' + ] + ], + 'Data set 2' => [ + [], + [ + 'product_id' => 1, + 'sample_type' => 'sample_type1', + 'sample_url' => 'sample_url1', + 'sample_file' => 'sample_file1', + 'link_file' => 'link_file1', + 'link_type' => 'link_type1', + 'link_url' => 'link_url1' + ], + [], + [] + ] + ]; + } + + /** + * Test prepareDataForSave with dataProvider + * + * @param array $base + * @param array $replacement + * @param array $expected + * @dataProvider prepareDataForSaveDataProvider + */ + public function testPrepareDataForSave($base, $replacement, $expected) + { + $this->assertEquals($expected, $this->helper->prepareDataForSave($base, $replacement)); + } + + /** + * Data Provider to test prepareDataForSave + * + * @return array + */ + public function prepareDataForSaveDataProvider() + { + return [ + 'Data set 1' => [ + [], + [], + [] + ], + + 'Data set 2' => [ + [ + 'product_id' => 1, + 'sample_type' => 'sample_type1', + 'sample_url' => 'sample_url1', + 'sample_file' => 'sample_file1', + 'link_file' => 'link_file1', + 'link_type' => 'link_type1', + 'link_url' => 'link_url1' + ], + [ + [ + 'product_id' => 2, + 'sample_type' => 'sample_type2', + 'sample_url' => 'sample_url2', + 'sample_file' => 'sample_file2', + 'link_file' => 'link_file2', + 'link_type' => 'link_type2', + 'link_url' => 'link_url2' + ] + ], + [ + [ + 'product_id' => 2, + 'sample_type' => 'sample_type2', + 'sample_url' => 'sample_url2', + 'sample_file' => 'sample_file2', + 'link_file' => 'link_file2', + 'link_type' => 'link_type2', + 'link_url' => 'link_url2' + ] + ] + ] + ]; + } + + /** + * Test getTypeByValue with dataProvider + * + * @param string $option + * @param string $expected + * @dataProvider getTypeByValueDataProvider + */ + public function testGetTypeByValue($option, $expected) + { + $this->assertEquals($expected, $this->helper->getTypeByValue($option)); + } + + /** + * Data Provider for getTypeByValue + * + * @return array + */ + public function getTypeByValueDataProvider() + { + return [ + 'Case File Option Value' => [ + 'file1', + Downloadable::FILE_OPTION_VALUE + ], + 'Case url Option Value' => [ + 'https://example.com', + Downloadable::URL_OPTION_VALUE + ] + ]; + } +} diff --git a/app/code/Magento/DownloadableImportExport/registration.php b/app/code/Magento/DownloadableImportExport/registration.php index 0dcd082deb6d8..35147034c8d1e 100644 --- a/app/code/Magento/DownloadableImportExport/registration.php +++ b/app/code/Magento/DownloadableImportExport/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_DownloadableImportExport', __DIR__); diff --git a/app/code/Magento/Eav/Model/Config.php b/app/code/Magento/Eav/Model/Config.php index 0eecca21b0d54..20126d5146c35 100644 --- a/app/code/Magento/Eav/Model/Config.php +++ b/app/code/Magento/Eav/Model/Config.php @@ -7,12 +7,20 @@ use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; use Magento\Eav\Model\Entity\Type; +use Magento\Eav\Model\ResourceModel\Attribute\DefaultEntityAttributes\ProviderInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Model\AbstractModel; use Magento\Framework\Serialize\SerializerInterface; /** + * EAV config model. + * * @api + * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @since 100.0.2 */ class Config @@ -25,6 +33,11 @@ class Config const ATTRIBUTES_CODES_CACHE_ID = 'EAV_ENTITY_ATTRIBUTES_CODES'; /**#@-*/ + /** + * Xml path to caching user defined eav attributes configuration. + */ + private const XML_PATH_CACHE_USER_DEFINED_ATTRIBUTES = 'dev/caching/cache_user_defined_attributes'; + /**#@-*/ protected $_entityTypeData; @@ -116,6 +129,11 @@ class Config */ private $serializer; + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + /** * Cache of attributes per set * @@ -123,6 +141,20 @@ class Config */ private $attributesPerSet = []; + /** + * Is system attributes loaded flag. + * + * @var array + */ + private $isSystemAttributesLoaded = []; + + /** + * List of predefined system attributes for preload. + * + * @var array + */ + private $attributesForPreload; + /** * @param \Magento\Framework\App\CacheInterface $cache * @param \Magento\Eav\Model\Entity\TypeFactory $entityTypeFactory @@ -130,6 +162,8 @@ class Config * @param \Magento\Framework\App\Cache\StateInterface $cacheState * @param \Magento\Framework\Validator\UniversalFactory $universalFactory * @param SerializerInterface $serializer + * @param ScopeConfigInterface $scopeConfig + * @param array $attributesForPreload * @codeCoverageIgnore */ public function __construct( @@ -138,7 +172,9 @@ public function __construct( \Magento\Eav\Model\ResourceModel\Entity\Type\CollectionFactory $entityTypeCollectionFactory, \Magento\Framework\App\Cache\StateInterface $cacheState, \Magento\Framework\Validator\UniversalFactory $universalFactory, - SerializerInterface $serializer = null + SerializerInterface $serializer = null, + ScopeConfigInterface $scopeConfig = null, + $attributesForPreload = [] ) { $this->_cache = $cache; $this->_entityTypeFactory = $entityTypeFactory; @@ -146,6 +182,8 @@ public function __construct( $this->_cacheState = $cacheState; $this->_universalFactory = $universalFactory; $this->serializer = $serializer ?: ObjectManager::getInstance()->get(SerializerInterface::class); + $this->scopeConfig = $scopeConfig ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class); + $this->attributesForPreload = $attributesForPreload; } /** @@ -487,7 +525,7 @@ public function getAttributes($entityType) * @param mixed $entityType * @param mixed $code * @return AbstractAttribute - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException */ public function getAttribute($entityType, $code) { @@ -507,8 +545,152 @@ public function getAttribute($entityType, $code) return $this->attributes[$entityTypeCode][$code]; } + if (array_key_exists($entityTypeCode, $this->attributesForPreload) + && array_key_exists($code, $this->attributesForPreload[$entityTypeCode]) + ) { + $this->initSystemAttributes($entityType, $this->attributesForPreload[$entityTypeCode]); + } + if (isset($this->attributes[$entityTypeCode][$code])) { + \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__); + return $this->attributes[$entityTypeCode][$code]; + } + + if ($this->scopeConfig->getValue(self::XML_PATH_CACHE_USER_DEFINED_ATTRIBUTES)) { + $attribute = $this->cacheUserDefinedAttribute($entityType, $entityTypeCode, $code); + } else { + $attribute = $this->initUserDefinedAttribute($entityType, $entityTypeCode, $code); + } + + \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__); + return $attribute; + } + + /** + * Initialize predefined system attributes for preload. + * + * @param string $entityType + * @param array $systemAttributes + * @return $this|bool|void + * @throws LocalizedException + */ + private function initSystemAttributes($entityType, $systemAttributes) + { + $entityType = $this->getEntityType($entityType); + $entityTypeCode = $entityType->getEntityTypeCode(); + if (!empty($this->isSystemAttributesLoaded[$entityTypeCode])) { + return; + } + + $cacheKey = self::ATTRIBUTES_CACHE_ID . '-' . $entityTypeCode . '-preload'; + if ($this->isCacheEnabled() && ($attributes = $this->_cache->load($cacheKey))) { + $attributes = $this->serializer->unserialize($attributes); + if ($attributes) { + foreach ($attributes as $attribute) { + $attributeObject = $this->_createAttribute($entityType, $attribute); + $this->saveAttribute($attributeObject, $entityTypeCode, $attributeObject->getAttributeCode()); + } + return true; + } + } + + \Magento\Framework\Profiler::start('EAV: ' . __METHOD__, ['group' => 'EAV', 'method' => __METHOD__]); + + /** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Collection $attributes */ + $attributes = $this->_universalFactory->create( + $entityType->getEntityAttributeCollection() + )->setEntityTypeFilter( + $entityType + )->addFieldToFilter( + 'attribute_code', + ['in' => array_keys($systemAttributes)] + )->getData(); + + $attributeData = []; + foreach ($attributes as $attribute) { + if (empty($attribute['attribute_model'])) { + $attribute['attribute_model'] = $entityType->getAttributeModel(); + } + $attributeObject = $this->_createAttribute($entityType, $attribute); + $this->saveAttribute($attributeObject, $entityTypeCode, $attributeObject->getAttributeCode()); + $attributeData[$attribute['attribute_code']] = $attributeObject->toArray(); + } + if ($this->isCacheEnabled()) { + $this->_cache->save( + $this->serializer->serialize($attributeData), + $cacheKey, + [ + \Magento\Eav\Model\Cache\Type::CACHE_TAG, + \Magento\Eav\Model\Entity\Attribute::CACHE_TAG + ] + ); + } + + \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__); + $this->isSystemAttributesLoaded[$entityTypeCode] = true; + + return $this; + } + + /** + * Initialize user defined attribute from cache or cache it. + * + * @param string $entityType + * @param mixed $entityTypeCode + * @param string $code + * @return AbstractAttribute + * @throws LocalizedException + */ + private function cacheUserDefinedAttribute($entityType, $entityTypeCode, $code): AbstractAttribute + { + $cacheKey = self::ATTRIBUTES_CACHE_ID . '-attribute-' . $entityTypeCode . '-' . $code; + $attributeData = $this->isCacheEnabled() && ($attribute = $this->_cache->load($cacheKey)) + ? $this->serializer->unserialize($attribute) + : null; + if ($attributeData) { + if (isset($attributeData['attribute_id'])) { + $attribute = $this->_createAttribute($entityType, $attributeData); + } else { + $entityType = $this->getEntityType($entityType); + $attribute = $this->createAttribute($entityType->getAttributeModel()); + $attribute->setAttributeCode($code); + $attribute = $this->setAttributeData($attribute, $entityType); + } + } else { + $attribute = $this->createAttributeByAttributeCode($entityType, $code); + $this->_addAttributeReference( + $attribute->getAttributeId(), + $attribute->getAttributeCode(), + $entityTypeCode + ); + $this->saveAttribute($attribute, $entityTypeCode, $attribute->getAttributeCode()); + if ($this->isCacheEnabled()) { + $this->_cache->save( + $this->serializer->serialize($attribute->getData()), + $cacheKey, + [ + \Magento\Eav\Model\Cache\Type::CACHE_TAG, + \Magento\Eav\Model\Entity\Attribute::CACHE_TAG + ] + ); + } + } + + return $attribute; + } + + /** + * Initialize user defined attribute and save it to memory cache. + * + * @param mixed $entityType + * @param string $entityTypeCode + * @param string $code + * @return AbstractAttribute|null + * @throws LocalizedException + */ + private function initUserDefinedAttribute($entityType, $entityTypeCode, $code): ?AbstractAttribute + { $attributes = $this->loadAttributes($entityTypeCode); - $attribute = isset($attributes[$code]) ? $attributes[$code] : null; + $attribute = $attributes[$code] ?? null; if (!$attribute) { $attribute = $this->createAttributeByAttributeCode($entityType, $code); $this->_addAttributeReference( @@ -518,7 +700,7 @@ public function getAttribute($entityType, $code) ); $this->saveAttribute($attribute, $entityTypeCode, $attribute->getAttributeCode()); } - \Magento\Framework\Profiler::stop('EAV: ' . __METHOD__); + return $attribute; } @@ -639,7 +821,7 @@ protected function _createAttribute($entityType, $attributeData) $existsFullAttribute = $attribute->hasIsRequired(); $fullAttributeData = array_key_exists('is_required', $attributeData); - if ($existsFullAttribute || !$existsFullAttribute && !$fullAttributeData) { + if ($existsFullAttribute || (!$existsFullAttribute && !$fullAttributeData)) { return $attribute; } } @@ -708,6 +890,7 @@ public function importAttributesData($entityType, array $attributes) * @param string $entityType * @param string $attributeCode * @return AbstractAttribute + * @throws LocalizedException */ private function createAttributeByAttributeCode($entityType, $attributeCode) { @@ -723,13 +906,28 @@ private function createAttributeByAttributeCode($entityType, $attributeCode) $attribute->setAttributeCode($attributeCode); } + $attribute = $this->setAttributeData($attribute, $entityType); + + return $attribute; + } + + /** + * Set entity type id, backend type, is global to attribute. + * + * @param AbstractAttribute $attribute + * @param AbstractModel $entityType + * @return AbstractAttribute + */ + private function setAttributeData($attribute, $entityType): AbstractAttribute + { $entity = $entityType->getEntity(); - if ($entity instanceof \Magento\Eav\Model\ResourceModel\Attribute\DefaultEntityAttributes\ProviderInterface + if ($entity instanceof ProviderInterface && in_array($attribute->getAttributeCode(), $entity->getDefaultAttributes(), true) ) { $attribute->setBackendType(AbstractAttribute::TYPE_STATIC)->setIsGlobal(1); } $attribute->setEntityType($entityType)->setEntityTypeId($entityType->getId()); + return $attribute; } diff --git a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php index 1fd71e446e6bb..7649c89a07955 100644 --- a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php +++ b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php @@ -11,21 +11,22 @@ use Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend; use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource; use Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface; +use Magento\Eav\Model\ResourceModel\Attribute\DefaultEntityAttributes\ProviderInterface as DefaultAttributesProvider; use Magento\Framework\App\Config\Element; +use Magento\Framework\App\ObjectManager; use Magento\Framework\DataObject; use Magento\Framework\DB\Adapter\DuplicateException; use Magento\Framework\Exception\AlreadyExistsException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Model\AbstractModel; +use Magento\Framework\Model\ResourceModel\AbstractResource; use Magento\Framework\Model\ResourceModel\Db\ObjectRelationProcessor; use Magento\Framework\Model\ResourceModel\Db\TransactionManagerInterface; -use Magento\Eav\Model\ResourceModel\Attribute\DefaultEntityAttributes\ProviderInterface as DefaultAttributesProvider; -use Magento\Framework\Model\ResourceModel\AbstractResource; -use Magento\Framework\App\ObjectManager; /** * Entity/Attribute/Model - entity abstract * + * phpcs:disable Magento2.Classes.AbstractApi * @api * @author Magento Core Team <core@magentocommerce.com> * @SuppressWarnings(PHPMD.TooManyFields) @@ -266,6 +267,8 @@ public function setConnection($connection) /** * Resource initialization * + * phpcs:disable Magento2.CodeAnalysis.EmptyBlock + * * @return void */ protected function _construct() @@ -412,61 +415,44 @@ protected function _getConfig() * * @param string|int|Element $attribute * @return AbstractAttribute|false + * @throws LocalizedException * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function getAttribute($attribute) { /** @var $config \Magento\Eav\Model\Config */ $config = $this->_getConfig(); - if (is_numeric($attribute)) { - $attributeId = $attribute; - $attributeInstance = $config->getAttribute($this->getEntityType(), $attributeId); - if ($attributeInstance) { - $attributeCode = $attributeInstance->getAttributeCode(); - } - } elseif (is_string($attribute)) { - $attributeCode = $attribute; - $attributeInstance = $config->getAttribute($this->getEntityType(), $attributeCode); - if (!$attributeInstance->getAttributeCode() && in_array($attribute, $this->getDefaultAttributes())) { - $attributeInstance->setAttributeCode( - $attribute - )->setBackendType( - AbstractAttribute::TYPE_STATIC - )->setIsGlobal( - 1 - )->setEntity( - $this - )->setEntityType( - $this->getEntityType() - )->setEntityTypeId( - $this->getEntityType()->getId() - ); - } - } elseif ($attribute instanceof AbstractAttribute) { - $attributeInstance = $attribute; - $attributeCode = $attributeInstance->getAttributeCode(); + + $attributeInstance = $config->getAttribute($this->getEntityType(), $attribute); + + if (!$attributeInstance->getAttributeCode() && in_array($attribute, $this->getDefaultAttributes(), true)) { + $attributeInstance = clone $attributeInstance; + $attributeInstance->setData([]); + $attributeInstance->setAttributeCode( + $attribute + )->setBackendType( + AbstractAttribute::TYPE_STATIC + )->setIsGlobal( + 1 + )->setEntity( + $this + )->setEntityType( + $this->getEntityType() + )->setEntityTypeId( + $this->getEntityType()->getId() + ); } - if (empty($attributeInstance) - || !$attributeInstance instanceof AbstractAttribute - || !$attributeInstance->getId() - && !in_array($attributeInstance->getAttributeCode(), $this->getDefaultAttributes()) + if (!$attributeInstance instanceof AbstractAttribute + || (!$attributeInstance->getId() + && !in_array($attributeInstance->getAttributeCode(), $this->getDefaultAttributes(), true)) ) { return false; } - $attribute = $attributeInstance; - - if (!$attribute->getAttributeCode()) { - $attribute->setAttributeCode($attributeCode); - } - if (!$attribute->getAttributeModel()) { - $attribute->setAttributeModel($this->_getDefaultAttributeModel()); - } - - $this->addAttribute($attribute); + $this->addAttribute($attributeInstance); - return $attribute; + return $attributeInstance; } /** @@ -640,7 +626,7 @@ protected function _isApplicableAttribute($object, $attribute) public function walkAttributes($partMethod, array $args = [], $collectExceptionMessages = null) { $methodArr = explode('/', $partMethod); - switch (sizeof($methodArr)) { + switch (count($methodArr)) { case 1: $part = 'attribute'; $method = $methodArr[0]; @@ -687,6 +673,7 @@ public function walkAttributes($partMethod, array $args = [], $collectExceptionM } try { + // phpcs:disable Magento2.Functions.DiscouragedFunction $results[$attrCode] = call_user_func_array([$instance, $method], $args); } catch (\Magento\Eav\Model\Entity\Attribute\Exception $e) { if ($collectExceptionMessages) { @@ -826,9 +813,9 @@ public function getValueTablePrefix() $prefix = (string) $this->getEntityType()->getValueTablePrefix(); if (!empty($prefix)) { $this->_valueTablePrefix = $prefix; - /** - * entity type prefix include DB table name prefix - */ + /** + * entity type prefix include DB table name prefix + */ //$this->_resource->getTableName($prefix); } else { $this->_valueTablePrefix = $this->getEntityTable(); @@ -991,9 +978,9 @@ public function getDefaultAttributeSourceModel() /** * Load entity's attributes into the object * - * @param AbstractModel $object - * @param int $entityId - * @param array|null $attributes + * @param AbstractModel $object + * @param int $entityId + * @param array|null $attributes * @return $this */ public function load($object, $entityId, $attributes = []) @@ -1131,8 +1118,8 @@ protected function _getLoadAttributesSelect($object, $table) /** * Initialize attribute value for object * - * @param DataObject $object - * @param array $valueRow + * @param DataObject $object + * @param array $valueRow * @return $this */ protected function _setAttributeValue($object, $valueRow) @@ -1237,7 +1224,7 @@ protected function _getOrigObject($object) /** * Aggregate Data for attributes that will be deleted * - * @param array &$delete + * @param &array $delete * @param AbstractAttribute $attribute * @param AbstractEntity $object * @return void @@ -1248,6 +1235,7 @@ private function _aggregateDeleteData(&$delete, $attribute, $object) if (!isset($delete[$tableName])) { $delete[$tableName] = []; } + // phpcs:ignore Magento2.Performance.ForeachArrayMerge $delete[$tableName] = array_merge((array)$delete[$tableName], $valuesData); } } @@ -1422,7 +1410,7 @@ protected function _prepareStaticValue($key, $value) /** * Save object collected data * - * @param array $saveData array('newObject', 'entityRow', 'insert', 'update', 'delete') + * @param array $saveData array('newObject', 'entityRow', 'insert', 'update', 'delete') * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) @@ -1517,9 +1505,9 @@ protected function _processSaveData($saveData) /** * Insert entity attribute value * - * @param DataObject $object - * @param AbstractAttribute $attribute - * @param mixed $value + * @param DataObject $object + * @param AbstractAttribute $attribute + * @param mixed $value * @return $this */ protected function _insertAttribute($object, $attribute, $value) @@ -1530,10 +1518,10 @@ protected function _insertAttribute($object, $attribute, $value) /** * Update entity attribute value * - * @param DataObject $object - * @param AbstractAttribute $attribute - * @param mixed $valueId - * @param mixed $value + * @param DataObject $object + * @param AbstractAttribute $attribute + * @param mixed $valueId + * @param mixed $value * @return $this * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ @@ -1892,10 +1880,12 @@ protected function _getDefaultAttributes() */ public function getDefaultAttributes() { - return array_unique(array_merge( - $this->_getDefaultAttributes(), - [$this->getEntityIdField(), $this->getLinkField()] - )); + return array_unique( + array_merge( + $this->_getDefaultAttributes(), + [$this->getEntityIdField(), $this->getLinkField()] + ) + ); } /** diff --git a/app/code/Magento/Eav/Model/Entity/Attribute.php b/app/code/Magento/Eav/Model/Entity/Attribute.php index 8bd9ca2cc03c8..651bc96193780 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute.php @@ -285,13 +285,8 @@ public function beforeSave() // save default date value as timestamp if ($hasDefaultValue) { - try { - $locale = $this->_localeResolver->getLocale(); - $defaultValue = $this->_localeDate->date($defaultValue, $locale, false, false); - $this->setDefaultValue($defaultValue->format(DateTime::DATETIME_PHP_FORMAT)); - } catch (\Exception $e) { - throw new LocalizedException(__('The default date is invalid. Verify the date and try again.')); - } + $defaultValue = $this->getUtcDateDefaultValue($defaultValue); + $this->setDefaultValue($defaultValue); } } @@ -310,6 +305,29 @@ public function beforeSave() return parent::beforeSave(); } + /** + * Convert localized date default value to UTC + * + * @param string $defaultValue + * @return string + * @throws LocalizedException + */ + private function getUtcDateDefaultValue(string $defaultValue): string + { + $hasTime = $this->getFrontendInput() === 'datetime'; + try { + $defaultValue = $this->_localeDate->date($defaultValue, null, $hasTime, $hasTime); + if ($hasTime) { + $defaultValue->setTimezone(new \DateTimeZone($this->_localeDate->getDefaultTimezone())); + } + $utcValue = $defaultValue->format(DateTime::DATETIME_PHP_FORMAT); + } catch (\Exception $e) { + throw new LocalizedException(__('The default date is invalid. Verify the date and try again.')); + } + + return $utcValue; + } + /** * @inheritdoc * @@ -346,6 +364,7 @@ public function getBackendTypeByInput($type) break; case 'date': + case 'datetime': $field = 'datetime'; break; @@ -401,6 +420,10 @@ public function getDefaultValueByInput($type) $field = 'default_value_date'; break; + case 'datetime': + $field = 'default_value_datetime'; + break; + case 'boolean': $field = 'default_value_yesno'; break; @@ -489,9 +512,6 @@ public function getIdentities() /** * @inheritdoc * @since 100.0.7 - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __sleep() { @@ -505,9 +525,6 @@ public function __sleep() /** * @inheritdoc * @since 100.0.7 - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __wakeup() { diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php index 16fe495de18db..7066a752fe2a2 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php @@ -329,7 +329,7 @@ public function getAttributeCode() } /** - * Set attribute model + * Set attribute model class. * * @param array $data * @return $this @@ -1405,9 +1405,6 @@ public function setExtensionAttributes(\Magento\Eav\Api\Data\AttributeExtensionI /** * @inheritdoc * @since 100.0.7 - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __sleep() { @@ -1433,9 +1430,6 @@ public function __sleep() /** * @inheritdoc * @since 100.0.7 - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __wakeup() { diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/Datetime.php b/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/Datetime.php index a9cd3be246bb1..ea454fc0bcc74 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/Datetime.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Frontend/Datetime.php @@ -7,6 +7,8 @@ namespace Magento\Eav\Model\Entity\Attribute\Frontend; /** + * Entity datetime frontend attribute + * * @api * @since 100.0.2 */ @@ -42,10 +44,12 @@ public function getValue(\Magento\Framework\DataObject $object) $value = parent::getValue($object); if ($value) { + $showTime = $this->getAttribute()->getFrontendInput() === 'datetime' + ? \IntlDateFormatter::MEDIUM : \IntlDateFormatter::NONE; $data = $this->_localeDate->formatDateTime( new \DateTime($value), \IntlDateFormatter::MEDIUM, - \IntlDateFormatter::NONE + $showTime ); } diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Source/SpecificSourceInterface.php b/app/code/Magento/Eav/Model/Entity/Attribute/Source/SpecificSourceInterface.php new file mode 100644 index 0000000000000..c6422962f6b1b --- /dev/null +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Source/SpecificSourceInterface.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Eav\Model\Entity\Attribute\Source; + +use Magento\Framework\Api\CustomAttributesDataInterface; + +/** + * Can provide entity-specific options for an attribute. + */ +interface SpecificSourceInterface extends SourceInterface +{ + /** + * List of options specific to an entity. + * + * Same format as for "getAllOptions". + * Will be called instead of "getAllOptions". + * + * @param CustomAttributesDataInterface $entity + * @return array + */ + public function getOptionsFor(CustomAttributesDataInterface $entity): array; +} diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php b/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php index f9aa1a9ed3ba1..ef7b4a69808bc 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/Source/Table.php @@ -211,9 +211,13 @@ public function addValueSortToCollection($collection, $dir = \Magento\Framework\ $collection, $attribute, $valueExpr + )->addOptionToCollection( + $collection, + $attribute, + $valueExpr ); - $collection->getSelect()->order("{$attribute->getAttributeCode()} {$dir}"); + $collection->getSelect()->order("{$attribute->getAttributeCode()}_order {$dir}"); return $this; } diff --git a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php index dbeb23231a85d..9e4ad6fb53a33 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php +++ b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php @@ -780,9 +780,6 @@ public function getValidAttributeIds($attributeIds) * * @return array * @since 100.0.7 - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __sleep() { @@ -796,9 +793,6 @@ public function __sleep() * * @return void * @since 100.0.7 - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __wakeup() { diff --git a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Option.php b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Option.php index 79c277dcb6a82..a7be59e2c05d5 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Option.php +++ b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Option.php @@ -42,10 +42,13 @@ public function addOptionValueToCollection($collection, $attribute, $valueExpr) "{$optionTable2}.option_id={$valueExpr} AND {$optionTable2}.store_id=?", $collection->getStoreId() ); - $valueExpr = $connection->getCheckSql( - "{$optionTable2}.value_id IS NULL", - "{$optionTable1}.option_id", - "{$optionTable2}.option_id" + $valueIdExpr = $connection->getIfNullSql( + "{$optionTable2}.option_id", + "{$optionTable1}.option_id" + ); + $valueExpr = $connection->getIfNullSql( + "{$optionTable2}.value", + "{$optionTable1}.value" ); $collection->getSelect()->joinLeft( @@ -55,7 +58,37 @@ public function addOptionValueToCollection($collection, $attribute, $valueExpr) )->joinLeft( [$optionTable2 => $this->getTable('eav_attribute_option_value')], $tableJoinCond2, - [$attributeCode => $valueExpr] + [ + $attributeCode => $valueIdExpr, + $attributeCode . '_value' => $valueExpr, + ] + ); + + return $this; + } + + /** + * Add Join with option for collection select + * + * @param \Magento\Eav\Model\Entity\Collection\AbstractCollection $collection + * @param \Magento\Eav\Model\Entity\Attribute $attribute + * @param \Zend_Db_Expr $valueExpr + * @return $this + */ + public function addOptionToCollection($collection, $attribute, $valueExpr) + { + $connection = $this->getConnection(); + $attributeCode = $attribute->getAttributeCode(); + $optionTable1 = $attributeCode . '_option_t1'; + $tableJoinCond1 = "{$optionTable1}.option_id={$valueExpr}"; + $valueExpr = $connection->getIfNullSql( + "{$optionTable1}.sort_order" + ); + + $collection->getSelect()->joinLeft( + [$optionTable1 => $this->getTable('eav_attribute_option')], + $tableJoinCond1, + ["{$attributeCode}_order" => $valueExpr] ); return $this; diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DatetimeTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DatetimeTest.php index 66549f2e00415..163f3d208e724 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DatetimeTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Frontend/DatetimeTest.php @@ -5,22 +5,31 @@ */ namespace Magento\Eav\Test\Unit\Model\Entity\Attribute\Frontend; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; use Magento\Eav\Model\Entity\Attribute\Frontend\Datetime; +use Magento\Eav\Model\Entity\Attribute\Source\BooleanFactory; +use Magento\Framework\DataObject; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class DatetimeTest extends \PHPUnit\Framework\TestCase +/** + * Class to test Entity datetime frontend attribute + */ +class DatetimeTest extends TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var TimezoneInterface|MockObject */ private $localeDateMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var BooleanFactory|MockObject */ private $booleanFactoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var AbstractAttribute|MockObject */ private $attributeMock; @@ -29,40 +38,63 @@ class DatetimeTest extends \PHPUnit\Framework\TestCase */ private $model; + /** + * @inheritdoc + */ protected function setUp() { - $this->booleanFactoryMock = $this->createMock(\Magento\Eav\Model\Entity\Attribute\Source\BooleanFactory::class); - $this->localeDateMock = $this->createMock(\Magento\Framework\Stdlib\DateTime\TimezoneInterface::class); + $this->booleanFactoryMock = $this->createMock(BooleanFactory::class); + $this->localeDateMock = $this->createMock(TimezoneInterface::class); $this->attributeMock = $this->createPartialMock( - \Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class, - ['getAttributeCode', 'getFrontendLabel', 'getData'] + AbstractAttribute::class, + ['getAttributeCode', 'getFrontendLabel', 'getFrontendInput'] ); $this->model = new Datetime($this->booleanFactoryMock, $this->localeDateMock); $this->model->setAttribute($this->attributeMock); } - public function testGetValue() + /** + * Test to retrieve attribute value + * + * @param string $frontendInput + * @param int $timeType + * @dataProvider getValueDataProvider + */ + public function testGetValue(string $frontendInput, int $timeType) { $attributeValue = '11-11-2011'; + $attributeCode = 'datetime'; $date = new \DateTime($attributeValue); - $object = new \Magento\Framework\DataObject(['datetime' => $attributeValue]); + $object = new DataObject([$attributeCode => $attributeValue]); $this->attributeMock->expects($this->any()) ->method('getAttributeCode') - ->willReturn('datetime'); + ->willReturn($attributeCode); $this->attributeMock->expects($this->any()) - ->method('getData') - ->with('frontend_input') - ->willReturn('text'); + ->method('getFrontendInput') + ->willReturn($frontendInput); $this->localeDateMock->expects($this->once()) ->method('formatDateTime') - ->with($date, \IntlDateFormatter::MEDIUM, \IntlDateFormatter::NONE, null, null, null) + ->with($date, \IntlDateFormatter::MEDIUM, $timeType) ->willReturn($attributeValue); $this->assertEquals($attributeValue, $this->model->getValue($object)); } + /** + * Retrieve attribute value data provider + * + * @return array + */ + public function getValueDataProvider(): array + { + return [ + ['frontendInput' => 'date', 'timeType' => \IntlDateFormatter::NONE], + ['frontendInput' => 'datetime', 'timeType' => \IntlDateFormatter::MEDIUM], + ]; + } + /** * @param mixed $labelText * @param string $attributeCode diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/TableTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/TableTest.php index b68446d22f910..2997874f6ea34 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/TableTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/Source/TableTest.php @@ -314,7 +314,10 @@ public function testAddValueSortToCollection() $attrOption->expects($this->once())->method('addOptionValueToCollection') ->with($collection, $this->abstractAttributeMock, $expr) ->willReturnSelf(); - $select->expects($this->once())->method('order')->with("{$attributeCode} {$dir}"); + $attrOption->expects($this->once())->method('addOptionToCollection') + ->with($collection, $this->abstractAttributeMock, $expr) + ->willReturnSelf(); + $select->expects($this->once())->method('order')->with("{$attributeCode}_order {$dir}"); $this->assertEquals($this->model, $this->model->addValueSortToCollection($collection, $dir)); } diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php index 402497a1379c0..ae4ae7ee707e3 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/AttributeTest.php @@ -5,14 +5,21 @@ */ namespace Magento\Eav\Test\Unit\Model\Entity; +use Magento\Eav\Model\Entity\Attribute; +use Magento\Eav\Model\Entity\Attribute\FrontendLabel; +use Magento\Eav\Model\Entity\Attribute\FrontendLabelFactory; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + /** - * Class AttributeTest. + * Test for EAV Entity attribute model */ -class AttributeTest extends \PHPUnit\Framework\TestCase +class AttributeTest extends TestCase { /** * Attribute model to be tested - * @var \Magento\Eav\Model\Entity\Attribute|\PHPUnit_Framework_MockObject_MockObject + * @var Attribute|MockObject */ protected $_model; @@ -21,7 +28,7 @@ class AttributeTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $this->_model = $this->createPartialMock(\Magento\Eav\Model\Entity\Attribute::class, ['__wakeup']); + $this->_model = $this->createPartialMock(Attribute::class, ['__wakeup']); } /** @@ -57,6 +64,7 @@ public static function dataGetBackendTypeByInput() ['image', 'text'], ['textarea', 'text'], ['date', 'datetime'], + ['datetime', 'datetime'], ['select', 'int'], ['boolean', 'int'], ['price', 'decimal'], @@ -91,6 +99,7 @@ public static function dataGetDefaultValueByInput() ['weight', 'default_value_text'], ['textarea', 'default_value_textarea'], ['date', 'default_value_date'], + ['datetime', 'default_value_datetime'], ['boolean', 'default_value_yesno'] ]; } @@ -130,7 +139,7 @@ public function testGetFrontendLabels() { $attributeId = 1; $storeLabels = ['test_attribute_store1']; - $frontendLabelFactory = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\FrontendLabelFactory::class) + $frontendLabelFactory = $this->getMockBuilder(FrontendLabelFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); @@ -142,15 +151,15 @@ public function testGetFrontendLabels() '_resource' => $resource, 'frontendLabelFactory' => $frontendLabelFactory, ]; - $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->_model = $objectManager->getObject(\Magento\Eav\Model\Entity\Attribute::class, $arguments); + $objectManager = new ObjectManager($this); + $this->_model = $objectManager->getObject(Attribute::class, $arguments); $this->_model->setAttributeId($attributeId); $resource->expects($this->once()) ->method('getStoreLabelsByAttributeId') ->with($attributeId) ->willReturn($storeLabels); - $frontendLabel = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\FrontendLabel::class) + $frontendLabel = $this->getMockBuilder(FrontendLabel::class) ->setMethods(['setStoreId', 'setLabel']) ->disableOriginalConstructor() ->getMock(); diff --git a/app/code/Magento/Eav/etc/adminhtml/system.xml b/app/code/Magento/Eav/etc/adminhtml/system.xml new file mode 100644 index 0000000000000..86916abe812d9 --- /dev/null +++ b/app/code/Magento/Eav/etc/adminhtml/system.xml @@ -0,0 +1,21 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd"> + <system> + <section id="dev"> + <group id="caching" translate="label" type="text" sortOrder="120" showInDefault="1" showInWebsite="0" showInStore="0"> + <label>Caching Settings</label> + <field id="cache_user_defined_attributes" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <label>Cache User Defined Attributes</label> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <comment>By default only system EAV attributes are cached.</comment> + </field> + </group> + </section> + </system> +</config> diff --git a/app/code/Magento/Eav/etc/config.xml b/app/code/Magento/Eav/etc/config.xml index 2a86437d96abe..c0a4287909b7d 100644 --- a/app/code/Magento/Eav/etc/config.xml +++ b/app/code/Magento/Eav/etc/config.xml @@ -20,5 +20,10 @@ </input_types> </validator_data> </general> + <dev> + <caching> + <cache_user_defined_attributes>0</cache_user_defined_attributes> + </caching> + </dev> </default> </config> diff --git a/app/code/Magento/Eav/etc/db_schema.xml b/app/code/Magento/Eav/etc/db_schema.xml index 407aa8976d684..8dc917b22d09f 100644 --- a/app/code/Magento/Eav/etc/db_schema.xml +++ b/app/code/Magento/Eav/etc/db_schema.xml @@ -458,10 +458,6 @@ <constraint xsi:type="foreign" referenceId="EAV_ATTRIBUTE_OPTION_VALUE_STORE_ID_STORE_STORE_ID" table="eav_attribute_option_value" column="store_id" referenceTable="store" referenceColumn="store_id" onDelete="CASCADE"/> - <constraint xsi:type="unique" referenceId="EAV_ATTRIBUTE_OPTION_VALUE_STORE_ID_OPTION_ID"> - <column name="store_id"/> - <column name="option_id"/> - </constraint> <index referenceId="EAV_ATTRIBUTE_OPTION_VALUE_OPTION_ID" indexType="btree"> <column name="option_id"/> </index> diff --git a/app/code/Magento/Eav/etc/db_schema_whitelist.json b/app/code/Magento/Eav/etc/db_schema_whitelist.json index 1814c7ba2dbc3..fbcba0741eadf 100644 --- a/app/code/Magento/Eav/etc/db_schema_whitelist.json +++ b/app/code/Magento/Eav/etc/db_schema_whitelist.json @@ -287,8 +287,7 @@ "constraint": { "PRIMARY": true, "EAV_ATTR_OPT_VAL_OPT_ID_EAV_ATTR_OPT_OPT_ID": true, - "EAV_ATTRIBUTE_OPTION_VALUE_STORE_ID_STORE_STORE_ID": true, - "EAV_ATTRIBUTE_OPTION_VALUE_STORE_ID_OPTION_ID": true + "EAV_ATTRIBUTE_OPTION_VALUE_STORE_ID_STORE_STORE_ID": true } }, "eav_attribute_label": { @@ -389,4 +388,4 @@ "EAV_FORM_ELEMENT_TYPE_ID_ATTRIBUTE_ID": true } } -} \ No newline at end of file +} diff --git a/app/code/Magento/Eav/registration.php b/app/code/Magento/Eav/registration.php index 6506d5cd06738..07b222f67f633 100644 --- a/app/code/Magento/Eav/registration.php +++ b/app/code/Magento/Eav/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Eav', __DIR__); diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php index 93f4caa10adf9..b9102bc5e00c4 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php @@ -108,21 +108,28 @@ public function testConnection() */ private function buildConfig($options = []) { - $host = preg_replace('/http[s]?:\/\//i', '', $options['hostname']); + $hostname = preg_replace('/http[s]?:\/\//i', '', $options['hostname']); // @codingStandardsIgnoreStart $protocol = parse_url($options['hostname'], PHP_URL_SCHEME); // @codingStandardsIgnoreEnd if (!$protocol) { $protocol = 'http'; } - if (!empty($options['port'])) { - $host .= ':' . $options['port']; + + $authString = ''; + if (!empty($options['enableAuth']) && (int)$options['enableAuth'] === 1) { + $authString = "{$options['username']}:{$options['password']}@"; } - if (!empty($options['enableAuth']) && ($options['enableAuth'] == 1)) { - $host = sprintf('%s://%s:%s@%s', $protocol, $options['username'], $options['password'], $host); + + $portString = ''; + if (!empty($options['port'])) { + $portString = ':' . $options['port']; } + $host = $protocol . '://' . $authString . $hostname . $portString; + $options['hosts'] = [$host]; + return $options; } diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/ProductDataMapper.php b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/ProductDataMapper.php index 270ca37e2d42c..abdb064506641 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/ProductDataMapper.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/ProductDataMapper.php @@ -77,6 +77,13 @@ class ProductDataMapper implements BatchDataMapperInterface 'tax_class_id' ]; + /** + * @var string[] + */ + private $sortableAttributesValuesToImplode = [ + 'name', + ]; + /** * Construction for DocumentDataMapper * @@ -86,6 +93,7 @@ class ProductDataMapper implements BatchDataMapperInterface * @param AdditionalFieldsProviderInterface $additionalFieldsProvider * @param DataProvider $dataProvider * @param array $excludedAttributes + * @param array $sortableAttributesValuesToImplode */ public function __construct( Builder $builder, @@ -93,12 +101,17 @@ public function __construct( DateFieldType $dateFieldType, AdditionalFieldsProviderInterface $additionalFieldsProvider, DataProvider $dataProvider, - array $excludedAttributes = [] + array $excludedAttributes = [], + array $sortableAttributesValuesToImplode = [] ) { $this->builder = $builder; $this->fieldMapper = $fieldMapper; $this->dateFieldType = $dateFieldType; $this->excludedAttributes = array_merge($this->defaultExcludedAttributes, $excludedAttributes); + $this->sortableAttributesValuesToImplode = array_merge( + $this->sortableAttributesValuesToImplode, + $sortableAttributesValuesToImplode + ); $this->additionalFieldsProvider = $additionalFieldsProvider; $this->dataProvider = $dataProvider; $this->attributeOptionsCache = []; @@ -241,6 +254,13 @@ private function prepareAttributeValues( } } + if ($attribute->getUsedForSortBy() + && in_array($attribute->getAttributeCode(), $this->sortableAttributesValuesToImplode) + && count($attributeValues) > 1 + ) { + $attributeValues = [$productId => implode(' ', $attributeValues)]; + } + return $attributeValues; } @@ -252,9 +272,14 @@ private function prepareAttributeValues( */ private function prepareMultiselectValues(array $values): array { - return \array_merge(...\array_map(function (string $value) { - return \explode(',', $value); - }, $values)); + return \array_merge( + ...\array_map( + function (string $value) { + return \explode(',', $value); + }, + $values + ) + ); } /** @@ -285,9 +310,9 @@ private function getValuesLabels(Attribute $attribute, array $attributeValues): return $attributeLabels; } - foreach ($options as $option) { - if (\in_array($option->getValue(), $attributeValues)) { - $attributeLabels[] = $option->getLabel(); + foreach ($attributeValues as $attributeValue) { + if (isset($options[$attributeValue])) { + $attributeLabels[] = $options[$attributeValue]->getLabel(); } } @@ -304,7 +329,11 @@ private function getAttributeOptions(Attribute $attribute): array { if (!isset($this->attributeOptionsCache[$attribute->getId()])) { $options = $attribute->getOptions() ?? []; - $this->attributeOptionsCache[$attribute->getId()] = $options; + $optionsByValue = []; + foreach ($options as $option) { + $optionsByValue[$option->getValue()] = $option; + } + $this->attributeOptionsCache[$attribute->getId()] = $optionsByValue; } return $this->attributeOptionsCache[$attribute->getId()]; diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php b/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php index f6d0a6318a5f2..fa193d86c03c7 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php @@ -193,6 +193,9 @@ public function addDocs(array $documents, $storeId, $mappedIndexerId) */ public function cleanIndex($storeId, $mappedIndexerId) { + // needed to fix bug with double indices in alias because of second reindex in same process + unset($this->preparedIndex[$storeId]); + $this->checkIndex($storeId, $mappedIndexerId, true); $indexName = $this->indexNameResolver->getIndexName($storeId, $mappedIndexerId, $this->preparedIndex); diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/StaticField.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/StaticField.php index 0f3020974d08a..348a1c708a78c 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/StaticField.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/StaticField.php @@ -132,11 +132,17 @@ public function getFields(array $context = []): array if ($attributeAdapter->isTextType()) { $keywordFieldName = FieldTypeConverterInterface::INTERNAL_DATA_TYPE_KEYWORD; + $index = $this->indexTypeConverter->convert( + IndexTypeConverterInterface::INTERNAL_NO_ANALYZE_VALUE + ); $allAttributes[$fieldName]['fields'][$keywordFieldName] = [ 'type' => $this->fieldTypeConverter->convert( FieldTypeConverterInterface::INTERNAL_DATA_TYPE_KEYWORD ) ]; + if ($index) { + $allAttributes[$fieldName]['fields'][$keywordFieldName]['index'] = $index; + } } if ($attributeAdapter->isComplexType()) { diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/FieldsMappingPreprocessorInterface.php b/app/code/Magento/Elasticsearch/Model/Adapter/FieldsMappingPreprocessorInterface.php new file mode 100644 index 0000000000000..6eea6560f7273 --- /dev/null +++ b/app/code/Magento/Elasticsearch/Model/Adapter/FieldsMappingPreprocessorInterface.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch\Model\Adapter; + +/** + * Modifies fields mapping before save + */ +interface FieldsMappingPreprocessorInterface +{ + /** + * Modifies fields mapping before save + * + * @param array $mapping + * @return array + */ + public function process(array $mapping): array; +} diff --git a/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php index f9b827304446d..d933d8bb5d0b5 100644 --- a/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php @@ -103,21 +103,28 @@ public function testConnection() */ private function buildConfig($options = []) { - $host = preg_replace('/http[s]?:\/\//i', '', $options['hostname']); + $hostname = preg_replace('/http[s]?:\/\//i', '', $options['hostname']); // @codingStandardsIgnoreStart $protocol = parse_url($options['hostname'], PHP_URL_SCHEME); // @codingStandardsIgnoreEnd if (!$protocol) { $protocol = 'http'; } - if (!empty($options['port'])) { - $host .= ':' . $options['port']; + + $authString = ''; + if (!empty($options['enableAuth']) && (int)$options['enableAuth'] === 1) { + $authString = "{$options['username']}:{$options['password']}@"; } - if (!empty($options['enableAuth']) && ($options['enableAuth'] == 1)) { - $host = sprintf('%s://%s:%s@%s', $protocol, $options['username'], $options['password'], $host); + + $portString = ''; + if (!empty($options['port'])) { + $portString = ':' . $options['port']; } + $host = $protocol . '://' . $authString . $hostname . $portString; + $options['hosts'] = [$host]; + return $options; } diff --git a/app/code/Magento/Elasticsearch/Model/Config/Backend/MinimumShouldMatch.php b/app/code/Magento/Elasticsearch/Model/Config/Backend/MinimumShouldMatch.php new file mode 100644 index 0000000000000..ea64ccd772682 --- /dev/null +++ b/app/code/Magento/Elasticsearch/Model/Config/Backend/MinimumShouldMatch.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch\Model\Config\Backend; + +use Magento\Framework\App\Config\Value; +use Magento\Framework\Exception\LocalizedException; + +/** + * Elasticsearch minimum should match data model + */ +class MinimumShouldMatch extends Value +{ + /** + * @inheritDoc + */ + public function beforeSave() + { + $result = parent::beforeSave(); + $this->validateValue(); + return $result; + } + + /** + * Validates config value + * + * @throws LocalizedException + */ + public function validateValue(): void + { + if (strlen($this->getValue()) && !preg_match('/^((\d+<)?-?\d+%?\s?)+$/', $this->getValue())) { + throw new LocalizedException( + __( + 'Value for the field "%1" was not saved because of the incorrect format.', + __('Minimum Terms to Match') + ) + ); + } + } +} diff --git a/app/code/Magento/Elasticsearch/Observer/CategoryProductIndexer.php b/app/code/Magento/Elasticsearch/Observer/CategoryProductIndexer.php index fd2734bb713b3..e2b3e18a0ffb9 100644 --- a/app/code/Magento/Elasticsearch/Observer/CategoryProductIndexer.php +++ b/app/code/Magento/Elasticsearch/Observer/CategoryProductIndexer.php @@ -11,6 +11,7 @@ use Magento\Elasticsearch\Model\Config; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; +use Magento\Catalog\Model\Indexer\Category\Flat\State as FlatState; /** * Checks if a category has changed products and depends on indexer configuration. @@ -27,14 +28,24 @@ class CategoryProductIndexer implements ObserverInterface */ private $processor; + /** + * @var FlatState + */ + private $flatState; + /** * @param Config $config * @param Processor $processor + * @param FlatState $flatState */ - public function __construct(Config $config, Processor $processor) - { + public function __construct( + Config $config, + Processor $processor, + FlatState $flatState + ) { $this->processor = $processor; $this->config = $config; + $this->flatState = $flatState; } /** @@ -47,7 +58,7 @@ public function execute(Observer $observer): void } $productIds = $observer->getEvent()->getProductIds(); - if (!empty($productIds) && $this->processor->isIndexerScheduled()) { + if (!empty($productIds) && $this->processor->isIndexerScheduled() && $this->flatState->isFlatEnabled()) { $this->processor->markIndexerAsInvalid(); } } diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php index ddf75c0a78e25..8a44b58d35fb8 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php @@ -7,6 +7,7 @@ use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ResolverInterface as TypeResolver; +use Magento\Elasticsearch\Model\Config; use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerPool; use Magento\Framework\App\ObjectManager; use Magento\Framework\Search\Request\Query\BoolExpression; @@ -50,6 +51,10 @@ class Match implements QueryInterface * @var ValueTransformerPool */ private $valueTransformerPool; + /** + * @var Config + */ + private $config; /** * @param FieldMapperInterface $fieldMapper @@ -57,13 +62,15 @@ class Match implements QueryInterface * @param AttributeProvider|null $attributeProvider * @param TypeResolver|null $fieldTypeResolver * @param ValueTransformerPool|null $valueTransformerPool + * @param Config|null $config */ public function __construct( FieldMapperInterface $fieldMapper, array $preprocessorContainer, AttributeProvider $attributeProvider = null, TypeResolver $fieldTypeResolver = null, - ValueTransformerPool $valueTransformerPool = null + ValueTransformerPool $valueTransformerPool = null, + Config $config = null ) { $this->fieldMapper = $fieldMapper; $this->preprocessorContainer = $preprocessorContainer; @@ -73,6 +80,7 @@ public function __construct( ->get(TypeResolver::class); $this->valueTransformerPool = $valueTransformerPool ?? ObjectManager::getInstance() ->get(ValueTransformerPool::class); + $this->config = $config ?? ObjectManager::getInstance()->get(Config::class); } /** @@ -83,11 +91,15 @@ public function build(array $selectQuery, RequestQueryInterface $requestQuery, $ $queryValue = $this->prepareQuery($requestQuery->getValue(), $conditionType); $queries = $this->buildQueries($requestQuery->getMatches(), $queryValue); $requestQueryBoost = $requestQuery->getBoost() ?: 1; + $minimumShouldMatch = $this->config->getElasticsearchConfigData('minimum_should_match'); foreach ($queries as $query) { $queryBody = $query['body']; $matchKey = isset($queryBody['match_phrase']) ? 'match_phrase' : 'match'; foreach ($queryBody[$matchKey] as $field => $matchQuery) { $matchQuery['boost'] = $requestQueryBoost + $matchQuery['boost']; + if ($minimumShouldMatch) { + $matchQuery['minimum_should_match'] = $minimumShouldMatch; + } $queryBody[$matchKey][$field] = $matchQuery; } $selectQuery['bool'][$query['condition']][] = $queryBody; diff --git a/app/code/Magento/Elasticsearch/Test/Mftf/Test/ProductQuickSearchUsingElasticSearchTest.xml b/app/code/Magento/Elasticsearch/Test/Mftf/Test/ProductQuickSearchUsingElasticSearchTest.xml index d8ce091fba76f..fb28e6dd4d9df 100644 --- a/app/code/Magento/Elasticsearch/Test/Mftf/Test/ProductQuickSearchUsingElasticSearchTest.xml +++ b/app/code/Magento/Elasticsearch/Test/Mftf/Test/ProductQuickSearchUsingElasticSearchTest.xml @@ -11,7 +11,7 @@ <test name="ProductQuickSearchUsingElasticSearchTest"> <annotations> <features value="Search"/> - <stories value="Quick Search of products on Storefront when ES 5.0+ is enabled"/> + <stories value="Quick Search of products on Storefront when ES 5.x is enabled"/> <title value="Product quick search doesn't throw exception after ES is chosen as search engine"/> <description value="Verify no elastic search exception is thrown when searching for product before catalogsearch reindexing"/> <severity value="CRITICAL"/> @@ -28,18 +28,18 @@ <after> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> <deleteData createDataKey="categoryFirst" stepKey="deleteCategory"/> - <actionGroup ref="ResetSearchEngineConfiguration" stepKey="resetCatalogSearchConfiguration"/> - <actionGroup ref="updateIndexerOnSave" stepKey="resetIndexerBackToOriginalState"> + <actionGroup ref="ResetSearchEngineConfigurationActionGroup" stepKey="resetCatalogSearchConfiguration"/> + <actionGroup ref="UpdateIndexerOnSaveActionGroup" stepKey="resetIndexerBackToOriginalState"> <argument name="indexerName" value="catalogsearch_fulltext"/> </actionGroup> <actionGroup ref="logout" stepKey="logoutOfAdmin"/> </after> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <comment userInput="Change Catalog search engine option to Elastic Search 5.0+" stepKey="chooseElasticSearch5"/> - <actionGroup ref="ChooseElasticSearchAsSearchEngine" stepKey="chooseES5"/> + <comment userInput="Change Catalog search engine option to Elastic Search 5.x" stepKey="chooseElasticSearch5"/> + <actionGroup ref="ChooseElasticSearchAsSearchEngineActionGroup" stepKey="chooseES5"/> <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearing"/> - <actionGroup ref="updateIndexerBySchedule" stepKey="updateAnIndexerBySchedule"> + <actionGroup ref="UpdateIndexerByScheduleActionGroup" stepKey="updateAnIndexerBySchedule"> <argument name="indexerName" value="catalogsearch_fulltext"/> </actionGroup> <actionGroup ref="logout" stepKey="logoutOfAdmin"/> @@ -57,4 +57,4 @@ <comment userInput="End of searching products" stepKey="endOfSearchingProducts"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin2"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php index 5f5807e212961..99fd416b5cd3e 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php @@ -3,8 +3,10 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Elasticsearch\Test\Unit\Elasticsearch5\Model\Client; +use Magento\Elasticsearch\Elasticsearch5\Model\Client\Elasticsearch; use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; @@ -38,7 +40,7 @@ class ElasticsearchTest extends \PHPUnit\Framework\TestCase * * @return void */ - protected function setUp() + protected function setUp(): void { $this->elasticsearchClientMock = $this->getMockBuilder(\Elasticsearch\Client::class) ->setMethods( @@ -497,6 +499,40 @@ public function testDeleteMapping() ); } + /** + * Ensure that configuration returns correct url. + * + * @param array $options + * @param string $expectedResult + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \ReflectionException + * @dataProvider getOptionsDataProvider + */ + public function testBuildConfig(array $options, $expectedResult): void + { + $buildConfig = new Elasticsearch($options); + $config = $this->getPrivateMethod(Elasticsearch::class, 'buildConfig'); + $result = $config->invoke($buildConfig, $options); + $this->assertEquals($expectedResult, $result['hosts'][0]); + } + + /** + * Return private method for elastic search class. + * + * @param $className + * @param $methodName + * @return \ReflectionMethod + * @throws \ReflectionException + */ + private function getPrivateMethod($className, $methodName) + { + $reflector = new \ReflectionClass($className); + $method = $reflector->getMethod($methodName); + $method->setAccessible(true); + + return $method; + } + /** * Test deleteMapping() method * @expectedException \Exception @@ -545,6 +581,35 @@ public function testSuggest() $this->assertEquals([], $this->model->suggest($query)); } + /** + * Get options data provider. + */ + public function getOptionsDataProvider() + { + return [ + [ + 'without_protocol' => [ + 'hostname' => 'localhost', + 'port' => '9200', + 'timeout' => 15, + 'index' => 'magento2', + 'enableAuth' => 0, + ], + 'expected_result' => 'http://localhost:9200' + ], + [ + 'with_protocol' => [ + 'hostname' => 'https://localhost', + 'port' => '9200', + 'timeout' => 15, + 'index' => 'magento2', + 'enableAuth' => 0, + ], + 'expected_result' => 'https://localhost:9200' + ] + ]; + } + /** * Get elasticsearch client options * diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/StaticFieldTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/StaticFieldTest.php index f90c13c9bfb65..c08a4298b42d2 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/StaticFieldTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/StaticFieldTest.php @@ -264,6 +264,7 @@ public function attributeProvider() 'fields' => [ 'keyword' => [ 'type' => 'string', + 'index' => 'not_analyzed' ] ] ], @@ -294,6 +295,7 @@ public function attributeProvider() 'fields' => [ 'keyword' => [ 'type' => 'string', + 'index' => 'not_analyzed' ] ] ], diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Config/Backend/MinimumShouldMatchTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Config/Backend/MinimumShouldMatchTest.php new file mode 100644 index 0000000000000..e8f3621eda25d --- /dev/null +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/Config/Backend/MinimumShouldMatchTest.php @@ -0,0 +1,75 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch\Test\Unit\Model\Config\Backend; + +use Magento\Elasticsearch\Model\Config\Backend\MinimumShouldMatch; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; +use Throwable; + +/** + * Test elasticsearch minimum should match data model + */ +class MinimumShouldMatchTest extends TestCase +{ + /** + * @var MinimumShouldMatch + */ + private $model; + + /** + * @inheritDoc + */ + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject(MinimumShouldMatch::class); + parent::setUp(); + } + + /** + * @param string $value + * @param bool $valid + * @dataProvider validateValueDataProvider + * @throws LocalizedException + */ + public function testValidateValue(string $value, bool $valid) + { + $this->model->setValue($value); + try { + $this->model->validateValue(); + } catch (Throwable $exception) { + $this->assertFalse($valid); + return; + } + $this->assertTrue($valid); + } + + /** + * @return array + */ + public function validateValueDataProvider(): array + { + return [ + ['3', true], + ['-2', true], + ['75%', true], + ['-25%', true], + ['3<90%', true], + ['2<-25% 9<-3', true], + ['90%<3', false], + ['<90%', false], + ['90%<', false], + ['-3<2', false], + ['two', false], + ['2<', false], + ['<2', false], + ]; + } +} diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Observer/CategoryProductIndexerTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Observer/CategoryProductIndexerTest.php index adebee0d591ab..944699908b984 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Observer/CategoryProductIndexerTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Observer/CategoryProductIndexerTest.php @@ -69,7 +69,6 @@ public function testExecuteIfCategoryHasChangedProducts() { $this->getProductIdsWithEnabledElasticSearch(); $this->processorMock->expects($this->once())->method('isIndexerScheduled')->willReturn(true); - $this->processorMock->expects($this->once())->method('markIndexerAsInvalid'); $this->observer->execute($this->observerMock); } 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 d0ffc6debcd8a..705b06e9769f2 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 @@ -9,14 +9,19 @@ use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ResolverInterface as TypeResolver; use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; +use Magento\Elasticsearch\Model\Config; 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; +use PHPUnit\Framework\TestCase; -class MatchTest extends \PHPUnit\Framework\TestCase +/** + * Test Match query builder + */ +class MatchTest extends TestCase { /** * @var AttributeProvider|MockObject @@ -32,6 +37,14 @@ class MatchTest extends \PHPUnit\Framework\TestCase * @var MatchQueryBuilder */ private $matchQueryBuilder; + /** + * @var MockObject + */ + private $config; + /** + * @var MockObject + */ + private $fieldMapper; /** * @inheritdoc @@ -40,22 +53,25 @@ protected function setUp() { $this->attributeProvider = $this->createMock(AttributeProvider::class); $this->fieldTypeResolver = $this->createMock(TypeResolver::class); - + $this->config = $this->createMock(Config::class); + $this->fieldMapper = $this->getMockForAbstractClass(FieldMapperInterface::class); + $this->fieldMapper->method('getFieldName') + ->willReturnArgument(0); $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(), + 'fieldMapper' => $this->fieldMapper, 'preprocessorContainer' => [], 'attributeProvider' => $this->attributeProvider, 'fieldTypeResolver' => $this->fieldTypeResolver, 'valueTransformerPool' => $valueTransformerPoolMock, + 'config' => $this->config, ] ); } @@ -63,130 +79,146 @@ protected function setUp() /** * Tests that method constructs a correct select query. * - * @see MatchQueryBuilder::build + * @param string $searchQuery + * @param array $fields + * @param array $expected + * @param string|null $minimumShouldMatch + * @dataProvider buildDataProvider */ - public function testBuild() - { - $attributeAdapter = $this->createMock(AttributeAdapter::class); - $this->attributeProvider->expects($this->once()) - ->method('getByAttributeCode') - ->with('some_field') - ->willReturn($attributeAdapter); - $this->fieldTypeResolver->expects($this->once()) - ->method('getFieldType') - ->with($attributeAdapter) - ->willReturn('text'); - - $rawQueryValue = 'query_value'; - $selectQuery = $this->matchQueryBuilder->build([], $this->getMatchRequestQuery($rawQueryValue), 'not'); + public function testBuild( + string $searchQuery, + array $fields, + array $expected, + ?string $minimumShouldMatch = null + ) { + $this->config->method('getElasticsearchConfigData') + ->with('minimum_should_match') + ->willReturn($minimumShouldMatch); + + foreach ($fields as $field) { + $this->mockAttribute($field['field']); + } + + $requestQuery = new MatchRequestQuery('match', $searchQuery, 1, $fields); + $query = $this->matchQueryBuilder->build([], $requestQuery, 'should'); $expectedSelectQuery = [ 'bool' => [ - 'must_not' => [ + 'should' => $expected, + ], + ]; + + $this->assertEquals( + $expectedSelectQuery, + $query + ); + } + + /** + * @return array + */ + public function buildDataProvider(): array + { + return [ + 'match query without minimum_should_match' => [ + 'fitness bottle', + [ + [ + 'field' => 'name', + 'boost' => 5 + ] + ], + [ + [ + 'match' => [ + 'name' => [ + 'query' => 'fitness bottle', + 'boost' => 6, + ], + ], + ], + ] + ], + 'match_phrase query without minimum_should_match' => [ + '"fitness bottle"', + [ + [ + 'field' => 'name', + 'boost' => 5 + ] + ], + [ + [ + 'match_phrase' => [ + 'name' => [ + 'query' => 'fitness bottle', + 'boost' => 6, + ], + ], + ], + ] + ], + 'match query with minimum_should_match' => [ + 'fitness bottle', + [ + [ + 'field' => 'name', + 'boost' => 5 + ] + ], + [ [ 'match' => [ - 'some_field' => [ - 'query' => $rawQueryValue, - 'boost' => 43, + 'name' => [ + 'query' => 'fitness bottle', + 'boost' => 6, + 'minimum_should_match' => '2<75%', ], ], ], ], + '2<75%' ], + 'match_phrase query with minimum_should_match' => [ + '"fitness bottle"', + [ + [ + 'field' => 'name', + 'boost' => 5 + ] + ], + [ + [ + 'match_phrase' => [ + 'name' => [ + 'query' => 'fitness bottle', + 'boost' => 6, + 'minimum_should_match' => '2<75%', + ], + ], + ], + ], + '2<75%' + ] ]; - $this->assertEquals($expectedSelectQuery, $selectQuery); } /** - * Tests that method constructs a correct "match" query depending on query value. - * - * @dataProvider matchProvider + * Mock attribute * - * @param string $rawQueryValue - * @param string $queryValue - * @param string $match + * @param string $attributeCode + * @param string $type */ - public function testBuildMatchQuery($rawQueryValue, $queryValue, $match) + private function mockAttribute(string $attributeCode, string $type = 'text') { $attributeAdapter = $this->createMock(AttributeAdapter::class); $this->attributeProvider->expects($this->once()) ->method('getByAttributeCode') - ->with('some_field') + ->with($attributeCode) ->willReturn($attributeAdapter); $this->fieldTypeResolver->expects($this->once()) ->method('getFieldType') ->with($attributeAdapter) - ->willReturn('text'); - - $query = $this->matchQueryBuilder->build([], $this->getMatchRequestQuery($rawQueryValue), 'should'); - - $expectedSelectQuery = [ - 'bool' => [ - 'should' => [ - [ - $match => [ - 'some_field' => [ - 'query' => $queryValue, - 'boost' => 43, - ], - ], - ], - ], - ], - ]; - - $this->assertEquals( - $expectedSelectQuery, - $query, - sprintf('Wrong "match" query. Should be processed with "%s"', $match) - ); - } - - /** - * @return array - */ - public function matchProvider() - { - return [ - ['query_value', 'query_value', 'match'], - ['"query value"', 'query value', 'match_phrase'], - ]; - } - - /** - * Gets fieldMapper mock object. - * - * @return FieldMapperInterface|MockObject - */ - private function getFieldMapper() - { - $fieldMapper = $this->getMockBuilder(FieldMapperInterface::class) - ->getMockForAbstractClass(); - - $fieldMapper->method('getFieldName') - ->with('some_field', ['type' => FieldMapperInterface::TYPE_QUERY]) - ->willReturnArgument(0); - - return $fieldMapper; - } - - /** - * Gets RequestQuery mock object. - * - * @param string $rawQueryValue - * @return MatchRequestQuery|MockObject - */ - private function getMatchRequestQuery($rawQueryValue) - { - $matchRequestQuery = $this->getMockBuilder(MatchRequestQuery::class) - ->disableOriginalConstructor() - ->getMock(); - - $matchRequestQuery->method('getValue') - ->willReturn($rawQueryValue); - $matchRequestQuery->method('getMatches') - ->willReturn([['field' => 'some_field', 'boost' => 42]]); - - return $matchRequestQuery; + ->willReturn($type); } } diff --git a/app/code/Magento/Elasticsearch/composer.json b/app/code/Magento/Elasticsearch/composer.json index 05d3ce94f02f9..3cf5444d86223 100644 --- a/app/code/Magento/Elasticsearch/composer.json +++ b/app/code/Magento/Elasticsearch/composer.json @@ -12,7 +12,7 @@ "magento/module-store": "*", "magento/module-catalog-inventory": "*", "magento/framework": "*", - "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1" + "elasticsearch/elasticsearch": "~2.0||~5.1||~6.1" }, "suggest": { "magento/module-config": "*" diff --git a/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml b/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml index dd42b408ff75e..7e3c83dae9847 100644 --- a/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml +++ b/app/code/Magento/Elasticsearch/etc/adminhtml/system.xml @@ -63,7 +63,15 @@ <field id="engine">elasticsearch</field> </depends> </field> - <!-- Elasticsearch 5.0+ --> + <field id="elasticsearch_minimum_should_match" translate="label" type="text" sortOrder="93" showInDefault="1" showInWebsite="0" showInStore="0"> + <label>Minimum Terms to Match</label> + <depends> + <field id="engine">elasticsearch</field> + </depends> + <comment><![CDATA[<a href="https://docs.magento.com/m2/ce/user_guide/catalog/search-elasticsearch.html">Learn more</a> about valid syntax.]]></comment> + <backend_model>Magento\Elasticsearch\Model\Config\Backend\MinimumShouldMatch</backend_model> + </field> + <!-- Elasticsearch 5.x --> <field id="elasticsearch5_server_hostname" translate="label" type="text" sortOrder="61" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Elasticsearch Server Hostname</label> <depends> @@ -117,6 +125,14 @@ <field id="engine">elasticsearch5</field> </depends> </field> + <field id="elasticsearch5_minimum_should_match" translate="label" type="text" sortOrder="93" showInDefault="1" showInWebsite="0" showInStore="0"> + <label>Minimum Terms to Match</label> + <depends> + <field id="engine">elasticsearch5</field> + </depends> + <comment><![CDATA[<a href="https://docs.magento.com/m2/ce/user_guide/catalog/search-elasticsearch.html">Learn more</a> about valid syntax.]]></comment> + <backend_model>Magento\Elasticsearch\Model\Config\Backend\MinimumShouldMatch</backend_model> + </field> </group> </section> </system> diff --git a/app/code/Magento/Elasticsearch/etc/config.xml b/app/code/Magento/Elasticsearch/etc/config.xml index 0e01aba5ed857..9df21978b5414 100644 --- a/app/code/Magento/Elasticsearch/etc/config.xml +++ b/app/code/Magento/Elasticsearch/etc/config.xml @@ -14,12 +14,14 @@ <elasticsearch_index_prefix>magento2</elasticsearch_index_prefix> <elasticsearch_enable_auth>0</elasticsearch_enable_auth> <elasticsearch_server_timeout>15</elasticsearch_server_timeout> + <elasticsearch_minimum_should_match></elasticsearch_minimum_should_match> <elasticsearch5_server_hostname>localhost</elasticsearch5_server_hostname> <elasticsearch5_server_port>9200</elasticsearch5_server_port> <elasticsearch5_index_prefix>magento2</elasticsearch5_index_prefix> <elasticsearch5_enable_auth>0</elasticsearch5_enable_auth> <elasticsearch5_server_timeout>15</elasticsearch5_server_timeout> + <elasticsearch5_minimum_should_match></elasticsearch5_minimum_should_match> </search> </catalog> </default> diff --git a/app/code/Magento/Elasticsearch/etc/di.xml b/app/code/Magento/Elasticsearch/etc/di.xml index f42d957276d76..2b29e53635e3a 100644 --- a/app/code/Magento/Elasticsearch/etc/di.xml +++ b/app/code/Magento/Elasticsearch/etc/di.xml @@ -169,7 +169,7 @@ <arguments> <argument name="engines" xsi:type="array"> <item name="elasticsearch" xsi:type="string">Elasticsearch</item> - <item name="elasticsearch5" xsi:type="string">Elasticsearch 5.0+</item> + <item name="elasticsearch5" xsi:type="string">Elasticsearch 5.x</item> </argument> </arguments> </type> diff --git a/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/AddDefaultSearchField.php b/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/AddDefaultSearchField.php new file mode 100644 index 0000000000000..27767f6567d96 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/AddDefaultSearchField.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch6\Model\Adapter\FieldMapper; + +use Magento\Elasticsearch\Model\Adapter\FieldsMappingPreprocessorInterface; + +/** + * Add default search field (catch all field) to the mapping. + */ +class AddDefaultSearchField implements FieldsMappingPreprocessorInterface +{ + /** + * catch all field name + */ + private const NAME = '_search'; + /** + * Add default search field (catch all field) to the fields. + * + * Emulates catch all field (_all) for elasticsearch version 6.0+ + * + * @param array $mapping + * @return array + */ + public function process(array $mapping): array + { + return [self::NAME => ['type' => 'text']] + $mapping; + } +} diff --git a/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchField.php b/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchField.php new file mode 100644 index 0000000000000..6179eacba5ad7 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchField.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch6\Model\Adapter\FieldMapper; + +use Magento\Elasticsearch\Model\Adapter\FieldsMappingPreprocessorInterface; + +/** + * Add "copy_to" parameter for default search field to index fields. + */ +class CopySearchableFieldsToSearchField implements FieldsMappingPreprocessorInterface +{ + /** + * List of field types to copy + */ + private const FIELD_TYPES = ['text', 'keyword']; + /** + * Add "copy_to" parameter for default search field to index fields. + * + * Emulates catch all field (_all) for elasticsearch version 6.0+ + * + * @param array $mapping + * @return array + */ + public function process(array $mapping): array + { + foreach ($mapping as $field => $definition) { + if ($this->isSearchable($definition)) { + $definition['copy_to'][] = '_search'; + $mapping[$field] = $definition; + } + } + return $mapping; + } + + /** + * Determine if the field is searchable by mapping + * + * The field is searchable if it's indexed and its mapping type is either "text" or "keyword" + * + * @param array $mapping + * @return bool + */ + private function isSearchable(array $mapping): bool + { + return in_array($mapping['type'] ?? null, self::FIELD_TYPES) && (($mapping['index'] ?? true) !== false); + } +} diff --git a/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php index 34129a5af0012..ff299be786aa8 100644 --- a/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php @@ -5,8 +5,9 @@ */ namespace Magento\Elasticsearch6\Model\Client; -use Magento\Framework\Exception\LocalizedException; use Magento\AdvancedSearch\Model\Client\ClientInterface; +use Magento\Elasticsearch\Model\Adapter\FieldsMappingPreprocessorInterface; +use Magento\Framework\Exception\LocalizedException; /** * Elasticsearch client @@ -29,17 +30,23 @@ class Elasticsearch implements ClientInterface * @var bool */ private $pingResult; + /** + * @var FieldsMappingPreprocessorInterface[] + */ + private $fieldsMappingPreprocessors; /** * Initialize Elasticsearch Client * * @param array $options * @param \Elasticsearch\Client|null $elasticsearchClient + * @param FieldsMappingPreprocessorInterface[] $fieldsMappingPreprocessors * @throws LocalizedException */ public function __construct( $options = [], - $elasticsearchClient = null + $elasticsearchClient = null, + $fieldsMappingPreprocessors = [] ) { if (empty($options['hostname']) || ((!empty($options['enableAuth']) && ($options['enableAuth'] == 1)) && (empty($options['username']) || empty($options['password'])))) { @@ -54,6 +61,17 @@ public function __construct( } $this->client[getmypid()] = $elasticsearchClient; $this->clientOptions = $options; + foreach ($fieldsMappingPreprocessors as $preprocessor) { + if (!$preprocessor instanceof FieldsMappingPreprocessorInterface) { + throw new \InvalidArgumentException( + sprintf( + 'Instance of FieldsMappingPreprocessorInterface is expected, got %s instead.', + get_class($preprocessor) + ) + ); + } + } + $this->fieldsMappingPreprocessors = $fieldsMappingPreprocessors; } /** @@ -103,21 +121,28 @@ public function testConnection() */ private function buildConfig($options = []) { - $host = preg_replace('/http[s]?:\/\//i', '', $options['hostname']); + $hostname = preg_replace('/http[s]?:\/\//i', '', $options['hostname']); // @codingStandardsIgnoreStart $protocol = parse_url($options['hostname'], PHP_URL_SCHEME); // @codingStandardsIgnoreEnd if (!$protocol) { $protocol = 'http'; } - if (!empty($options['port'])) { - $host .= ':' . $options['port']; + + $authString = ''; + if (!empty($options['enableAuth']) && (int)$options['enableAuth'] === 1) { + $authString = "{$options['username']}:{$options['password']}@"; } - if (!empty($options['enableAuth']) && ($options['enableAuth'] == 1)) { - $host = sprintf('%s://%s:%s@%s', $protocol, $options['username'], $options['password'], $host); + + $portString = ''; + if (!empty($options['port'])) { + $portString = ':' . $options['port']; } + $host = $protocol . '://' . $authString . $hostname . $portString; + $options['hosts'] = [$host]; + return $options; } @@ -249,11 +274,7 @@ public function addFieldsMapping(array $fields, $index, $entityType) 'type' => $entityType, 'body' => [ $entityType => [ - 'properties' => [ - '_search' => [ - 'type' => 'text' - ], - ], + 'properties' => [], 'dynamic_templates' => [ [ 'price_mapping' => [ @@ -291,7 +312,7 @@ public function addFieldsMapping(array $fields, $index, $entityType) ], ]; - foreach ($fields as $field => $fieldInfo) { + foreach ($this->applyFieldsMappingPreprocessors($fields) as $field => $fieldInfo) { $params['body'][$entityType]['properties'][$field] = $fieldInfo; } @@ -336,4 +357,18 @@ public function suggest($query) { return $this->getClient()->suggest($query); } + + /** + * Apply fields mapping preprocessors + * + * @param array $properties + * @return array + */ + private function applyFieldsMappingPreprocessors(array $properties): array + { + foreach ($this->fieldsMappingPreprocessors as $preprocessor) { + $properties = $preprocessor->process($properties); + } + return $properties; + } } diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/ActionGroup/AdminElasticConnectionTestActionGroup.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/ActionGroup/AdminElasticConnectionTestActionGroup.xml new file mode 100644 index 0000000000000..d00c1c59a0f8d --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/ActionGroup/AdminElasticConnectionTestActionGroup.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="AdminElasticConnectionTestActionGroup"> + <annotations> + <description>Check ElasticSearch connection after enabling.</description> + </annotations> + <amOnPage url="{{AdminCatalogSearchConfigurationPage.url}}" stepKey="openAdminCatalogSearchConfigPage"/> + <waitForPageLoad stepKey="waitPageToLoad"/> + <conditionalClick selector="{{AdminCatalogSearchConfigurationSection.catalogSearchTab}}" dependentSelector="{{AdminCatalogSearchConfigurationSection.elastic6ConnectionWizard}}" visible="false" stepKey="expandCatalogSearchTab"/> + <waitForElementVisible selector="{{AdminCatalogSearchConfigurationSection.elastic6ConnectionWizard}}" stepKey="waitForConnectionButton"/> + <click selector="{{AdminCatalogSearchConfigurationSection.elastic6ConnectionWizard}}" stepKey="clickOnTestConnectionButton"/> + <waitForPageLoad stepKey="waitForConnectionEstablishment"/> + <grabTextFrom selector="{{AdminCatalogSearchConfigurationSection.connectionStatus}}" stepKey="grabConnectionStatus"/> + <assertEquals expected="{{AdminElasticsearch6TestConnectionMessageData.successMessage}}" expectedType="string" actual="$grabConnectionStatus" stepKey="assertThatConnectionSuccessful"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Data/AdminElasticSearch6MessagesData.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Data/AdminElasticSearch6MessagesData.xml new file mode 100644 index 0000000000000..3fa0ef9fd1c28 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Data/AdminElasticSearch6MessagesData.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="AdminElasticsearch6TestConnectionMessageData"> + <data key="successMessage">Successful! Test again?</data> + </entity> +</entities> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Data/ConfigData.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Data/ConfigData.xml index f1f2f39f4457b..03a1c63f71515 100644 --- a/app/code/Magento/Elasticsearch6/Test/Mftf/Data/ConfigData.xml +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Data/ConfigData.xml @@ -10,7 +10,7 @@ <entity name="SearchEngineElasticsearchConfigData"> <data key="path">catalog/search/engine</data> <data key="scope_id">1</data> - <data key="label">Elasticsearch 6.0+</data> + <data key="label">Elasticsearch 6.x</data> <data key="value">elasticsearch6</data> </entity> </entities> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Data/Elasticsearch6ConfigData.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Data/Elasticsearch6ConfigData.xml new file mode 100644 index 0000000000000..7a61f13e62049 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Data/Elasticsearch6ConfigData.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="EnableElasticSearch6Config"> + <data key="path">catalog/search/engine</data> + <data key="value">elasticsearch6</data> + </entity> +</entities> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Section/AdminCatalogSearchConfigurationSection.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Section/AdminCatalogSearchConfigurationSection.xml new file mode 100644 index 0000000000000..a6f35606ed79b --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Section/AdminCatalogSearchConfigurationSection.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="AdminCatalogSearchConfigurationSection"> + <element name="elastic6ConnectionWizard" type="button" selector="#catalog_search_elasticsearch6_test_connect_wizard"/> + <element name="connectionStatus" type="text" selector="#catalog_search_elasticsearch6_test_connect_wizard_result"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml new file mode 100644 index 0000000000000..fd18a0f4e1e5e --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticSearchForChineseLocaleTest.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontElasticSearch6ForChineseLocaleTest"> + <annotations> + <features value="Elasticsearch6"/> + <stories value="Elasticsearch6 for Chinese"/> + <title value="Elastic search for Chinese locale"/> + <description value="Elastic search for Chinese locale"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-6310"/> + <useCaseId value="MAGETWO-91625"/> + <group value="elasticsearch"/> + </annotations> + <before> + <!-- Set search engine to Elastic 6, set Locale to China, create category and product, then go to Storefront --> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <magentoCLI command="config:set --scope={{GeneralLocalCodeConfigsForChina.scope}} --scope-code={{GeneralLocalCodeConfigsForChina.scope_code}} {{GeneralLocalCodeConfigsForChina.path}} {{GeneralLocalCodeConfigsForChina.value}}" stepKey="setLocaleToChina"/> + <magentoCLI command="config:set {{EnableElasticSearch6Config.path}} {{EnableElasticSearch6Config.value}}" stepKey="enableElasticsearch6"/> + <actionGroup ref="AdminElasticConnectionTestActionGroup" stepKey="checkConnection"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> + </before> + <after> + <!-- Delete created data and reset initial configuration --> + <magentoCLI command="config:set --scope={{GeneralLocalCodeConfigsForUS.scope}} --scope-code={{GeneralLocalCodeConfigsForUS.scope_code}} {{GeneralLocalCodeConfigsForUS.path}} {{GeneralLocalCodeConfigsForUS.value}}" stepKey="setLocaleToUS"/> + <magentoCLI command="config:set {{SetDefaultSearchEngineConfig.path}} {{SetDefaultSearchEngineConfig.value}}" stepKey="resetSearchEnginePreviousState"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + </after> + <!-- Search for product by name --> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchByProductName"> + <argument name="phrase" value="$$createProduct.name$$"/> + </actionGroup> + <!-- Check if searched product is displayed --> + <see selector="{{StorefrontCategoryMainSection.productName}}" userInput="$$createProduct.name$$" stepKey="seeProductNameInCategoryPage"/> + </test> +</tests> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml new file mode 100644 index 0000000000000..050ce1263d10d --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontElasticsearch6SearchInvalidValueTest.xml @@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontElasticsearch6SearchInvalidValueTest"> + <annotations> + <features value="Elasticsearch6"/> + <stories value="Search Product on Storefront"/> + <title value="Elasticsearch: try to search by invalid value of 'Searchable' attribute"/> + <description value="Elasticsearch: try to search by invalid value of 'Searchable' attribute"/> + <severity value="MAJOR"/> + <testCaseId value="MC-17906"/> + <useCaseId value="MC-15759"/> + <group value="elasticsearch"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Create category--> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <!--Set Minimal Query Length--> + <magentoCLI command="config:set {{SetMinQueryLength2Config.path}} {{SetMinQueryLength2Config.value}}" stepKey="setMinQueryLength"/> + <!--Reindex indexes and clear cache--> + <magentoCLI command="indexer:reindex catalogsearch_fulltext" stepKey="reindex"/> + <magentoCLI command="cache:flush config" stepKey="flushCache"/> + </before> + <after> + <!--Set configs to default--> + <magentoCLI command="config:set {{SetMinQueryLength3Config.path}} {{SetMinQueryLength3Config.value}}" stepKey="setMinQueryLengthPreviousState"/> + <!--Delete created data--> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="OpenProductAttributeFromSearchResultInGridActionGroup" stepKey="openProductAttributeFromSearchResultInGrid"> + <argument name="productAttributeCode" value="{{textProductAttribute.attribute_code}}"/> + </actionGroup> + <actionGroup ref="DeleteProductAttributeByAttributeCodeActionGroup" stepKey="deleteProductAttributeByAttributeCode"> + <argument name="productAttributeCode" value="{{textProductAttribute.attribute_code}}"/> + </actionGroup> + <actionGroup ref="AssertProductAttributeRemovedSuccessfullyActionGroup" stepKey="deleteProductAttributeSuccess"/> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> + <waitForPageLoad stepKey="waitForAttributePageLoad"/> + <click selector="{{AdminProductAttributeGridSection.ResetFilter}}" stepKey="resetFiltersOnGrid"/> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="goToProductCatalog"/> + <waitForPageLoad stepKey="waitForProductIndexPage"/> + <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteProduct"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetFiltersIfExist"/> + <magentoCLI command="indexer:reindex catalogsearch_fulltext" stepKey="reindex"/> + <magentoCLI command="cache:flush config" stepKey="flushCache"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!--Create new searchable product attribute--> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + <actionGroup ref="AdminCreateSearchableProductAttributeActionGroup" stepKey="createAttribute"> + <argument name="attribute" value="textProductAttribute"/> + </actionGroup> + <!--Assign attribute to the Default set--> + <actionGroup ref="AdminOpenAttributeSetGridPageActionGroup" stepKey="openAttributeSetPage"/> + <actionGroup ref="AdminOpenAttributeSetByNameActionGroup" stepKey="openDefaultAttributeSet"/> + <actionGroup ref="AssignAttributeToGroupActionGroup" stepKey="assignAttributeToGroup"> + <argument name="group" value="Product Details"/> + <argument name="attribute" value="{{textProductAttribute.attribute_code}}"/> + </actionGroup> + <actionGroup ref="SaveAttributeSetActionGroup" stepKey="saveAttributeSet"/> + <!--Create product and fill new attribute field--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPage"/> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> + <argument name="product" value="SimpleProduct"/> + </actionGroup> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillProductForm"> + <argument name="product" value="ProductWithSpecialSymbols"/> + </actionGroup> + <actionGroup ref="SetCategoryByNameActionGroup" stepKey="addCategoryToProduct"> + <argument name="categoryName" value="$$createCategory.name$$"/> + </actionGroup> + <fillField selector="{{AdminProductFormSection.attributeRequiredInput(textProductAttribute.attribute_code)}}" userInput="searchable" stepKey="fillTheAttributeRequiredInputField"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> + <!-- TODO: REMOVE AFTER FIX MC-21717 --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush eav" stepKey="flushCache"/> + <!--Assert search results on storefront--> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefrontPage"/> + <waitForPageLoad stepKey="waitForStorefrontPageLoad"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchForFirstSearchTerm"> + <argument name="phrase" value="?searchable;"/> + </actionGroup> + <see selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{ProductWithSpecialSymbols.name}}" stepKey="seeProductName"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchProductForSecondSearchTerm"> + <argument name="phrase" value="? searchable ;"/> + </actionGroup> + <see selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{ProductWithSpecialSymbols.name}}" stepKey="seeProductNameSecondTime"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchForSecondSearchTerm"> + <argument name="phrase" value="?;"/> + </actionGroup> + <actionGroup ref="StorefrontCheckSearchIsEmptyActionGroup" stepKey="checkEmptyForSecondSearchTerm"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchProductForWithSpecialSymbols"> + <argument name="phrase" value="?{{ProductWithSpecialSymbols.name}};"/> + </actionGroup> + <see selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{ProductWithSpecialSymbols.name}}" stepKey="seeProductWithSpecialSymbols"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchProductForWithSpecialSymbolsSecondTime"> + <argument name="phrase" value="? {{ProductWithSpecialSymbols.name}} ;"/> + </actionGroup> + <see selector="{{StorefrontCategoryMainSection.productName}}" userInput="{{ProductWithSpecialSymbols.name}}" stepKey="seeProductWithSpecialSymbolsSecondTime"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchForThirdSearchTerm"> + <argument name="phrase" value="?anythingcangobetween;"/> + </actionGroup> + <actionGroup ref="StorefrontCheckSearchIsEmptyActionGroup" stepKey="checkEmptyForThirdSearchTerm"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchForForthSearchTerm"> + <argument name="phrase" value="? anything at all ;"/> + </actionGroup> + <actionGroup ref="StorefrontCheckSearchIsEmptyActionGroup" stepKey="checkEmptyForForthSearchTerm"/> + </test> +</tests> diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/AddDefaultSearchFieldTest.php b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/AddDefaultSearchFieldTest.php new file mode 100644 index 0000000000000..1a1e7f4e0d643 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/AddDefaultSearchFieldTest.php @@ -0,0 +1,74 @@ +<?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; + +use Magento\Elasticsearch6\Model\Adapter\FieldMapper\AddDefaultSearchField; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Test mapping preprocessor AddDefaultSearchField + */ +class AddDefaultSearchFieldTest extends TestCase +{ + /** + * Test default search field "_search" should be prepended and overwrite if exist. + * + * @dataProvider processDataProvider + * @param array $mappingBefore + * @param array $mappingAfter + */ + public function testProcess(array $mappingBefore, array $mappingAfter) + { + $objectManager = new ObjectManager($this); + $model = $objectManager->getObject(AddDefaultSearchField::class); + $this->assertEquals($mappingAfter, $model->process($mappingBefore)); + } + + /** + * @return array + */ + public function processDataProvider(): array + { + return [ + '_search field should be prepended if not exist' => [ + [ + 'name' => [ + 'type' => 'text' + ] + ], + [ + '_search' => [ + 'type' => 'text' + ], + 'name' => [ + 'type' => 'text' + ] + ] + ], + '_search field should be prepended and overwrite if exist' => [ + [ + 'name' => [ + 'type' => 'text', + ], + '_search' => [ + 'type' => 'keyword' + ], + ], + [ + '_search' => [ + 'type' => 'text' + ], + 'name' => [ + 'type' => 'text', + ] + ] + ] + ]; + } +} diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchFieldTest.php b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchFieldTest.php new file mode 100644 index 0000000000000..cfe8b71095d21 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/CopySearchableFieldsToSearchFieldTest.php @@ -0,0 +1,125 @@ +<?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; + +use Magento\Elasticsearch6\Model\Adapter\FieldMapper\CopySearchableFieldsToSearchField; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Test mapping preprocessor CopySearchableFieldsToSearchField + */ +class CopySearchableFieldsToSearchFieldTest extends TestCase +{ + /** + * Test "copy_to" parameter should be added to searchable fields. + * + * @dataProvider processDataProvider + * @param array $mappingBefore + * @param array $mappingAfter + */ + public function testProcess(array $mappingBefore, array $mappingAfter) + { + $objectManager = new ObjectManager($this); + $model = $objectManager->getObject(CopySearchableFieldsToSearchField::class); + $this->assertEquals($mappingAfter, $model->process($mappingBefore)); + } + + /** + * @return array + */ + public function processDataProvider(): array + { + return [ + 'index text field should be copied' => [ + [ + 'name' => [ + 'type' => 'text' + ] + ], + [ + 'name' => [ + 'type' => 'text', + 'copy_to' => [ + '_search' + ] + ] + ] + ], + 'non-index text field should not be copied' => [ + [ + 'name' => [ + 'type' => 'text', + 'index' => false + ] + ], + [ + 'name' => [ + 'type' => 'text', + 'index' => false + ] + ] + ], + 'index keyword field should be copied' => [ + [ + 'material' => [ + 'type' => 'keyword' + ] + ], + [ + 'material' => [ + 'type' => 'keyword', + 'copy_to' => [ + '_search' + ] + ] + ] + ], + 'non-index keyword field should not be copied' => [ + [ + 'country_of_manufacture' => [ + 'type' => 'keyword', + 'index' => false + ] + ], + [ + 'country_of_manufacture' => [ + 'type' => 'keyword', + 'index' => false + ] + ] + ], + 'index integer field should not be copied' => [ + [ + 'sale' => [ + 'type' => 'integer', + ] + ], + [ + 'sale' => [ + 'type' => 'integer', + ] + ] + ], + 'non-index integer field should not be copied' => [ + [ + 'position_category_1' => [ + 'type' => 'integer', + 'index' => false + ] + ], + [ + 'position_category_1' => [ + 'type' => 'integer', + 'index' => false + ] + ] + ], + ]; + } +} diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php index 487a5a886f951..607624d7b5e8e 100644 --- a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php +++ b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php @@ -3,13 +3,16 @@ * 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\Elasticsearch6\Model\Adapter\FieldMapper\AddDefaultSearchField; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Elasticsearch6\Model\Client\Elasticsearch; /** - * Class ElasticsearchTest + * Test elasticsearch client methods */ class ElasticsearchTest extends \PHPUnit\Framework\TestCase { @@ -83,10 +86,13 @@ protected function setUp() $this->objectManager = new ObjectManagerHelper($this); $this->model = $this->objectManager->getObject( - \Magento\Elasticsearch6\Model\Client\Elasticsearch::class, + Elasticsearch::class, [ 'options' => $this->getOptions(), - 'elasticsearchClient' => $this->elasticsearchClientMock + 'elasticsearchClient' => $this->elasticsearchClientMock, + 'fieldsMappingPreprocessors' => [ + new AddDefaultSearchField() + ] ] ); } @@ -97,7 +103,7 @@ protected function setUp() public function testConstructorOptionsException() { $result = $this->objectManager->getObject( - \Magento\Elasticsearch6\Model\Client\Elasticsearch::class, + Elasticsearch::class, [ 'options' => [] ] @@ -119,6 +125,69 @@ public function testConstructorWithOptions() $this->assertNotNull($result); } + /** + * Ensure that configuration returns correct url. + * + * @param array $options + * @param string $expectedResult + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \ReflectionException + * @dataProvider getOptionsDataProvider + */ + public function testBuildConfig(array $options, $expectedResult): void + { + $buildConfig = new Elasticsearch($options); + $config = $this->getPrivateMethod(Elasticsearch::class, 'buildConfig'); + $result = $config->invoke($buildConfig, $options); + $this->assertEquals($expectedResult, $result['hosts'][0]); + } + + /** + * Return private method for elastic search class. + * + * @param $className + * @param $methodName + * @return \ReflectionMethod + * @throws \ReflectionException + */ + private function getPrivateMethod($className, $methodName) + { + $reflector = new \ReflectionClass($className); + $method = $reflector->getMethod($methodName); + $method->setAccessible(true); + + return $method; + } + + /** + * Get options data provider. + */ + public function getOptionsDataProvider() + { + return [ + [ + 'without_protocol' => [ + 'hostname' => 'localhost', + 'port' => '9200', + 'timeout' => 15, + 'index' => 'magento2', + 'enableAuth' => 0, + ], + 'expected_result' => 'http://localhost:9200' + ], + [ + 'with_protocol' => [ + 'hostname' => 'https://localhost', + 'port' => '9200', + 'timeout' => 15, + 'index' => 'magento2', + 'enableAuth' => 0, + ], + 'expected_result' => 'https://localhost:9200' + ] + ]; + } + /** * Test ping functionality */ diff --git a/app/code/Magento/Elasticsearch6/composer.json b/app/code/Magento/Elasticsearch6/composer.json index 9107f5e900415..b2411f6740fae 100644 --- a/app/code/Magento/Elasticsearch6/composer.json +++ b/app/code/Magento/Elasticsearch6/composer.json @@ -9,7 +9,7 @@ "magento/module-search": "*", "magento/module-store": "*", "magento/module-elasticsearch": "*", - "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1" + "elasticsearch/elasticsearch": "~2.0||~5.1||~6.1" }, "suggest": { "magento/module-config": "*" diff --git a/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml b/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml index 067a0acb8c908..5c6a9357a5f6f 100644 --- a/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml +++ b/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml @@ -10,7 +10,7 @@ <system> <section id="catalog"> <group id="search"> - <!-- Elasticsearch 6.0+ --> + <!-- Elasticsearch 6.x --> <field id="elasticsearch6_server_hostname" translate="label" type="text" sortOrder="71" showInDefault="1" showInWebsite="0" showInStore="0"> <label>Elasticsearch Server Hostname</label> @@ -79,6 +79,15 @@ <field id="engine">elasticsearch6</field> </depends> </field> + <field id="elasticsearch6_minimum_should_match" translate="label" type="text" sortOrder="93" showInDefault="1" + showInWebsite="0" showInStore="0"> + <label>Minimum Terms to Match</label> + <depends> + <field id="engine">elasticsearch6</field> + </depends> + <comment><![CDATA[<a href="https://docs.magento.com/m2/ce/user_guide/catalog/search-elasticsearch.html">Learn more</a> about valid syntax.]]></comment> + <backend_model>Magento\Elasticsearch\Model\Config\Backend\MinimumShouldMatch</backend_model> + </field> </group> </section> </system> diff --git a/app/code/Magento/Elasticsearch6/etc/config.xml b/app/code/Magento/Elasticsearch6/etc/config.xml index 047ae977fdef1..3c0f28ee16eaa 100644 --- a/app/code/Magento/Elasticsearch6/etc/config.xml +++ b/app/code/Magento/Elasticsearch6/etc/config.xml @@ -14,6 +14,7 @@ <elasticsearch6_index_prefix>magento2</elasticsearch6_index_prefix> <elasticsearch6_enable_auth>0</elasticsearch6_enable_auth> <elasticsearch6_server_timeout>15</elasticsearch6_server_timeout> + <elasticsearch6_minimum_should_match></elasticsearch6_minimum_should_match> </search> </catalog> </default> diff --git a/app/code/Magento/Elasticsearch6/etc/di.xml b/app/code/Magento/Elasticsearch6/etc/di.xml index 580c61ffc8cdb..69efe1a4a4f59 100644 --- a/app/code/Magento/Elasticsearch6/etc/di.xml +++ b/app/code/Magento/Elasticsearch6/etc/di.xml @@ -17,7 +17,7 @@ <type name="Magento\Search\Model\Adminhtml\System\Config\Source\Engine"> <arguments> <argument name="engines" xsi:type="array"> - <item name="elasticsearch6" xsi:type="string">Elasticsearch 6.0+</item> + <item name="elasticsearch6" xsi:type="string">Elasticsearch 6.x</item> </argument> </arguments> </type> @@ -111,6 +111,15 @@ </arguments> </type> + <type name="Magento\Elasticsearch6\Model\Client\Elasticsearch"> + <arguments> + <argument name="fieldsMappingPreprocessors" xsi:type="array"> + <item name="elasticsearch6_copy_searchable_fields_to_search_field" xsi:type="object">Magento\Elasticsearch6\Model\Adapter\FieldMapper\CopySearchableFieldsToSearchField</item> + <item name="elasticsearch6_add_default_search_field" xsi:type="object">Magento\Elasticsearch6\Model\Adapter\FieldMapper\AddDefaultSearchField</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\Search\Dynamic\IntervalFactory"> <arguments> <argument name="intervals" xsi:type="array"> diff --git a/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Save.php b/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Save.php index 135506f4068a8..8e0b4d30ec511 100644 --- a/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Save.php +++ b/app/code/Magento/Email/Controller/Adminhtml/Email/Template/Save.php @@ -1,15 +1,63 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Email\Controller\Adminhtml\Email\Template; +use Exception; +use Magento\Backend\App\Action\Context; +use Magento\Backend\Model\Session; +use Magento\Email\Controller\Adminhtml\Email\Template; +use Magento\Email\Model\ResourceModel\Template as TemplateResource; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\TemplateTypesInterface; +use Magento\Framework\Registry; +use Magento\Framework\Stdlib\DateTime\DateTime; -class Save extends \Magento\Email\Controller\Adminhtml\Email\Template +/** + * Save Controller + */ +class Save extends Template implements HttpPostActionInterface { + /** + * @var DateTime + */ + private $dateTime; + + /** + * @var TemplateResource + */ + private $templateResource; + + /** + * @var Session + */ + private $backendSession; + + /** + * Save constructor + * + * @param Context $context + * @param Registry $coreRegistry + * @param DateTime|null $dateTime + * @param TemplateResource|null $templateResource + * @param Session|null $backendSession + */ + public function __construct( + Context $context, + Registry $coreRegistry, + DateTime $dateTime = null, + TemplateResource $templateResource = null, + Session $backendSession = null + ) { + $this->dateTime = $dateTime ?: ObjectManager::getInstance()->get(DateTime::class); + $this->templateResource = $templateResource ?: ObjectManager::getInstance()->get(TemplateResource::class); + $this->backendSession = $backendSession ?: ObjectManager::getInstance()->get(Session::class); + parent::__construct($context, $coreRegistry); + } + /** * Save transactional email action * @@ -18,10 +66,10 @@ class Save extends \Magento\Email\Controller\Adminhtml\Email\Template public function execute() { $request = $this->getRequest(); - $id = $this->getRequest()->getParam('id'); + $templateId = $this->getRequest()->getParam('id'); $template = $this->_initTemplate('id'); - if (!$template->getId() && $id) { + if (!$template->getId() && $templateId) { $this->messageManager->addErrorMessage(__('This email template no longer exists.')); $this->_redirect('adminhtml/*/'); return; @@ -37,7 +85,7 @@ public function execute() )->setTemplateStyles( $request->getParam('template_styles') )->setModifiedAt( - $this->_objectManager->get(\Magento\Framework\Stdlib\DateTime\DateTime::class)->gmtDate() + $this->dateTime->gmtDate() )->setOrigTemplateCode( $request->getParam('orig_template_code') )->setOrigTemplateVariables( @@ -53,17 +101,13 @@ public function execute() $template->setTemplateStyles(''); } - $template->save(); - $this->_objectManager->get(\Magento\Backend\Model\Session::class)->setFormData(false); + $this->templateResource->save($template); + + $this->backendSession->setFormData(false); $this->messageManager->addSuccessMessage(__('You saved the email template.')); $this->_redirect('adminhtml/*'); - } catch (\Exception $e) { - $this->_objectManager->get( - \Magento\Backend\Model\Session::class - )->setData( - 'email_template_form_data', - $request->getParams() - ); + } catch (Exception $e) { + $this->backendSession->setData('email_template_form_data', $request->getParams()); $this->messageManager->addErrorMessage($e->getMessage()); $this->_forward('new'); } diff --git a/app/code/Magento/Email/Model/AbstractTemplate.php b/app/code/Magento/Email/Model/AbstractTemplate.php index 5eae1d1462184..8acb0a9fddb75 100644 --- a/app/code/Magento/Email/Model/AbstractTemplate.php +++ b/app/code/Magento/Email/Model/AbstractTemplate.php @@ -362,12 +362,19 @@ public function getProcessedTemplate(array $variables = []) $variables = $this->addEmailVariables($variables, $storeId); $processor->setVariables($variables); + $previousStrictMode = $processor->setStrictMode( + !$this->getData('is_legacy') && is_numeric($this->getTemplateId()) + ); + try { $result = $processor->filter($this->getTemplateText()); } catch (\Exception $e) { $this->cancelDesignConfig(); throw new \LogicException(__($e->getMessage()), $e->getCode(), $e); + } finally { + $processor->setStrictMode($previousStrictMode); } + if ($isDesignApplied) { $this->cancelDesignConfig(); } @@ -455,6 +462,9 @@ protected function addEmailVariables($variables, $storeId) if (!isset($variables['store'])) { $variables['store'] = $store; } + if (!isset($variables['store']['frontend_name'])) { + $variables['store']['frontend_name'] = $store->getFrontendName(); + } if (!isset($variables['logo_url'])) { $variables['logo_url'] = $this->getLogoUrl($storeId); } diff --git a/app/code/Magento/Email/Model/Template.php b/app/code/Magento/Email/Model/Template.php index 7306db1222872..ca962b31e63af 100644 --- a/app/code/Magento/Email/Model/Template.php +++ b/app/code/Magento/Email/Model/Template.php @@ -246,12 +246,20 @@ public function getProcessedTemplateSubject(array $variables) $this->applyDesignConfig(); $storeId = $this->getDesignConfig()->getStore(); + + $previousStrictMode = $processor->setStrictMode( + !$this->getData('is_legacy') && is_numeric($this->getTemplateId()) + ); + try { $processedResult = $processor->setStoreId($storeId)->filter(__($this->getTemplateSubject())); } catch (\Exception $e) { $this->cancelDesignConfig(); throw new \Magento\Framework\Exception\MailException(__($e->getMessage()), $e); + } finally { + $processor->setStrictMode($previousStrictMode); } + $this->cancelDesignConfig(); return $processedResult; } diff --git a/app/code/Magento/Email/Model/Template/Filter.php b/app/code/Magento/Email/Model/Template/Filter.php index a29b1165d83c8..82ebc8c78d8b2 100644 --- a/app/code/Magento/Email/Model/Template/Filter.php +++ b/app/code/Magento/Email/Model/Template/Filter.php @@ -8,6 +8,7 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\Filter\VariableResolverInterface; use Magento\Framework\View\Asset\ContentProcessorException; use Magento\Framework\View\Asset\ContentProcessorInterface; @@ -50,6 +51,7 @@ class Filter extends \Magento\Framework\Filter\Template * Modifier Callbacks * * @var array + * @deprecated Use the new Directive Processor interfaces */ protected $_modifiers = ['nl2br' => '']; @@ -165,15 +167,20 @@ class Filter extends \Magento\Framework\Filter\Template protected $configVariables; /** - * @var \Magento\Email\Model\Template\Css\Processor + * @var Css\Processor */ private $cssProcessor; /** - * @var ReadInterface + * @var Filesystem */ private $pubDirectory; + /** + * @var \Magento\Framework\Filesystem\Directory\Read + */ + private $pubDirectoryRead; + /** * @param \Magento\Framework\Stdlib\StringUtils $string * @param \Psr\Log\LoggerInterface $logger @@ -190,7 +197,10 @@ class Filter extends \Magento\Framework\Filter\Template * @param \Magento\Variable\Model\Source\Variables $configVariables * @param array $variables * @param \Magento\Framework\Css\PreProcessor\Adapter\CssInliner|null $cssInliner - * + * @param array $directiveProcessors + * @param VariableResolverInterface|null $variableResolver + * @param Css\Processor|null $cssProcessor + * @param Filesystem|null $pubDirectory * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -208,7 +218,11 @@ public function __construct( \Pelago\Emogrifier $emogrifier, \Magento\Variable\Model\Source\Variables $configVariables, $variables = [], - \Magento\Framework\Css\PreProcessor\Adapter\CssInliner $cssInliner = null + \Magento\Framework\Css\PreProcessor\Adapter\CssInliner $cssInliner = null, + array $directiveProcessors = [], + VariableResolverInterface $variableResolver = null, + Css\Processor $cssProcessor = null, + Filesystem $pubDirectory = null ) { $this->_escaper = $escaper; $this->_assetRepo = $assetRepo; @@ -224,8 +238,12 @@ public function __construct( $this->emogrifier = $emogrifier; $this->cssInliner = $cssInliner ?: \Magento\Framework\App\ObjectManager::getInstance() ->get(\Magento\Framework\Css\PreProcessor\Adapter\CssInliner::class); + $this->cssProcessor = $cssProcessor ?: ObjectManager::getInstance() + ->get(Css\Processor::class); + $this->pubDirectory = $pubDirectory ?: ObjectManager::getInstance() + ->get(Filesystem::class); $this->configVariables = $configVariables; - parent::__construct($string, $variables); + parent::__construct($string, $variables, $directiveProcessors, $variableResolver); } /** @@ -321,32 +339,14 @@ public function setDesignParams(array $designParams) } /** - * Get CSS processor - * - * @deprecated 100.1.2 - * @return Css\Processor - */ - private function getCssProcessor() - { - if (!$this->cssProcessor) { - $this->cssProcessor = ObjectManager::getInstance()->get(Css\Processor::class); - } - return $this->cssProcessor; - } - - /** - * Get pub directory + * Sets pub directory * - * @deprecated 100.1.2 * @param string $dirType - * @return ReadInterface + * @return void */ - private function getPubDirectory($dirType) + private function setPubDirectory($dirType) { - if (!$this->pubDirectory) { - $this->pubDirectory = ObjectManager::getInstance()->get(Filesystem::class)->getDirectoryRead($dirType); - } - return $this->pubDirectory; + $this->pubDirectoryRead = $this->pubDirectory->getDirectoryRead($dirType); } /** @@ -639,7 +639,10 @@ public function varDirective($construction) return $construction[0]; } - list($directive, $modifiers) = $this->explodeModifiers($construction[2], 'escape'); + list($directive, $modifiers) = $this->explodeModifiers( + $construction[2] . ($construction['filters'] ?? ''), + 'escape' + ); return $this->applyModifiers($this->getVariable($directive, ''), $modifiers); } @@ -656,6 +659,7 @@ public function varDirective($construction) * @param string $value * @param string $default assumed modifier if none present * @return array + * @deprecated Use the new FilterApplier or Directive Processor interfaces */ protected function explodeModifiers($value, $default = null) { @@ -674,6 +678,7 @@ protected function explodeModifiers($value, $default = null) * @param string $value * @param string $modifiers * @return string + * @deprecated Use the new FilterApplier or Directive Processor interfaces */ protected function applyModifiers($value, $modifiers) { @@ -701,6 +706,7 @@ protected function applyModifiers($value, $modifiers) * @param string $value * @param string $type * @return string + * @deprecated Use the new FilterApplier or Directive Processor interfaces */ public function modifierEscape($value, $type = 'html') { @@ -844,7 +850,7 @@ public function cssDirective($construction) return '/* ' . __('"file" parameter must be specified') . ' */'; } - $css = $this->getCssProcessor()->process( + $css = $this->cssProcessor->process( $this->getCssFilesContent([$params['file']]) ); @@ -947,9 +953,9 @@ public function getCssFilesContent(array $files) try { foreach ($files as $file) { $asset = $this->_assetRepo->createAsset($file, $designParams); - $pubDirectory = $this->getPubDirectory($asset->getContext()->getBaseDirType()); - if ($pubDirectory->isExist($asset->getPath())) { - $css .= $pubDirectory->readFile($asset->getPath()); + $this->setPubDirectory($asset->getContext()->getBaseDirType()); + if ($this->pubDirectoryRead->isExist($asset->getPath())) { + $css .= $this->pubDirectoryRead->readFile($asset->getPath()); } else { $css .= $asset->getContent(); } @@ -979,7 +985,7 @@ public function applyInlineCss($html) $cssToInline = $this->getCssFilesContent( $this->getInlineCssFiles() ); - $cssToInline = $this->getCssProcessor()->process($cssToInline); + $cssToInline = $this->cssProcessor->process($cssToInline); // Only run Emogrify if HTML and CSS contain content if ($html && $cssToInline) { diff --git a/app/code/Magento/Email/Setup/Patch/Data/FlagLegacyTemplates.php b/app/code/Magento/Email/Setup/Patch/Data/FlagLegacyTemplates.php new file mode 100644 index 0000000000000..6a97f38239bd6 --- /dev/null +++ b/app/code/Magento/Email/Setup/Patch/Data/FlagLegacyTemplates.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Email\Setup\Patch\Data; + +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; + +/** + * Flag all existing email templates overrides as legacy + */ +class FlagLegacyTemplates implements DataPatchInterface +{ + /** + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** + * @param ModuleDataSetupInterface $moduleDataSetup + */ + public function __construct(ModuleDataSetupInterface $moduleDataSetup) + { + $this->moduleDataSetup = $moduleDataSetup; + } + + /** + * @inheritDoc + */ + public function apply() + { + $this->moduleDataSetup + ->getConnection() + ->update($this->moduleDataSetup->getTable('email_template'), ['is_legacy' => '1']); + } + + /** + * @inheritDoc + */ + public static function getDependencies() + { + return []; + } + + /** + * @inheritDoc + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/Email/Test/Mftf/ActionGroup/AssertEmailTemplateContentActionGroup.xml b/app/code/Magento/Email/Test/Mftf/ActionGroup/AssertEmailTemplateContentActionGroup.xml new file mode 100644 index 0000000000000..653de14a56c9c --- /dev/null +++ b/app/code/Magento/Email/Test/Mftf/ActionGroup/AssertEmailTemplateContentActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertEmailTemplateContentActionGroup"> + <arguments> + <argument name="expectedContent" type="string" defaultValue="{{EmailTemplate.templateText}}"/> + </arguments> + + <see userInput="{{expectedContent}}" stepKey="checkTemplateContainText"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Email/Test/Mftf/ActionGroup/CreateCustomTemplateActionGroup.xml b/app/code/Magento/Email/Test/Mftf/ActionGroup/CreateCustomTemplateActionGroup.xml new file mode 100644 index 0000000000000..6fe1dfad761ff --- /dev/null +++ b/app/code/Magento/Email/Test/Mftf/ActionGroup/CreateCustomTemplateActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateCustomTemplateActionGroup" extends="CreateNewTemplateActionGroup"> + <remove keyForRemoval="selectValueFromTemplateDropDown"/> + <remove keyForRemoval="clickLoadTemplateButton"/> + + <fillField selector="{{AdminEmailTemplateEditSection.templateSubject}}" userInput="{{template.templateSubject}}" after="fillTemplateNameField" stepKey="fillTemplateSubject"/> + <fillField selector="{{AdminEmailTemplateEditSection.templateText}}" userInput="{{template.templateText}}" after="fillTemplateSubject" stepKey="fillTemplateText"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Email/Test/Mftf/ActionGroup/CreateNewTemplateActionGroup.xml b/app/code/Magento/Email/Test/Mftf/ActionGroup/CreateNewTemplateActionGroup.xml new file mode 100644 index 0000000000000..c6e45ad06971a --- /dev/null +++ b/app/code/Magento/Email/Test/Mftf/ActionGroup/CreateNewTemplateActionGroup.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!--Create New Template --> + <actionGroup name="CreateNewTemplateActionGroup"> + <annotations> + <description>Clicks on Add New Template. Fills the Template details. Clicks on Save. PLEASE NOTE: The values are Hardcoded.</description> + </annotations> + <arguments> + <argument name="template" defaultValue="EmailTemplate"/> + </arguments> + + <!--Go to Marketing> Email Templates--> + <amOnPage url="{{AdminEmailTemplateIndexPage.url}}" stepKey="navigateToEmailTemplatePage"/> + <!--Click "Add New Template" button--> + <click selector="{{AdminMainActionsSection.add}}" stepKey="clickAddNewTemplateButton"/> + <!--Select value for "Template" drop-down menu in "Load Default Template" tab--> + <selectOption selector="{{AdminEmailTemplateEditSection.templateDropDown}}" userInput="Registry Update" stepKey="selectValueFromTemplateDropDown"/> + <!--Fill in required fields in "Template Information" tab and click "Save Template" button--> + <click selector="{{AdminEmailTemplateEditSection.loadTemplateButton}}" stepKey="clickLoadTemplateButton"/> + <fillField selector="{{AdminEmailTemplateEditSection.templateCode}}" userInput="{{EmailTemplate.templateName}}" stepKey="fillTemplateNameField"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveTemplateButton"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the email template." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Email/Test/Mftf/ActionGroup/DeleteEmailTemplateActionGroup.xml b/app/code/Magento/Email/Test/Mftf/ActionGroup/DeleteEmailTemplateActionGroup.xml new file mode 100644 index 0000000000000..b12f79da5f597 --- /dev/null +++ b/app/code/Magento/Email/Test/Mftf/ActionGroup/DeleteEmailTemplateActionGroup.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="DeleteEmailTemplateActionGroup" extends="FindAndOpenEmailTemplateActionGroup"> + <annotations> + <description>Clicks on Delete Template. Accepts the Popup. Validates that the Email Template is NOT present in the Email Templates Grid.</description> + </annotations> + <click selector="{{AdminEmailTemplateEditSection.deleteTemplateButton}}" after="checkTemplateName" stepKey="deleteTemplate"/> + <waitForElementVisible selector="{{AdminEmailTemplateEditSection.acceptPopupButton}}" after="deleteTemplate" stepKey="waitForConfirmButton"/> + <click selector="{{AdminEmailTemplateEditSection.acceptPopupButton}}" after="waitForConfirmButton" stepKey="acceptPopup"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" after="acceptPopup" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the email template." after="waitForSuccessMessage" stepKey="seeSuccessfulMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Email/Test/Mftf/ActionGroup/EmailTemplateActionGroup.xml b/app/code/Magento/Email/Test/Mftf/ActionGroup/EmailTemplateActionGroup.xml deleted file mode 100644 index 3b99ade32e6ce..0000000000000 --- a/app/code/Magento/Email/Test/Mftf/ActionGroup/EmailTemplateActionGroup.xml +++ /dev/null @@ -1,86 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Create New Template --> - <actionGroup name="CreateNewTemplate"> - <annotations> - <description>Clicks on Add New Template. Fills the Template details. Clicks on Save. PLEASE NOTE: The values are Hardcoded.</description> - </annotations> - <arguments> - <argument name="template" defaultValue="EmailTemplate"/> - </arguments> - - <!--Go to Marketing> Email Templates--> - <amOnPage url="{{AdminEmailTemplateIndexPage.url}}" stepKey="navigateToEmailTemplatePage"/> - <!--Click "Add New Template" button--> - <click selector="{{AdminMainActionsSection.add}}" stepKey="clickAddNewTemplateButton"/> - <!--Select value for "Template" drop-down menu in "Load Default Template" tab--> - <selectOption selector="{{AdminEmailTemplateEditSection.templateDropDown}}" userInput="Registry Update" stepKey="selectValueFromTemplateDropDown"/> - <!--Fill in required fields in "Template Information" tab and click "Save Template" button--> - <click selector="{{AdminEmailTemplateEditSection.loadTemplateButton}}" stepKey="clickLoadTemplateButton"/> - <fillField selector="{{AdminEmailTemplateEditSection.templateCode}}" userInput="{{EmailTemplate.templateName}}" stepKey="fillTemplateNameField"/> - <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveTemplateButton"/> - <waitForElementVisible selector="{{AdminMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="You saved the email template." stepKey="seeSuccessMessage"/> - </actionGroup> - - <!--Create New Custom Template --> - <actionGroup name="CreateCustomTemplate" extends="CreateNewTemplate"> - <remove keyForRemoval="selectValueFromTemplateDropDown"/> - <remove keyForRemoval="clickLoadTemplateButton"/> - - <fillField selector="{{AdminEmailTemplateEditSection.templateSubject}}" userInput="{{template.templateSubject}}" after="fillTemplateNameField" stepKey="fillTemplateSubject"/> - <fillField selector="{{AdminEmailTemplateEditSection.templateText}}" userInput="{{template.templateText}}" after="fillTemplateSubject" stepKey="fillTemplateText"/> - </actionGroup> - - <!-- Find and Open Email Template --> - <actionGroup name="FindAndOpenEmailTemplate"> - <arguments> - <argument name="template" defaultValue="EmailTemplate"/> - </arguments> - - <amOnPage url="{{AdminEmailTemplateIndexPage.url}}" stepKey="navigateEmailTemplatePage" /> - <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clearFilters"/> - <fillField selector="{{AdminEmailTemplateIndexSection.searchTemplateField}}" userInput="{{template.templateName}}" stepKey="findCreatedTemplate"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearch"/> - <waitForElementVisible selector="{{AdminEmailTemplateIndexSection.templateRowByName(template.templateName)}}" stepKey="waitForTemplatesAppeared"/> - <click selector="{{AdminEmailTemplateIndexSection.templateRowByName(template.templateName)}}" stepKey="clickToOpenTemplate"/> - <waitForElementVisible selector="{{AdminEmailTemplateEditSection.templateCode}}" stepKey="waitForTemplateNameisible"/> - <seeInField selector="{{AdminEmailTemplateEditSection.templateCode}}" userInput="{{template.templateName}}" stepKey="checkTemplateName"/> - </actionGroup> - - <actionGroup name="DeleteEmailTemplate" extends="FindAndOpenEmailTemplate"> - <annotations> - <description>Clicks on Delete Template. Accepts the Popup. Validates that the Email Template is NOT present in the Email Templates Grid.</description> - </annotations> - <click selector="{{AdminEmailTemplateEditSection.deleteTemplateButton}}" after="checkTemplateName" stepKey="deleteTemplate"/> - <waitForElementVisible selector="{{AdminEmailTemplateEditSection.acceptPopupButton}}" after="deleteTemplate" stepKey="waitForConfirmButton"/> - <click selector="{{AdminEmailTemplateEditSection.acceptPopupButton}}" after="waitForConfirmButton" stepKey="acceptPopup"/> - <waitForElementVisible selector="{{AdminMessagesSection.successMessage}}" after="acceptPopup" stepKey="waitForSuccessMessage"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="You deleted the email template." after="waitForSuccessMessage" stepKey="seeSuccessfulMessage"/> - </actionGroup> - - <actionGroup name="PreviewEmailTemplate" extends="FindAndOpenEmailTemplate"> - <click selector="{{AdminEmailTemplateEditSection.previewTemplateButton}}" after="checkTemplateName" stepKey="clickPreviewTemplate"/> - <switchToNextTab after="clickPreviewTemplate" stepKey="switchToNewOpenedTab"/> - <seeInCurrentUrl url="{{AdminEmailTemplatePreviewPage.url}}" after="switchToNewOpenedTab" stepKey="seeCurrentUrl"/> - <seeElement selector="{{AdminEmailTemplatePreviewSection.iframe}}" after="seeCurrentUrl" stepKey="seeIframeOnPage"/> - <switchToIFrame userInput="preview_iframe" after="seeIframeOnPage" stepKey="switchToIframe"/> - <waitForPageLoad after="switchToIframe" stepKey="waitForPageLoaded"/> - </actionGroup> - - <actionGroup name="AssertEmailTemplateContent"> - <arguments> - <argument name="expectedContent" type="string" defaultValue="{{EmailTemplate.templateText}}"/> - </arguments> - - <see userInput="{{expectedContent}}" stepKey="checkTemplateContainText"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Email/Test/Mftf/ActionGroup/FindAndOpenEmailTemplateActionGroup.xml b/app/code/Magento/Email/Test/Mftf/ActionGroup/FindAndOpenEmailTemplateActionGroup.xml new file mode 100644 index 0000000000000..5e95b4a72b0ec --- /dev/null +++ b/app/code/Magento/Email/Test/Mftf/ActionGroup/FindAndOpenEmailTemplateActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FindAndOpenEmailTemplateActionGroup"> + <arguments> + <argument name="template" defaultValue="EmailTemplate"/> + </arguments> + + <amOnPage url="{{AdminEmailTemplateIndexPage.url}}" stepKey="navigateEmailTemplatePage" /> + <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clearFilters"/> + <fillField selector="{{AdminEmailTemplateIndexSection.searchTemplateField}}" userInput="{{template.templateName}}" stepKey="findCreatedTemplate"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearch"/> + <waitForElementVisible selector="{{AdminEmailTemplateIndexSection.templateRowByName(template.templateName)}}" stepKey="waitForTemplatesAppeared"/> + <click selector="{{AdminEmailTemplateIndexSection.templateRowByName(template.templateName)}}" stepKey="clickToOpenTemplate"/> + <waitForElementVisible selector="{{AdminEmailTemplateEditSection.templateCode}}" stepKey="waitForTemplateNameisible"/> + <seeInField selector="{{AdminEmailTemplateEditSection.templateCode}}" userInput="{{template.templateName}}" stepKey="checkTemplateName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Email/Test/Mftf/ActionGroup/PreviewEmailTemplateActionGroup.xml b/app/code/Magento/Email/Test/Mftf/ActionGroup/PreviewEmailTemplateActionGroup.xml new file mode 100644 index 0000000000000..4038a34625800 --- /dev/null +++ b/app/code/Magento/Email/Test/Mftf/ActionGroup/PreviewEmailTemplateActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="PreviewEmailTemplateActionGroup" extends="FindAndOpenEmailTemplateActionGroup"> + <click selector="{{AdminEmailTemplateEditSection.previewTemplateButton}}" after="checkTemplateName" stepKey="clickPreviewTemplate"/> + <switchToNextTab after="clickPreviewTemplate" stepKey="switchToNewOpenedTab"/> + <seeInCurrentUrl url="{{AdminEmailTemplatePreviewPage.url}}" after="switchToNewOpenedTab" stepKey="seeCurrentUrl"/> + <seeElement selector="{{AdminEmailTemplatePreviewSection.iframe}}" after="seeCurrentUrl" stepKey="seeIframeOnPage"/> + <switchToIFrame userInput="preview_iframe" after="seeIframeOnPage" stepKey="switchToIframe"/> + <waitForPageLoad after="switchToIframe" stepKey="waitForPageLoaded"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Email/Test/Mftf/Test/AdminEmailTemplatePreviewTest.xml b/app/code/Magento/Email/Test/Mftf/Test/AdminEmailTemplatePreviewTest.xml index 459e3c0f9290f..ca73de1f88fcc 100644 --- a/app/code/Magento/Email/Test/Mftf/Test/AdminEmailTemplatePreviewTest.xml +++ b/app/code/Magento/Email/Test/Mftf/Test/AdminEmailTemplatePreviewTest.xml @@ -27,14 +27,14 @@ <after> <!--Delete created Template--> - <actionGroup ref="DeleteEmailTemplate" stepKey="deleteTemplate"/> + <actionGroup ref="DeleteEmailTemplateActionGroup" stepKey="deleteTemplate"/> <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clearFilters"/> <!--Logout from Admin Area--> <actionGroup ref="logout" stepKey="logoutOfAdmin"/> </after> - <actionGroup ref="CreateCustomTemplate" stepKey="createTemplate"/> - <actionGroup ref="PreviewEmailTemplate" stepKey="previewTemplate"/> - <actionGroup ref="AssertEmailTemplateContent" stepKey="assertContent"/> + <actionGroup ref="CreateCustomTemplateActionGroup" stepKey="createTemplate"/> + <actionGroup ref="PreviewEmailTemplateActionGroup" stepKey="previewTemplate"/> + <actionGroup ref="AssertEmailTemplateContentActionGroup" stepKey="assertContent"/> </test> </tests> diff --git a/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/Render/SenderTest.php b/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/Render/SenderTest.php new file mode 100644 index 0000000000000..4ca330f87a6ef --- /dev/null +++ b/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/Render/SenderTest.php @@ -0,0 +1,72 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Email\Test\Unit\Block\Adminhtml\Template\Render; + +use Magento\Email\Block\Adminhtml\Template\Grid\Renderer\Sender; +use Magento\Framework\DataObject; + +/** + * Class \Magento\Email\Test\Unit\Block\Adminhtml\Template\Render\SenderTest + */ +class SenderTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject|Sender + */ + protected $block; + + /** + * Setup environment + */ + protected function setUp() + { + $this->block = $this->getMockBuilder(Sender::class) + ->disableOriginalConstructor() + ->setMethods(['escapeHtml']) + ->getMock(); + } + + /** + * Test render() with sender name and sender email are not empty + */ + public function testRenderWithSenderNameAndEmail() + { + $templateSenderEmail = 'test'; + $this->block->expects($this->any())->method('escapeHtml')->with($templateSenderEmail) + ->willReturn('test'); + $actualResult = $this->block->render( + new DataObject( + [ + 'template_sender_name' => 'test', + 'template_sender_email' => 'test@localhost.com' + ] + ) + ); + $this->assertEquals('test [test@localhost.com]', $actualResult); + } + + /** + * Test render() with sender name and sender email are empty + */ + public function testRenderWithNoSenderNameAndEmail() + { + $templateSenderEmail = ''; + $this->block->expects($this->any())->method('escapeHtml')->with($templateSenderEmail) + ->willReturn(''); + $actualResult = $this->block->render( + new DataObject( + [ + 'template_sender_name' => '', + 'template_sender_email' => '' + ] + ) + ); + $this->assertEquals('---', $actualResult); + } +} diff --git a/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/Render/TypeTest.php b/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/Render/TypeTest.php new file mode 100644 index 0000000000000..88eff38c81799 --- /dev/null +++ b/app/code/Magento/Email/Test/Unit/Block/Adminhtml/Template/Render/TypeTest.php @@ -0,0 +1,94 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Email\Test\Unit\Block\Adminhtml\Template\Render; + +use Magento\Email\Block\Adminhtml\Template\Grid\Renderer\Type; +use Magento\Framework\DataObject; +use Magento\Framework\Phrase; + +/** + * Class \Magento\Email\Test\Unit\Block\Adminhtml\Template\Render\TypeTest + */ +class TypeTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject|Type + */ + protected $block; + + /** + * Setup environment + */ + protected function setUp() + { + $this->block = $this->getMockBuilder(Type::class) + ->disableOriginalConstructor() + ->setMethods(['__']) + ->getMock(); + } + + /** + * Test render() with supported template Text type + */ + public function testRenderWithSupportedTemplateTextType() + { + $testCase = [ + 'dataset' => [ + 'template_type' => '1' + ], + 'expectedResult' => 'Text' + ]; + $this->executeTestCase($testCase); + } + + /** + * Test render() with supported template HTML type + */ + public function testRenderWithSupportedTemplateHtmlType() + { + $testCase = [ + 'dataset' => [ + 'template_type' => '2' + ], + 'expectedResult' => 'HTML' + ]; + $this->executeTestCase($testCase); + } + + /** + * Test render() with unsupported template type + */ + public function testRenderWithUnsupportedTemplateType() + { + $testCase = [ + 'dataset' => [ + 'template_type' => '5' + ], + 'expectedResult' => 'Unknown' + ]; + $this->executeTestCase($testCase); + } + + /** + * Execute Test case + * + * @param array $testCase + */ + public function executeTestCase($testCase) + { + $actualResult = $this->block->render( + new DataObject( + [ + 'template_type' => $testCase['dataset']['template_type'], + ] + ) + ); + $this->assertEquals(new Phrase($testCase['expectedResult']), $actualResult); + } +} diff --git a/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php b/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php index 0a0e9f780351d..036ab1b273fb0 100644 --- a/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php +++ b/app/code/Magento/Email/Test/Unit/Model/AbstractTemplateTest.php @@ -117,8 +117,8 @@ protected function setUp() /** * Return the model under test with additional methods mocked. * - * @param array $mockedMethods - * @param array $data + * @param array $mockedMethods + * @param array $data * @return \Magento\Email\Model\Template|\PHPUnit_Framework_MockObject_MockObject */ protected function getModelMock(array $mockedMethods = [], array $data = []) @@ -150,17 +150,18 @@ protected function getModelMock(array $mockedMethods = [], array $data = []) } /** - * @param $variables array - * @param $templateType string - * @param $storeId int - * @param $expectedVariables array - * @param $expectedResult string + * @param $variables array + * @param $templateType string + * @param $storeId int + * @param $expectedVariables array + * @param $expectedResult string * @dataProvider getProcessedTemplateProvider */ public function testGetProcessedTemplate($variables, $templateType, $storeId, $expectedVariables, $expectedResult) { $filterTemplate = $this->getMockBuilder(\Magento\Email\Model\Template\Filter::class) - ->setMethods([ + ->setMethods( + [ 'setUseSessionInUrl', 'setPlainTemplateMode', 'setIsChildTemplate', @@ -170,7 +171,9 @@ public function testGetProcessedTemplate($variables, $templateType, $storeId, $e 'filter', 'getStoreId', 'getInlineCssFiles', - ]) + 'setStrictMode', + ] + ) ->disableOriginalConstructor() ->getMock(); @@ -194,20 +197,27 @@ public function testGetProcessedTemplate($variables, $templateType, $storeId, $e $filterTemplate->expects($this->any()) ->method('getStoreId') ->will($this->returnValue($storeId)); + $filterTemplate->expects($this->exactly(2)) + ->method('setStrictMode') + ->withConsecutive([$this->equalTo(true)], [$this->equalTo(false)]) + ->willReturnOnConsecutiveCalls(false, true); $expectedVariables['store'] = $this->store; - $model = $this->getModelMock([ + $model = $this->getModelMock( + [ 'getDesignParams', 'applyDesignConfig', 'getTemplateText', 'isPlain', - ]); + ] + ); $filterTemplate->expects($this->any()) ->method('setVariables') ->with(array_merge(['this' => $model], $expectedVariables)); $model->setTemplateFilter($filterTemplate); $model->setTemplateType($templateType); + $model->setTemplateId('123'); $designParams = [ 'area' => \Magento\Framework\App\Area::AREA_FRONTEND, @@ -241,7 +251,8 @@ public function testGetProcessedTemplate($variables, $templateType, $storeId, $e public function testGetProcessedTemplateException() { $filterTemplate = $this->getMockBuilder(\Magento\Email\Model\Template\Filter::class) - ->setMethods([ + ->setMethods( + [ 'setUseSessionInUrl', 'setPlainTemplateMode', 'setIsChildTemplate', @@ -251,7 +262,9 @@ public function testGetProcessedTemplateException() 'filter', 'getStoreId', 'getInlineCssFiles', - ]) + 'setStrictMode', + ] + ) ->disableOriginalConstructor() ->getMock(); $filterTemplate->expects($this->once()) @@ -272,13 +285,19 @@ public function testGetProcessedTemplateException() $filterTemplate->expects($this->any()) ->method('getStoreId') ->will($this->returnValue(1)); + $filterTemplate->expects($this->exactly(2)) + ->method('setStrictMode') + ->withConsecutive([$this->equalTo(false)], [$this->equalTo(true)]) + ->willReturnOnConsecutiveCalls(true, false); - $model = $this->getModelMock([ + $model = $this->getModelMock( + [ 'getDesignParams', 'applyDesignConfig', 'getTemplateText', 'isPlain', - ]); + ] + ); $designParams = [ 'area' => \Magento\Framework\App\Area::AREA_FRONTEND, @@ -290,6 +309,7 @@ public function testGetProcessedTemplateException() ->will($this->returnValue($designParams)); $model->setTemplateFilter($filterTemplate); $model->setTemplateType(\Magento\Framework\App\TemplateTypesInterface::TYPE_TEXT); + $model->setTemplateId('abc'); $filterTemplate->expects($this->once()) ->method('filter') @@ -361,9 +381,9 @@ public function testGetDefaultEmailLogo() } /** - * @param array $config + * @param array $config * @expectedException \Magento\Framework\Exception\LocalizedException - * @dataProvider invalidInputParametersDataProvider + * @dataProvider invalidInputParametersDataProvider */ public function testSetDesignConfigWithInvalidInputParametersThrowsException($config) { diff --git a/app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php b/app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php index 2c9fdae111fd8..778573396b011 100644 --- a/app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php +++ b/app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php @@ -3,6 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Email\Test\Unit\Model\Template; use Magento\Email\Model\Template\Css\Processor; @@ -14,6 +17,7 @@ /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyFields) */ class FilterTest extends \PHPUnit\Framework\TestCase { @@ -92,6 +96,31 @@ class FilterTest extends \PHPUnit\Framework\TestCase */ private $cssInliner; + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Email\Model\Template\Css\Processor + */ + private $cssProcessor; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Framework\Filesystem + */ + private $pubDirectory; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Framework\Filesystem\Directory\Read + */ + private $pubDirectoryRead; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject|\Magento\Framework\Filter\VariableResolver\StrategyResolver + */ + private $variableResolver; + + /** + * @var array + */ + private $directiveProcessors; + protected function setUp() { $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -147,6 +176,41 @@ protected function setUp() $this->cssInliner = $this->objectManager->getObject( \Magento\Framework\Css\PreProcessor\Adapter\CssInliner::class ); + + $this->cssProcessor = $this->getMockBuilder(\Magento\Email\Model\Template\Css\Processor::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->pubDirectory = $this->getMockBuilder(\Magento\Framework\Filesystem::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->pubDirectoryRead = $this->getMockBuilder(\Magento\Framework\Filesystem\Directory\Read::class) + ->disableOriginalConstructor() + ->getMock(); + $this->variableResolver = + $this->getMockBuilder(\Magento\Framework\Filter\VariableResolver\StrategyResolver::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->directiveProcessors = [ + 'depend' => + $this->getMockBuilder(\Magento\Framework\Filter\DirectiveProcessor\DependDirective::class) + ->disableOriginalConstructor() + ->getMock(), + 'if' => + $this->getMockBuilder(\Magento\Framework\Filter\DirectiveProcessor\IfDirective::class) + ->disableOriginalConstructor() + ->getMock(), + 'template' => + $this->getMockBuilder(\Magento\Framework\Filter\DirectiveProcessor\TemplateDirective::class) + ->disableOriginalConstructor() + ->getMock(), + 'legacy' => + $this->getMockBuilder(\Magento\Framework\Filter\DirectiveProcessor\LegacyDirective::class) + ->disableOriginalConstructor() + ->getMock(), + ]; } /** @@ -173,89 +237,16 @@ protected function getModel($mockedMethods = null) $this->configVariables, [], $this->cssInliner, + $this->directiveProcessors, + $this->variableResolver, + $this->cssProcessor, + $this->pubDirectory ] ) ->setMethods($mockedMethods) ->getMock(); } - /** - * Tests proper parsing of the {{trans ...}} directive used in email templates - * - * @dataProvider transDirectiveDataProvider - * @param $value - * @param $expected - * @param array $variables - */ - public function testTransDirective($value, $expected, array $variables = []) - { - $filter = $this->getModel()->setVariables($variables); - $this->assertEquals($expected, $filter->filter($value)); - } - - /** - * Data provider for various possible {{trans ...}} usages - * - * @return array - */ - public function transDirectiveDataProvider() - { - return [ - 'empty directive' => [ - '{{trans}}', - '', - ], - - 'empty string' => [ - '{{trans ""}}', - '', - ], - - 'no padding' => [ - '{{trans"Hello cruel coder..."}}', - 'Hello cruel coder...', - ], - - 'multi-line padding' => [ - "{{trans \t\n\r'Hello cruel coder...' \t\n\r}}", - 'Hello cruel coder...', - ], - - 'capture escaped double-quotes inside text' => [ - '{{trans "Hello \"tested\" world!"}}', - 'Hello "tested" world!', - ], - - 'capture escaped single-quotes inside text' => [ - "{{trans 'Hello \\'tested\\' world!'|escape}}", - "Hello 'tested' world!", - ], - - 'basic var' => [ - '{{trans "Hello %adjective world!" adjective="tested"}}', - 'Hello tested world!', - ], - - 'auto-escaped output' => [ - '{{trans "Hello %adjective <strong>world</strong>!" adjective="<em>bad</em>"}}', - 'Hello <em>bad</em> <strong>world</strong>!', - ], - - 'unescaped modifier' => [ - '{{trans "Hello %adjective <strong>world</strong>!" adjective="<em>bad</em>"|raw}}', - 'Hello <em>bad</em> <strong>world</strong>!', - ], - - 'variable replacement' => [ - '{{trans "Hello %adjective world!" adjective="$mood"}}', - 'Hello happy world!', - [ - 'mood' => 'happy' - ], - ], - ]; - } - /** * Test basic usages of applyInlineCss * @@ -329,17 +320,16 @@ public function testGetCssFilesContent() ->with($file, $designParams) ->willReturn($asset); - $pubDirectory = $this->getMockBuilder(ReadInterface::class) - ->getMockForAbstractClass(); - $reflectionClass = new \ReflectionClass(Filter::class); - $reflectionProperty = $reflectionClass->getProperty('pubDirectory'); - $reflectionProperty->setAccessible(true); - $reflectionProperty->setValue($filter, $pubDirectory); - $pubDirectory->expects($this->once()) + $this->pubDirectory + ->expects($this->once()) + ->method('getDirectoryRead') + ->willReturn($this->pubDirectoryRead); + + $this->pubDirectoryRead->expects($this->once()) ->method('isExist') ->with($path . DIRECTORY_SEPARATOR . $file) ->willReturn(true); - $pubDirectory->expects($this->once()) + $this->pubDirectoryRead->expects($this->once()) ->method('readFile') ->with($path . DIRECTORY_SEPARATOR . $file) ->willReturn($css); @@ -396,43 +386,6 @@ public function testApplyInlineCssThrowsExceptionWhenDesignParamsNotSet() $filter->applyInlineCss('test'); } - /** - * Ensure that after filter callbacks are reset after exception is thrown during filtering - */ - public function testAfterFilterCallbackGetsResetWhenExceptionTriggered() - { - $value = '{{var random_var}}'; - $exception = new \Exception('Test exception'); - $exceptionResult = sprintf(__('Error filtering template: %s'), $exception->getMessage()); - - $this->appState->expects($this->once()) - ->method('getMode') - ->will($this->returnValue(\Magento\Framework\App\State::MODE_DEVELOPER)); - $this->logger->expects($this->once()) - ->method('critical') - ->with($exception); - - $filter = $this->getModel(['varDirective', 'resetAfterFilterCallbacks']); - $filter->expects($this->once()) - ->method('varDirective') - ->will($this->throwException($exception)); - - // Callbacks must be reset after exception is thrown - $filter->expects($this->once()) - ->method('resetAfterFilterCallbacks'); - - // Build arbitrary object to pass into the addAfterFilterCallback method - $callbackObject = $this->getMockBuilder('stdObject') - ->setMethods(['afterFilterCallbackMethod']) - ->getMock(); - // Callback should never run due to exception happening during filtering - $callbackObject->expects($this->never()) - ->method('afterFilterCallbackMethod'); - $filter->addAfterFilterCallback([$callbackObject, 'afterFilterCallbackMethod']); - - $this->assertEquals($exceptionResult, $filter->filter($value)); - } - public function testConfigDirectiveAvailable() { $path = "web/unsecure/base_url"; diff --git a/app/code/Magento/Email/Test/Unit/Model/TemplateTest.php b/app/code/Magento/Email/Test/Unit/Model/TemplateTest.php index 12b970f623e2d..cda3a4f4a1934 100644 --- a/app/code/Magento/Email/Test/Unit/Model/TemplateTest.php +++ b/app/code/Magento/Email/Test/Unit/Model/TemplateTest.php @@ -514,14 +514,20 @@ public function testGetProcessedTemplateSubject() $templateSubject = 'templateSubject'; $model->setTemplateSubject($templateSubject); + $model->setTemplateId('123'); + class_exists(Template::class, true); $filterTemplate = $this->getMockBuilder(\Magento\Framework\Filter\Template::class) - ->setMethods(['setVariables', 'setStoreId', 'filter']) + ->setMethods(['setVariables', 'setStoreId', 'filter', 'setStrictMode']) ->disableOriginalConstructor() ->getMock(); $model->expects($this->once()) ->method('getTemplateFilter') ->willReturn($filterTemplate); + $filterTemplate->expects($this->exactly(2)) + ->method('setStrictMode') + ->withConsecutive([$this->equalTo(true)], [$this->equalTo(false)]) + ->willReturnOnConsecutiveCalls(false, true); $model->expects($this->once()) ->method('applyDesignConfig'); diff --git a/app/code/Magento/Email/composer.json b/app/code/Magento/Email/composer.json index 548d9a422c132..9070fbac7c653 100644 --- a/app/code/Magento/Email/composer.json +++ b/app/code/Magento/Email/composer.json @@ -12,6 +12,7 @@ "magento/module-config": "*", "magento/module-store": "*", "magento/module-theme": "*", + "magento/module-require-js": "*", "magento/module-media-storage": "*", "magento/module-variable": "*" }, diff --git a/app/code/Magento/Email/etc/db_schema.xml b/app/code/Magento/Email/etc/db_schema.xml index dbb8855e9e57e..82c5f18ddb9e9 100644 --- a/app/code/Magento/Email/etc/db_schema.xml +++ b/app/code/Magento/Email/etc/db_schema.xml @@ -27,6 +27,8 @@ <column xsi:type="varchar" name="orig_template_code" nullable="true" length="200" comment="Original Template Code"/> <column xsi:type="text" name="orig_template_variables" nullable="true" comment="Original Template Variables"/> + <column xsi:type="boolean" name="is_legacy" nullable="false" + default="false" comment="Should the template render in legacy mode"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="template_id"/> </constraint> diff --git a/app/code/Magento/Email/etc/di.xml b/app/code/Magento/Email/etc/di.xml index 9ec3e04728a2c..73ba21ed7537b 100644 --- a/app/code/Magento/Email/etc/di.xml +++ b/app/code/Magento/Email/etc/di.xml @@ -72,4 +72,22 @@ </argument> </arguments> </type> + <type name="Magento\Framework\Filter\Template"> + <arguments> + <argument name="directiveProcessors" xsi:type="array"> + <item name="depend" sortOrder="100" xsi:type="object">Magento\Framework\Filter\DirectiveProcessor\DependDirective</item> + <item name="if" sortOrder="200" xsi:type="object">Magento\Framework\Filter\DirectiveProcessor\IfDirective</item> + <item name="template" sortOrder="300" xsi:type="object">Magento\Framework\Filter\DirectiveProcessor\TemplateDirective</item> + <item name="legacy" sortOrder="400" xsi:type="object">Magento\Framework\Filter\DirectiveProcessor\LegacyDirective</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\Filter\DirectiveProcessor\Filter\FilterPool"> + <arguments> + <argument name="filters" xsi:type="array"> + <item name="nl2br" xsi:type="object">Magento\Framework\Filter\DirectiveProcessor\Filter\NewlineToBreakFilter</item> + <item name="escape" xsi:type="object">Magento\Framework\Filter\DirectiveProcessor\Filter\EscapeFilter</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Email/registration.php b/app/code/Magento/Email/registration.php index 70601ab3d3b04..132f509e93cc0 100644 --- a/app/code/Magento/Email/registration.php +++ b/app/code/Magento/Email/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Email', __DIR__); diff --git a/app/code/Magento/Email/view/adminhtml/layout/adminhtml_email_template_preview.xml b/app/code/Magento/Email/view/adminhtml/layout/adminhtml_email_template_preview.xml index e7cbc675ce386..886a76b3af6f8 100644 --- a/app/code/Magento/Email/view/adminhtml/layout/adminhtml_email_template_preview.xml +++ b/app/code/Magento/Email/view/adminhtml/layout/adminhtml_email_template_preview.xml @@ -17,6 +17,7 @@ <argument name="preview_form_view_model" xsi:type="object">Magento\Email\ViewModel\Template\Preview\Form</argument> </arguments> </block> + <block class="Magento\RequireJs\Block\Html\Head\Config" name="requirejs-config"/> </referenceContainer> </body> </page> diff --git a/app/code/Magento/Email/view/frontend/email/footer.html b/app/code/Magento/Email/view/frontend/email/footer.html index 40e18372252bc..b9db3b8597cad 100644 --- a/app/code/Magento/Email/view/frontend/email/footer.html +++ b/app/code/Magento/Email/view/frontend/email/footer.html @@ -6,7 +6,7 @@ --> <!--@subject {{trans "Footer"}} @--> <!--@vars { -"var store.getFrontendName()":"Store Name" +"var store.frontend_name":"Store Name" } @--> <!-- End Content --> @@ -14,7 +14,7 @@ </tr> <tr> <td class="footer"> - <p class="closing">{{trans "Thank you, %store_name" store_name=$store.getFrontendName()}}!</p> + <p class="closing">{{trans "Thank you, %store_name" store_name=$store.frontend_name}}!</p> </td> </tr> </table> diff --git a/app/code/Magento/Email/view/frontend/email/header.html b/app/code/Magento/Email/view/frontend/email/header.html index c4f49698dc69b..45e299cb51d17 100644 --- a/app/code/Magento/Email/view/frontend/email/header.html +++ b/app/code/Magento/Email/view/frontend/email/header.html @@ -6,6 +6,8 @@ --> <!--@subject {{trans "Header"}} @--> <!--@vars { +"var logo_url":"Email Logo Image URL", +"var logo_alt":"Email Logo Alt Text", "var logo_height":"Email Logo Image Height", "var logo_width":"Email Logo Image Width", "var template_styles|raw":"Template CSS" diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/ActionGroup/AdminEncryptionKeyChangeKeyAutoActionGroup.xml b/app/code/Magento/EncryptionKey/Test/Mftf/ActionGroup/AdminEncryptionKeyChangeKeyAutoActionGroup.xml new file mode 100644 index 0000000000000..62568ebb551e1 --- /dev/null +++ b/app/code/Magento/EncryptionKey/Test/Mftf/ActionGroup/AdminEncryptionKeyChangeKeyAutoActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminEncryptionKeyChangeKeyAutoActionGroup"> + <annotations> + <description>Change Encryption Key Auto Generate Action Group.</description> + </annotations> + <arguments> + <argument name="encryptionKeyData" defaultValue="AdminEncryptionKeyAutoGenerate"/> + </arguments> + + <selectOption selector="{{AdminEncryptionKeyChangeFormSection.autoGenerate}}" userInput="{{encryptionKeyData.autoGenerate}}" stepKey="selectGenerateMode"/> + <click selector="{{AdminEncryptionKeyChangeFormSection.changeEncryptionKey}}" stepKey="clickChangeButton"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The encryption key has been changed." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/ActionGroup/AdminEncryptionKeyChangeKeyManualActionGroup.xml b/app/code/Magento/EncryptionKey/Test/Mftf/ActionGroup/AdminEncryptionKeyChangeKeyManualActionGroup.xml new file mode 100644 index 0000000000000..0880bd07a0739 --- /dev/null +++ b/app/code/Magento/EncryptionKey/Test/Mftf/ActionGroup/AdminEncryptionKeyChangeKeyManualActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminEncryptionKeyChangeKeyManualActionGroup"> + <annotations> + <description>Change Encryption Key - No-Auto Generate Action Group.</description> + </annotations> + <arguments> + <argument name="encryptionKeyData" defaultValue="AdminEncryptionKeyManualGenerate"/> + </arguments> + + <selectOption selector="{{AdminEncryptionKeyChangeFormSection.autoGenerate}}" userInput="{{encryptionKeyData.autoGenerate}}" stepKey="selectGenerateMode"/> + <fillField selector="{{AdminEncryptionKeyChangeFormSection.cryptKey}}" userInput="{{encryptionKeyData.cryptKey}}" stepKey="fillCryptKey"/> + <click selector="{{AdminEncryptionKeyChangeFormSection.changeEncryptionKey}}" stepKey="clickChangeButton"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The encryption key has been changed." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/ActionGroup/AdminEncryptionKeyNavigateToChangePageActionGroup.xml b/app/code/Magento/EncryptionKey/Test/Mftf/ActionGroup/AdminEncryptionKeyNavigateToChangePageActionGroup.xml new file mode 100644 index 0000000000000..69847526a15a0 --- /dev/null +++ b/app/code/Magento/EncryptionKey/Test/Mftf/ActionGroup/AdminEncryptionKeyNavigateToChangePageActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminEncryptionKeyNavigateToChangePageActionGroup"> + <annotations> + <description>Navigate to change encryption key page.</description> + </annotations> + <amOnPage url="{{AdminEncryptionKeyChangeFormPage.url}}" stepKey="navigateToChangeEncryptionPage" /> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/Data/AdminEncryptionKeyData.xml b/app/code/Magento/EncryptionKey/Test/Mftf/Data/AdminEncryptionKeyData.xml new file mode 100644 index 0000000000000..ff1fe3fd2e10c --- /dev/null +++ b/app/code/Magento/EncryptionKey/Test/Mftf/Data/AdminEncryptionKeyData.xml @@ -0,0 +1,18 @@ +<?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="AdminEncryptionKeyAutoGenerate"> + <data key="autoGenerate">Yes</data> + </entity> + <entity name="AdminEncryptionKeyManualGenerate"> + <data key="autoGenerate">No</data> + <data key="cryptKey">9d469ae32ec27dfec0206cb5d63f135d</data> + </entity> +</entities> diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/Page/AdminEncryptionKeyChangeFormPage.xml b/app/code/Magento/EncryptionKey/Test/Mftf/Page/AdminEncryptionKeyChangeFormPage.xml new file mode 100644 index 0000000000000..b3b8b33fc0364 --- /dev/null +++ b/app/code/Magento/EncryptionKey/Test/Mftf/Page/AdminEncryptionKeyChangeFormPage.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminEncryptionKeyChangeFormPage" url="admin/crypt_key/index" area="admin" module="Magento_EncryptionKey"> + <section name="AdminCustomerConfigSection"/> + </page> +</pages> diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/Section/AdminEncryptionKeyChangeFormSection.xml b/app/code/Magento/EncryptionKey/Test/Mftf/Section/AdminEncryptionKeyChangeFormSection.xml new file mode 100644 index 0000000000000..7ce37af60fd7f --- /dev/null +++ b/app/code/Magento/EncryptionKey/Test/Mftf/Section/AdminEncryptionKeyChangeFormSection.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="AdminEncryptionKeyChangeFormSection"> + <element name="autoGenerate" type="select" selector="#generate_random"/> + <element name="cryptKey" type="input" selector="#crypt_key"/> + <element name="changeEncryptionKey" type="button" selector=".page-actions-buttons #save" timeout="10"/> + </section> +</sections> diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyAutoGenerateKeyTest.xml b/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyAutoGenerateKeyTest.xml new file mode 100644 index 0000000000000..ded57f4aad019 --- /dev/null +++ b/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyAutoGenerateKeyTest.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminEncryptionKeyAutoGenerateKeyTest"> + <annotations> + <features value="Encryption Key"/> + <stories value="Change Encryption Key"/> + <title value="Change Encryption Key by Auto Generate Key"/> + <description value="Change Encryption Key by Auto Generate Key"/> + <severity value="CRITICAL"/> + <group value="encryption_key"/> + </annotations> + + <before> + <!--Login to Admin Area--> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + </before> + + <after> + <!--Logout from Admin Area--> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + </after> + + <actionGroup ref="AdminEncryptionKeyNavigateToChangePageActionGroup" stepKey="navigateToPage"/> + <actionGroup ref="AdminEncryptionKeyChangeKeyAutoActionGroup" stepKey="changeKeyAutoGenerate"/> + </test> +</tests> diff --git a/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyManualGenerateKeyTest.xml b/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyManualGenerateKeyTest.xml new file mode 100644 index 0000000000000..f3a9849969263 --- /dev/null +++ b/app/code/Magento/EncryptionKey/Test/Mftf/Test/AdminEncryptionKeyManualGenerateKeyTest.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminEncryptionKeyManualGenerateKeyTest"> + <annotations> + <features value="Encryption Key"/> + <stories value="Change Encryption Key"/> + <title value="Change Encryption Key by Manual Key"/> + <description value="Change Encryption Key by Manual Key"/> + <severity value="CRITICAL"/> + <group value="encryption_key"/> + </annotations> + + <before> + <!--Login to Admin Area--> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> + </before> + + <after> + <!--Logout from Admin Area--> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + </after> + + <actionGroup ref="AdminEncryptionKeyNavigateToChangePageActionGroup" stepKey="navigateToPage"/> + <actionGroup ref="AdminEncryptionKeyChangeKeyManualActionGroup" stepKey="changeKeyManualGenerate"/> + </test> +</tests> diff --git a/app/code/Magento/EncryptionKey/registration.php b/app/code/Magento/EncryptionKey/registration.php index 4426a9200e571..773cf44a207c4 100644 --- a/app/code/Magento/EncryptionKey/registration.php +++ b/app/code/Magento/EncryptionKey/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_EncryptionKey', __DIR__); diff --git a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml index 91a76383babd4..928ce5a2a918f 100644 --- a/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml +++ b/app/code/Magento/Fedex/Test/Mftf/Test/AdminCreatingShippingLabelTest.xml @@ -83,7 +83,7 @@ <actionGroup ref="AdminFillProductCountryOfManufactureActionGroup" stepKey="fillCountryOfManufacture"> <argument name="countryId" value="DE"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveSimpleProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSimpleProduct"/> <!--Place for order using FedEx shipping method--> <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="amOnStorefrontProductPage"/> <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="addProductToCart"> @@ -104,7 +104,7 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> <!--Open created order in admin--> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> - <actionGroup ref="searchAdminDataGridByKeyword" stepKey="searchOrder"> + <actionGroup ref="SearchAdminDataGridByKeywordActionGroup" stepKey="searchOrder"> <argument name="keyword" value="$grabOrderNumber"/> </actionGroup> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> diff --git a/app/code/Magento/Fedex/registration.php b/app/code/Magento/Fedex/registration.php index bb44e20ec4674..3562091f9ba68 100644 --- a/app/code/Magento/Fedex/registration.php +++ b/app/code/Magento/Fedex/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Fedex', __DIR__); diff --git a/app/code/Magento/GiftMessage/Model/Plugin/OrderItemGet.php b/app/code/Magento/GiftMessage/Model/Plugin/OrderItemGet.php new file mode 100644 index 0000000000000..3de69da37eef8 --- /dev/null +++ b/app/code/Magento/GiftMessage/Model/Plugin/OrderItemGet.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GiftMessage\Model\Plugin; + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\GiftMessage\Api\OrderItemRepositoryInterface as GiftMessageItemRepositoryInterface; +use Magento\Sales\Api\Data\OrderItemExtensionFactory; +use Magento\Sales\Api\Data\OrderItemInterface; +use Magento\Sales\Api\OrderItemRepositoryInterface; + +/** + * Plugin for adding gift message to order item + */ +class OrderItemGet +{ + + /** + * @var OrderItemExtensionFactory + */ + private $orderItemExtensionFactory; + + /** + * @var GiftMessageItemRepositoryInterface + */ + private $giftMessageItemRepository; + + /** + * OrderItemGet constructor. + * + * @param GiftMessageItemRepositoryInterface $giftMessageItemRepository + * @param OrderItemExtensionFactory $orderItemExtensionFactory + */ + public function __construct( + GiftMessageItemRepositoryInterface $giftMessageItemRepository, + OrderItemExtensionFactory $orderItemExtensionFactory + ) { + $this->giftMessageItemRepository = $giftMessageItemRepository; + $this->orderItemExtensionFactory = $orderItemExtensionFactory; + } + + /** + * Add gift message for order item + * + * @param OrderItemRepositoryInterface $subject + * @param OrderItemInterface $orderItem + * @return OrderItemInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterGet(OrderItemRepositoryInterface $subject, OrderItemInterface $orderItem) + { + $extensionAttributes = $orderItem->getExtensionAttributes(); + if ($extensionAttributes && $extensionAttributes->getGiftMessage()) { + return $orderItem; + } + try { + /* @var \Magento\GiftMessage\Api\Data\MessageInterface $giftMessage */ + $giftMessage = $this->giftMessageItemRepository->get( + $orderItem->getOrderId(), + $orderItem->getItemId() + ); + } catch (NoSuchEntityException $e) { + return $orderItem; + } + /** @var \Magento\Sales\Api\Data\OrderItemExtension $orderItemExtension */ + $orderItemExtension = $extensionAttributes ?: $this->orderItemExtensionFactory->create(); + $orderItemExtension->setGiftMessage($giftMessage); + $orderItem->setExtensionAttributes($orderItemExtension); + + return $orderItem; + } +} diff --git a/app/code/Magento/GiftMessage/Test/Unit/Observer/SalesEventOrderItemToQuoteItemObserverTest.php b/app/code/Magento/GiftMessage/Test/Unit/Observer/SalesEventOrderItemToQuoteItemObserverTest.php new file mode 100644 index 0000000000000..5ac75caa4b512 --- /dev/null +++ b/app/code/Magento/GiftMessage/Test/Unit/Observer/SalesEventOrderItemToQuoteItemObserverTest.php @@ -0,0 +1,225 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\GiftMessage\Test\Unit\Observer; + +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Framework\Message\MessageInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\GiftMessage\Helper\Message as MessageHelper; +use Magento\GiftMessage\Model\Message as MessageModel; +use Magento\GiftMessage\Model\MessageFactory; +use Magento\GiftMessage\Observer\SalesEventOrderItemToQuoteItemObserver; +use Magento\Quote\Model\Quote\Item as QuoteItem; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Item as OrderItem; +use Magento\Store\Model\Store; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class SalesEventOrderItemToQuoteItemObserverTest extends TestCase +{ + /** + * Stub message id + */ + private const STUB_MESSAGE_ID = 1; + + /** + * Stub new message id + */ + private const STUB_NEW_MESSAGE_ID = 2; + + /** + * @var SalesEventOrderItemToQuoteItemObserver + */ + private $observer; + + /** + * @var MessageFactory|MockObject + */ + private $messageFactoryMock; + + /** + * @var MessageHelper|MockObject + */ + private $giftMessageHelperMock; + + /** + * @var Observer|MockObject + */ + private $observerMock; + + /** + * @var Event|MockObject + */ + private $eventMock; + + /** + * @var Order|MockObject + */ + private $orderMock; + + /** + * @var OrderItem|MockObject + */ + private $orderItemMock; + + /** + * @var Store|MockObject + */ + private $storeMock; + + /** + * @var MessageInterface|MockObject + */ + private $messageMock; + + /** + * @var QuoteItem|MockObject + */ + private $quoteItemMock; + + /** + * Prepare environment for test + */ + public function setUp(): void + { + $this->messageFactoryMock = $this->getMockBuilder(MessageFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->giftMessageHelperMock = $this->createMock(MessageHelper::class); + $this->observerMock = $this->createMock(Observer::class); + $this->eventMock = $this->createPartialMock(Event::class, ['getOrderItem', 'getQuoteItem']); + $this->orderItemMock = $this->createPartialMock( + OrderItem::class, + ['getOrder', 'getStoreId', 'getGiftMessageId'] + ); + $this->quoteItemMock = $this->createPartialMock(QuoteItem::class, ['setGiftMessageId']); + $this->orderMock = $this->createPartialMock(Order::class, ['getReordered']); + $this->storeMock = $this->createMock(Store::class); + $this->messageMock = $this->createMock(MessageModel::class); + + $this->eventMock->expects($this->atLeastOnce()) + ->method('getOrderItem') + ->willReturn($this->orderItemMock); + + $this->orderItemMock->expects($this->atLeastOnce()) + ->method('getOrder') + ->willReturn($this->orderMock); + + $this->observerMock->expects($this->atLeastOnce()) + ->method('getEvent') + ->willReturn($this->eventMock); + + $objectManager = new ObjectManager($this); + + $this->observer = $objectManager->getObject( + SalesEventOrderItemToQuoteItemObserver::class, + [ + 'messageFactory' => $this->messageFactoryMock, + 'giftMessageMessage' => $this->giftMessageHelperMock + ] + ); + } + + /** + * Test when the order is reorder + */ + public function testReorder() + { + $this->orderMock->expects($this->once()) + ->method('getReordered') + ->willReturn(true); + + $this->giftMessageHelperMock->expects($this->never()) + ->method('isMessagesAllowed'); + + $this->eventMock + ->expects($this->never()) + ->method('getQuoteItem') + ->willReturn($this->quoteItemMock); + + /** Run observer */ + $this->observer->execute($this->observerMock); + } + + /** + * Test when the order is new reorder and gift message is not allowed + */ + public function testNewOrderWhenGiftMessageIsNotAllowed() + { + $this->orderMock->expects($this->once()) + ->method('getReordered') + ->willReturn(false); + + $this->giftMessageHelperMock->expects($this->once()) + ->method('isMessagesAllowed') + ->willReturn(false); + + $this->eventMock + ->expects($this->never()) + ->method('getQuoteItem') + ->willReturn($this->quoteItemMock); + + /** Run observer */ + $this->observer->execute($this->observerMock); + } + + /** + * Test when the order is new reorder and gift message is allowed + */ + public function testNewOrderWhenGiftMessageIsAllowed() + { + $this->orderMock->expects($this->once()) + ->method('getReordered') + ->willReturn(false); + + $this->giftMessageHelperMock->expects($this->once()) + ->method('isMessagesAllowed') + ->willReturn(true); + + $this->eventMock + ->expects($this->atLeastOnce()) + ->method('getQuoteItem') + ->willReturn($this->quoteItemMock); + + $this->orderItemMock->expects($this->once()) + ->method('getGiftMessageId') + ->willReturn(self::STUB_MESSAGE_ID); + + $this->messageFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->messageMock); + $this->messageMock->expects($this->once()) + ->method('load') + ->with(self::STUB_MESSAGE_ID) + ->willReturnSelf(); + $this->messageMock->expects($this->once()) + ->method('setId') + ->with(null) + ->willReturnSelf(); + $this->messageMock->expects($this->once()) + ->method('save') + ->willReturnSelf(); + $this->messageMock->expects($this->once()) + ->method('getId') + ->willReturn(self::STUB_NEW_MESSAGE_ID); + $this->quoteItemMock->expects($this->once()) + ->method('setGiftMessageId') + ->with(self::STUB_NEW_MESSAGE_ID) + ->willReturnSelf(); + + /** Run observer */ + $this->observer->execute($this->observerMock); + } +} diff --git a/app/code/Magento/GiftMessage/Test/Unit/Observer/SalesEventOrderToQuoteObserverTest.php b/app/code/Magento/GiftMessage/Test/Unit/Observer/SalesEventOrderToQuoteObserverTest.php new file mode 100644 index 0000000000000..85c062d9cec11 --- /dev/null +++ b/app/code/Magento/GiftMessage/Test/Unit/Observer/SalesEventOrderToQuoteObserverTest.php @@ -0,0 +1,179 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\GiftMessage\Test\Unit\Observer; + +use Magento\Framework\Event; +use Magento\Framework\Message\MessageInterface; +use Magento\GiftMessage\Helper\Message; +use Magento\GiftMessage\Model\Message as MessageModel; +use Magento\GiftMessage\Model\MessageFactory; +use Magento\GiftMessage\Observer\SalesEventOrderToQuoteObserver; +use Magento\Framework\Event\Observer; +use Magento\Quote\Model\Quote; +use Magento\Sales\Model\Order; +use Magento\Store\Model\Store; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * SalesEventOrderToQuoteObserverTest + */ +class SalesEventOrderToQuoteObserverTest extends TestCase +{ + /** + * @var SalesEventOrderToQuoteObserver + */ + private $observer; + + /** + * @var MessageFactory|MockObject + */ + private $messageFactoryMock; + + /** + * @var Message|MockObject + */ + private $giftMessageMock; + + /** + * @var Observer|MockObject + */ + private $observerMock; + + /** + * @var Event|MockObject + */ + private $eventMock; + + /** + * @var Order|MockObject + */ + private $orderMock; + + /** + * @var Store|MockObject + */ + private $storeMock; + + /** + * @var MessageInterface|MockObject + */ + private $messageMock; + + /** + * @var Quote|MockObject + */ + private $quoteMock; + + /** + * @return void + */ + public function setUp(): void + { + $this->messageFactoryMock = $this->createMock(MessageFactory::class); + $this->giftMessageMock = $this->createMock(Message::class); + $this->observerMock = $this->createMock(Observer::class); + $this->eventMock = $this->getMockBuilder(Event::class) + ->disableOriginalConstructor() + ->setMethods(['getOrder', 'getQuote']) + ->getMock(); + $this->orderMock = $this->getMockBuilder(Order::class) + ->setMethods(['getReordered', 'getStore', 'getGiftMessageId']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->quoteMock = $this->getMockBuilder(Quote::class) + ->setMethods(['setGiftMessageId']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->storeMock = $this->createMock(Store::class); + $this->messageMock = $this->createMock(MessageModel::class); + + $this->observer = new SalesEventOrderToQuoteObserver( + $this->messageFactoryMock, + $this->giftMessageMock + ); + } + + /** + * Tests duplicating gift message from order to quote + * + * @dataProvider giftMessageDataProvider + * + * @param bool $orderIsReordered + * @param bool $isMessagesAllowed + */ + public function testExecute(bool $orderIsReordered, bool $isMessagesAllowed): void + { + $giftMessageId = 1; + $newGiftMessageId = 2; + + $this->eventMock + ->expects($this->atLeastOnce()) + ->method('getOrder') + ->willReturn($this->orderMock); + $this->observerMock + ->expects($this->atLeastOnce()) + ->method('getEvent') + ->willReturn($this->eventMock); + + if (!$orderIsReordered && $isMessagesAllowed) { + $this->eventMock + ->expects($this->atLeastOnce()) + ->method('getQuote') + ->willReturn($this->quoteMock); + $this->orderMock->expects($this->once()) + ->method('getReordered') + ->willReturn($orderIsReordered); + $this->orderMock->expects($this->once()) + ->method('getGiftMessageId') + ->willReturn($giftMessageId); + $this->giftMessageMock->expects($this->once()) + ->method('isMessagesAllowed') + ->willReturn($isMessagesAllowed); + $this->messageFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->messageMock); + $this->messageMock->expects($this->once()) + ->method('load') + ->with($giftMessageId) + ->willReturnSelf(); + $this->messageMock->expects($this->once()) + ->method('setId') + ->with(null) + ->willReturnSelf(); + $this->messageMock->expects($this->once()) + ->method('save') + ->willReturnSelf(); + $this->messageMock->expects($this->once()) + ->method('getId') + ->willReturn($newGiftMessageId); + $this->quoteMock->expects($this->once()) + ->method('setGiftMessageId') + ->with($newGiftMessageId) + ->willReturnSelf(); + } + + $this->observer->execute($this->observerMock); + } + + /** + * Providing gift message data + * + * @return array + */ + public function giftMessageDataProvider(): array + { + return [ + [false, true], + [true, true], + [false, true], + [false, false], + ]; + } +} diff --git a/app/code/Magento/GiftMessage/Ui/DataProvider/Product/Modifier/GiftMessage.php b/app/code/Magento/GiftMessage/Ui/DataProvider/Product/Modifier/GiftMessage.php index e3d617eac1cd2..fe2479d778992 100644 --- a/app/code/Magento/GiftMessage/Ui/DataProvider/Product/Modifier/GiftMessage.php +++ b/app/code/Magento/GiftMessage/Ui/DataProvider/Product/Modifier/GiftMessage.php @@ -53,7 +53,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyData(array $data) { @@ -73,7 +73,7 @@ public function modifyData(array $data) } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyMeta(array $meta) { @@ -101,24 +101,28 @@ protected function customizeAllowGiftMessageField(array $meta) 'children' ); $fieldPath = $this->arrayManager->findPath(static::FIELD_MESSAGE_AVAILABLE, $meta, null, 'children'); - $groupConfig = $this->arrayManager->get($containerPath, $meta); $fieldConfig = $this->arrayManager->get($fieldPath, $meta); - $meta = $this->arrayManager->merge($containerPath, $meta, [ - 'arguments' => [ - 'data' => [ - 'config' => [ - 'formElement' => 'container', - 'componentType' => 'container', - 'component' => 'Magento_Ui/js/form/components/group', - 'label' => $groupConfig['arguments']['data']['config']['label'], - 'breakLine' => false, - 'sortOrder' => $fieldConfig['arguments']['data']['config']['sortOrder'], - 'dataScope' => '', + $meta = $this->arrayManager->merge( + $containerPath, + $meta, + [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'formElement' => 'container', + 'componentType' => 'container', + 'component' => 'Magento_Ui/js/form/components/group', + 'label' => false, + 'required' => false, + 'breakLine' => false, + 'sortOrder' => $fieldConfig['arguments']['data']['config']['sortOrder'], + 'dataScope' => '', + ], ], ], - ], - ]); + ] + ); $meta = $this->arrayManager->merge( $containerPath, $meta, diff --git a/app/code/Magento/GiftMessage/composer.json b/app/code/Magento/GiftMessage/composer.json index 1aaad24837719..4d56514f365c1 100644 --- a/app/code/Magento/GiftMessage/composer.json +++ b/app/code/Magento/GiftMessage/composer.json @@ -17,6 +17,7 @@ "magento/module-ui": "*" }, "suggest": { + "magento/module-eav": "*", "magento/module-multishipping": "*" }, "type": "magento2-module", diff --git a/app/code/Magento/GiftMessage/etc/di.xml b/app/code/Magento/GiftMessage/etc/di.xml index 1d03849d978b8..1b079f3c9fd55 100644 --- a/app/code/Magento/GiftMessage/etc/di.xml +++ b/app/code/Magento/GiftMessage/etc/di.xml @@ -28,4 +28,16 @@ <plugin name="save_gift_message" type="Magento\GiftMessage\Model\Plugin\OrderSave"/> <plugin name="get_gift_message" type="Magento\GiftMessage\Model\Plugin\OrderGet"/> </type> + <type name="Magento\Eav\Model\Config"> + <arguments> + <argument name="attributesForPreload" xsi:type="array"> + <item name="catalog_product" xsi:type="array"> + <item name="gift_message_available" xsi:type="string">catalog_product</item> + </item> + </argument> + </arguments> + </type> + <type name="Magento\Sales\Api\OrderItemRepositoryInterface"> + <plugin name="get_gift_message" type="Magento\GiftMessage\Model\Plugin\OrderItemGet"/> + </type> </config> diff --git a/app/code/Magento/GiftMessage/registration.php b/app/code/Magento/GiftMessage/registration.php index c7268d09cf929..78676ef9c32d0 100644 --- a/app/code/Magento/GiftMessage/registration.php +++ b/app/code/Magento/GiftMessage/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_GiftMessage', __DIR__); diff --git a/app/code/Magento/GoogleAdwords/registration.php b/app/code/Magento/GoogleAdwords/registration.php index 76aa83a3a55f9..e6ef3f7625f19 100644 --- a/app/code/Magento/GoogleAdwords/registration.php +++ b/app/code/Magento/GoogleAdwords/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_GoogleAdwords', __DIR__); diff --git a/app/code/Magento/GoogleAnalytics/Test/Unit/Helper/DataTest.php b/app/code/Magento/GoogleAnalytics/Test/Unit/Helper/DataTest.php new file mode 100644 index 0000000000000..4da5b70e10b91 --- /dev/null +++ b/app/code/Magento/GoogleAnalytics/Test/Unit/Helper/DataTest.php @@ -0,0 +1,120 @@ +<?php + +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\GoogleAnalytics\Test\Unit\Helper; + +use Magento\GoogleAnalytics\Helper\Data as HelperData; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; + +/** + * Unit test for Magento\GoogleAnalytics\Helper\Data + */ +class DataTest extends TestCase +{ + /** + * @var HelperData + */ + private $helper; + + /** + * @var ScopeConfigInterface|MockObject + */ + private $scopeConfigMock; + + /** + * @inheritDoc + */ + protected function setUp(): void + { + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) + ->setMethods(['getValue', 'isSetFlag']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $objectManager = new ObjectManager($this); + $this->helper = $objectManager->getObject( + HelperData::class, + [ + 'scopeConfig' => $this->scopeConfigMock + ] + ); + } + + /** + * Test for isGoogleAnalyticsAvailable() + * + * @param string $value + * @param bool $flag + * @param bool $result + * @return void + * @dataProvider gaDataProvider + */ + public function testIsGoogleAnalyticsAvailable($value, $flag, $result): void + { + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->with(HelperData::XML_PATH_ACCOUNT, ScopeInterface::SCOPE_STORE) + ->willReturn($value); + + $this->scopeConfigMock->expects($this->any()) + ->method('isSetFlag') + ->with(HelperData::XML_PATH_ACTIVE, ScopeInterface::SCOPE_STORE) + ->willReturn($flag); + + $this->assertEquals($result, $this->helper->isGoogleAnalyticsAvailable()); + } + + /** + * Data provider for isGoogleAnalyticsAvailable() + * + * @return array + */ + public function gaDataProvider(): array + { + return [ + ['GA-XXXX', true, true], + ['GA-XXXX', false, false], + ['', true, false] + ]; + } + + /** + * Test for isAnonymizedIpActive() + * + * @param string $value + * @param bool $result + * @return void + * @dataProvider yesNoDataProvider + */ + public function testIsAnonymizedIpActive($value, $result): void + { + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->with(HelperData::XML_PATH_ANONYMIZE, ScopeInterface::SCOPE_STORE) + ->willReturn($value); + $this->assertEquals($result, $this->helper->isAnonymizedIpActive()); + } + + /** + * Data provider for isAnonymizedIpActive() + * + * @return array + */ + public function yesNoDataProvider(): array + { + return [ + ['Yes' => '1', 'result' => true], + ['No' => '0', 'result' => false] + ]; + } +} diff --git a/app/code/Magento/GoogleAnalytics/registration.php b/app/code/Magento/GoogleAnalytics/registration.php index c2a8471a50ba2..6eae65b9d159e 100644 --- a/app/code/Magento/GoogleAnalytics/registration.php +++ b/app/code/Magento/GoogleAnalytics/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_GoogleAnalytics', __DIR__); diff --git a/app/code/Magento/GoogleOptimizer/registration.php b/app/code/Magento/GoogleOptimizer/registration.php index 36b94ef06eb18..4ba336ece8916 100644 --- a/app/code/Magento/GoogleOptimizer/registration.php +++ b/app/code/Magento/GoogleOptimizer/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_GoogleOptimizer', __DIR__); diff --git a/app/code/Magento/GraphQl/etc/schema.graphqls b/app/code/Magento/GraphQl/etc/schema.graphqls index 559ccf9428929..fccde015c3388 100644 --- a/app/code/Magento/GraphQl/etc/schema.graphqls +++ b/app/code/Magento/GraphQl/etc/schema.graphqls @@ -113,7 +113,7 @@ enum CurrencyEnum @doc(description: "The list of available currency codes") { BHD BDT BBD - BYR + BYN BZD BMD BTN diff --git a/app/code/Magento/GroupedCatalogInventory/registration.php b/app/code/Magento/GroupedCatalogInventory/registration.php index 8899a48edf6d5..f9b2729acb45a 100644 --- a/app/code/Magento/GroupedCatalogInventory/registration.php +++ b/app/code/Magento/GroupedCatalogInventory/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_GroupedCatalogInventory', __DIR__); diff --git a/app/code/Magento/GroupedImportExport/registration.php b/app/code/Magento/GroupedImportExport/registration.php index 90beb26820c47..de4aec530794e 100644 --- a/app/code/Magento/GroupedImportExport/registration.php +++ b/app/code/Magento/GroupedImportExport/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_GroupedImportExport', __DIR__); diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AdminAssignProductToGroupActionGroup.xml b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AdminAssignProductToGroupActionGroup.xml new file mode 100644 index 0000000000000..fa1b24616ee6f --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AdminAssignProductToGroupActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssignProductToGroupActionGroup"> + <annotations> + <description>Adds the provided Product to a Grouped Product on an Admin Grouped Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <scrollTo selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" x="0" y="-100" stepKey="scrollToGroupedSection"/> + <conditionalClick selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" dependentSelector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" visible="false" stepKey="openGroupedProductsSection"/> + <click selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="clickAddProductsToGroup"/> + <conditionalClick selector="{{AdminAddProductsToGroupPanel.clearFilters}}" dependentSelector="{{AdminAddProductsToGroupPanel.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> + <click selector="{{AdminAddProductsToGroupPanel.filters}}" stepKey="showFiltersPanel"/> + <fillField userInput="{{product.name}}" selector="{{AdminAddProductsToGroupPanel.nameFilter}}" stepKey="fillNameFilter"/> + <click selector="{{AdminAddProductsToGroupPanel.applyFilters}}" stepKey="clickApplyFilters"/> + <click selector="{{AdminAddProductsToGroupPanel.firstCheckbox}}" stepKey="selectProduct"/> + <click selector="{{AdminAddProductsToGroupPanel.addSelectedProducts}}" stepKey="clickAddSelectedGroupProducts"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AdminGroupedProductActionGroup.xml b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AdminGroupedProductActionGroup.xml deleted file mode 100644 index 7b44697880a70..0000000000000 --- a/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AdminGroupedProductActionGroup.xml +++ /dev/null @@ -1,92 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Check that required fields are actually required--> - <actionGroup name="checkRequiredFieldsInGroupedProductForm"> - <annotations> - <description>Clears the Product Name and SKU fields when adding a Grouped Product and then verifies that they are required after attempting to Save.</description> - </annotations> - - <clearField selector="{{AdminProductFormSection.productName}}" stepKey="clearProductSku"/> - <clearField selector="{{AdminProductFormSection.productSku}}" stepKey="clearProductName"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Product" stepKey="seeStillOnEditPage"/> - <see selector="{{AdminProductFormSection.fieldError('name')}}" userInput="This is a required field." stepKey="seeNameRequired"/> - <see selector="{{AdminProductFormSection.fieldError('sku')}}" userInput="This is a required field." stepKey="seeSkuRequired"/> - </actionGroup> - - <!--Fill main fields in grouped product form--> - <actionGroup name="fillGroupedProductForm"> - <annotations> - <description>Fills in the provided Product Name and SKU on the Grouped Product creation/edit page.</description> - </annotations> - <arguments> - <argument name="product" defaultValue="GroupedProduct"/> - </arguments> - - <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{product.name}}" stepKey="fillProductSku"/> - <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{product.sku}}" stepKey="fillProductName"/> - </actionGroup> - - <!--Filter product grid and see expected grouped product--> - <actionGroup name="viewGroupedProductInAdminGrid"> - <annotations> - <description>Goes to the Admin Products grid page. Filters the grid for the provided Product. Validates that the provided Product appears in the grid.</description> - </annotations> - <arguments> - <argument name="product" defaultValue="GroupedProduct"/> - </arguments> - - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> - <waitForPageLoad stepKey="waitForPageLoadInitial"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> - <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{product.name}}" stepKey="fillProductNameFilter"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillProductSkuFilter"/> - <selectOption selector="{{AdminProductGridFilterSection.typeFilter}}" userInput="{{product.type_id}}" stepKey="selectionProductType"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> - <see selector="{{AdminProductGridSection.firstProductRow}}" userInput="{{product.name}}" stepKey="seeProductNameInGrid"/> - <click selector="{{AdminProductGridFilterSection.clearFilters}}" stepKey="clickClearFiltersAfter"/> - </actionGroup> - - <!--Fill product min quantity in group products grid--> - <actionGroup name="fillDefaultQuantityForLinkedToGroupProductInGrid"> - <annotations> - <description>Fills the provided Qty for a Product linked to a Grouped Product.</description> - </annotations> - <arguments> - <argument name="productName" type="string"/> - <argument name="qty" type="string"/> - </arguments> - - <fillField selector="{{AdminAddedProductsToGroupGrid.inputByProductName(productName)}}" userInput="{{qty}}" stepKey="fillDefaultQtyForLinkedProduct"/> - </actionGroup> - - <!-- Assign Specified Product To Grouped Product --> - <!-- Assumes web client is on grouped product edit page --> - <actionGroup name="AdminAssignProductToGroup"> - <annotations> - <description>Adds the provided Product to a Grouped Product on an Admin Grouped Product creation/edit page.</description> - </annotations> - <arguments> - <argument name="product"/> - </arguments> - - <scrollTo selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" x="0" y="-100" stepKey="scrollToGroupedSection"/> - <conditionalClick selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" dependentSelector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" visible="false" stepKey="openGroupedProductsSection"/> - <click selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="clickAddProductsToGroup"/> - <conditionalClick selector="{{AdminAddProductsToGroupPanel.clearFilters}}" dependentSelector="{{AdminAddProductsToGroupPanel.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> - <click selector="{{AdminAddProductsToGroupPanel.filters}}" stepKey="showFiltersPanel"/> - <fillField userInput="{{product.name}}" selector="{{AdminAddProductsToGroupPanel.nameFilter}}" stepKey="fillNameFilter"/> - <click selector="{{AdminAddProductsToGroupPanel.applyFilters}}" stepKey="clickApplyFilters"/> - <click selector="{{AdminAddProductsToGroupPanel.firstCheckbox}}" stepKey="selectProduct"/> - <click selector="{{AdminAddProductsToGroupPanel.addSelectedProducts}}" stepKey="clickAddSelectedGroupProducts"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/CheckRequiredFieldsInGroupedProductFormActionGroup.xml b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/CheckRequiredFieldsInGroupedProductFormActionGroup.xml new file mode 100644 index 0000000000000..84bb291120ee5 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/CheckRequiredFieldsInGroupedProductFormActionGroup.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"> + <!--Check that required fields are actually required--> + <actionGroup name="CheckRequiredFieldsInGroupedProductFormActionGroup"> + <annotations> + <description>Clears the Product Name and SKU fields when adding a Grouped Product and then verifies that they are required after attempting to Save.</description> + </annotations> + + <clearField selector="{{AdminProductFormSection.productName}}" stepKey="clearProductSku"/> + <clearField selector="{{AdminProductFormSection.productSku}}" stepKey="clearProductName"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveButton"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Product" stepKey="seeStillOnEditPage"/> + <see selector="{{AdminProductFormSection.fieldError('name')}}" userInput="This is a required field." stepKey="seeNameRequired"/> + <see selector="{{AdminProductFormSection.fieldError('sku')}}" userInput="This is a required field." stepKey="seeSkuRequired"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/FillDefaultQuantityForLinkedToGroupProductInGridActionGroup.xml b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/FillDefaultQuantityForLinkedToGroupProductInGridActionGroup.xml new file mode 100644 index 0000000000000..faa39eb70cced --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/FillDefaultQuantityForLinkedToGroupProductInGridActionGroup.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="FillDefaultQuantityForLinkedToGroupProductInGridActionGroup"> + <annotations> + <description>Fills the provided Qty for a Product linked to a Grouped Product.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + <argument name="qty" type="string"/> + </arguments> + + <fillField selector="{{AdminAddedProductsToGroupGrid.inputByProductName(productName)}}" userInput="{{qty}}" stepKey="fillDefaultQtyForLinkedProduct"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/FillGroupedProductFormActionGroup.xml b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/FillGroupedProductFormActionGroup.xml new file mode 100644 index 0000000000000..c4df7f67ac201 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/FillGroupedProductFormActionGroup.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="FillGroupedProductFormActionGroup"> + <annotations> + <description>Fills in the provided Product Name and SKU on the Grouped Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="product" defaultValue="GroupedProduct"/> + </arguments> + + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{product.name}}" stepKey="fillProductSku"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{product.sku}}" stepKey="fillProductName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/StorefrontAddGroupedProductWithTwoLinksToCartActionGroup.xml b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/StorefrontAddGroupedProductWithTwoLinksToCartActionGroup.xml new file mode 100644 index 0000000000000..0a44aa6943862 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/StorefrontAddGroupedProductWithTwoLinksToCartActionGroup.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="StorefrontAddGroupedProductWithTwoLinksToCartActionGroup" extends="AddSimpleProductToCartActionGroup"> + <annotations> + <description>Adding to the Shopping Cart single Grouped product, with 2 associated from the Product page</description> + </annotations> + <arguments> + <argument name="linkedProduct1Name" type="string" defaultValue="{{_defaultProduct.name}}"/> + <argument name="linkedProduct2Name" type="string" defaultValue="{{_defaultProduct.name}}"/> + <argument name="linkedProduct1Qty" type="string" defaultValue="1"/> + <argument name="linkedProduct2Qty" type="string" defaultValue="1"/> + </arguments> + <fillField selector="{{StorefrontProductPageSection.qtyInputWithProduct(linkedProduct1Name)}}" userInput="{{linkedProduct1Qty}}" before="addToCart" stepKey="fillQuantityForFirsProduct"/> + <fillField selector="{{StorefrontProductPageSection.qtyInputWithProduct(linkedProduct2Name)}}" userInput="{{linkedProduct2Qty}}" after="fillQuantityForFirsProduct" stepKey="fillQuantityForSecondProduct"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/ViewGroupedProductInAdminGridActionGroup.xml b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/ViewGroupedProductInAdminGridActionGroup.xml new file mode 100644 index 0000000000000..369a0c3f12970 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/ViewGroupedProductInAdminGridActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ViewGroupedProductInAdminGridActionGroup"> + <annotations> + <description>Goes to the Admin Products grid page. Filters the grid for the provided Product. Validates that the provided Product appears in the grid.</description> + </annotations> + <arguments> + <argument name="product" defaultValue="GroupedProduct"/> + </arguments> + + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> + <waitForPageLoad stepKey="waitForPageLoadInitial"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{product.name}}" stepKey="fillProductNameFilter"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillProductSkuFilter"/> + <selectOption selector="{{AdminProductGridFilterSection.typeFilter}}" userInput="{{product.type_id}}" stepKey="selectionProductType"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <see selector="{{AdminProductGridSection.firstProductRow}}" userInput="{{product.name}}" stepKey="seeProductNameInGrid"/> + <click selector="{{AdminProductGridFilterSection.clearFilters}}" stepKey="clickClearFiltersAfter"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultImageGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultImageGroupedProductTest.xml index f2cb2cc993a50..d71ee505f8356 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultImageGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultImageGroupedProductTest.xml @@ -29,7 +29,7 @@ </createData> </before> <after> - <actionGroup ref="deleteProductBySku" stepKey="deleteGroupedProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteGroupedProduct"> <argument name="sku" value="{{GroupedProduct.sku}}"/> </actionGroup> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> @@ -41,11 +41,11 @@ <!-- Create product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPage"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="GroupedProduct"/> </actionGroup> - <actionGroup ref="fillGroupedProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillGroupedProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="GroupedProduct"/> </actionGroup> @@ -54,7 +54,7 @@ <click selector="body" stepKey="clickBodyToCorrectFocusGrouped"/> <click selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="clickAddProductsToGroup"/> <waitForElementVisible selector="{{AdminAddProductsToGroupPanel.filters}}" stepKey="waitForGroupedProductModal"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterGroupedProducts"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterGroupedProducts"> <argument name="sku" value="api-simple-product"/> </actionGroup> <checkOption selector="{{AdminAddProductsToGroupPanel.nThCheckbox('0')}}" stepKey="checkFilterResult1"/> @@ -63,21 +63,21 @@ <click selector="{{AdminAddProductsToGroupPanel.addSelectedProducts}}" stepKey="clickAddSelectedGroupProducts"/> <!-- Add image to product --> - <actionGroup ref="addProductImage" stepKey="addImageForProduct"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImageForProduct"> <argument name="image" value="MagentoLogo"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Assert product image in admin product form --> - <actionGroup ref="assertProductImageAdminProductPage" stepKey="assertProductImageAdminProductPage"/> + <actionGroup ref="AssertProductImageAdminProductPageActionGroup" stepKey="assertProductImageAdminProductPage"/> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="GroupedProduct"/> </actionGroup> <!-- Assert product image in storefront product page --> - <actionGroup ref="assertProductImageStorefrontProductPage" stepKey="assertProductImageStorefrontProductPage"> + <actionGroup ref="AssertProductImageStorefrontProductPageActionGroup" stepKey="assertProductImageStorefrontProductPage"> <argument name="product" value="GroupedProduct"/> <argument name="image" value="MagentoLogo"/> </actionGroup> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultVideoGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultVideoGroupedProductTest.xml index c3a95bbef3aa3..f9af2284ae71a 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultVideoGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAddDefaultVideoGroupedProductTest.xml @@ -29,10 +29,10 @@ <!-- Create a grouped product --> <!-- Replacing steps in base AdminAddDefaultVideoSimpleProductTest --> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProductPage"> <argument name="product" value="GroupedProduct"/> </actionGroup> - <actionGroup ref="fillGroupedProductForm" stepKey="fillMainProductForm"> + <actionGroup ref="FillGroupedProductFormActionGroup" stepKey="fillMainProductForm"> <argument name="product" value="GroupedProduct"/> </actionGroup> @@ -41,18 +41,18 @@ <conditionalClick selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" dependentSelector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" visible="false" stepKey="openGroupedProductSection" after="scrollToSection"/> <click selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="clickAddProductsToGroup" after="openGroupedProductSection"/> <waitForElementVisible selector="{{AdminAddProductsToGroupPanel.filters}}" stepKey="waitForFilter" after="clickAddProductsToGroup"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku1" after="waitForFilter"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku1" after="waitForFilter"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToGroupPanel.firstCheckbox}}" stepKey="checkOption1" after="filterProductGridBySku1"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku2" after="checkOption1"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku2" after="checkOption1"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToGroupPanel.firstCheckbox}}" stepKey="checkOption2" after="filterProductGridBySku2"/> <click selector="{{AdminAddProductsToGroupPanel.addSelectedProducts}}" stepKey="addSelectedProducts" before="saveProductForm"/> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="GroupedProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAssociateGroupedProductToWebsitesTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAssociateGroupedProductToWebsitesTest.xml index 3827666252478..41096c416d05e 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAssociateGroupedProductToWebsitesTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminAssociateGroupedProductToWebsitesTest.xml @@ -69,14 +69,14 @@ <argument name="websiteName" value="{{secondCustomWebsite.name}}"/> </actionGroup> - <actionGroup ref="NavigateToAndResetProductGridToDefaultView" stepKey="resetProductGridFilter"/> + <actionGroup ref="NavigateToAndResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridFilter"/> <!-- Admin logout --> <actionGroup ref="logout" stepKey="logout"/> </after> <!-- Open product page and assign grouped project to second website --> - <actionGroup ref="filterAndSelectProduct" stepKey="openAdminProductPage"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="openAdminProductPage"> <argument name="productSku" value="$$createGroupedProduct.sku$$"/> </actionGroup> <actionGroup ref="AdminAssignProductInWebsiteActionGroup" stepKey="assignProductToSecondWebsite"> @@ -85,15 +85,15 @@ <actionGroup ref="AdminUnassignProductInWebsiteActionGroup" stepKey="unassignProductFromDefaultWebsite"> <argument name="website" value="{{_defaultWebsite.name}}"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveGroupedProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveGroupedProduct"/> <!-- Assert product is assigned to Second website --> - <actionGroup ref="AssertProductIsAssignedToWebsite" stepKey="seeCustomWebsiteIsChecked"> + <actionGroup ref="AssertProductIsAssignedToWebsiteActionGroup" stepKey="seeCustomWebsiteIsChecked"> <argument name="website" value="{{secondCustomWebsite.name}}"/> </actionGroup> <!-- Assert product is not assigned to Main website --> - <actionGroup ref="AssertProductIsNotAssignedToWebsite" stepKey="seeMainWebsiteIsNotChecked"> + <actionGroup ref="AssertProductIsNotAssignedToWebsiteActionGroup" stepKey="seeMainWebsiteIsNotChecked"> <argument name="website" value="{{_defaultWebsite.name}}"/> </actionGroup> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminCreateAndEditGroupedProductSettingsTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminCreateAndEditGroupedProductSettingsTest.xml index 62a685554f735..e3b76dafdd100 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminCreateAndEditGroupedProductSettingsTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminCreateAndEditGroupedProductSettingsTest.xml @@ -30,7 +30,7 @@ </before> <after> <!-- Delete grouped product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="GroupedProduct"/> </actionGroup> @@ -47,17 +47,17 @@ </after> <!-- Create new grouped product --> - <actionGroup ref="GoToSpecifiedCreateProductPage" stepKey="createGroupedProduct"> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createGroupedProduct"> <argument name="productType" value="grouped"/> </actionGroup> <!-- Fill all main fields --> - <actionGroup ref="fillGroupedProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillGroupedProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="GroupedProduct"/> </actionGroup> <!-- Add simple product to the Group --> - <actionGroup ref="AdminAssignProductToGroup" stepKey="addFirstSimpleToGroup"> + <actionGroup ref="AdminAssignProductToGroupActionGroup" stepKey="addFirstSimpleToGroup"> <argument name="product" value="$$createProduct$$"/> </actionGroup> @@ -70,7 +70,7 @@ <actionGroup ref="AdminSetProductDesignSettingsActionGroup" stepKey="setProductDesignSettings"/> <!-- Save grouped product form --> - <actionGroup ref="saveProductForm" stepKey="clickSaveButton"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveButton"/> <!-- Open created simple product --> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> @@ -86,7 +86,7 @@ </actionGroup> <!-- Save simple product form --> - <actionGroup ref="saveProductForm" stepKey="clickSaveBtn"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveBtn"/> <!-- Open grouped product page --> <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openStorefrontProductPage"> @@ -100,7 +100,7 @@ <fillField selector="{{StorefrontProductPageSection.qtyInput}}" userInput="{{ApiSimpleSingleQty.quantity}}" stepKey="fillFieldQtyInput"/> <!-- Assert Gift Option product settings is present --> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProductPage6"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage6"> <argument name="productName" value="GroupedProduct.name"/> </actionGroup> <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openShoppingCart"/> @@ -120,7 +120,7 @@ </actionGroup> <!-- Assert product is assigned to websites --> - <actionGroup ref="AssertProductIsAssignedToWebsite" stepKey="seeCustomWebsiteIsChecked"> + <actionGroup ref="AssertProductIsAssignedToWebsiteActionGroup" stepKey="seeCustomWebsiteIsChecked"> <argument name="website" value="$createWebsite.website[name]$"/> </actionGroup> @@ -135,7 +135,7 @@ </actionGroup> <!-- Save grouped product form --> - <actionGroup ref="saveProductForm" stepKey="clickSaveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveProduct"/> <!-- Open created simple product --> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForProduct"> @@ -149,7 +149,7 @@ <actionGroup ref="AdminSwitchProductGiftMessageStatusActionGroup" stepKey="disableGiftMessageSettings"/> <!-- Save product form --> - <actionGroup ref="saveProductForm" stepKey="clickSaveSimpleProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveSimpleProduct"/> <!-- Verify Url Key after changing --> <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openProductPage"> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminDeleteGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminDeleteGroupedProductTest.xml index 6768dd5a1a249..f7b9357f1b34a 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminDeleteGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminDeleteGroupedProductTest.xml @@ -33,7 +33,7 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteGroupedProductFilteredBySkuAndName"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteGroupedProductFilteredBySkuAndName"> <argument name="product" value="$$createGroupedProduct$$"/> </actionGroup> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductSetEditContentTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductSetEditContentTest.xml index 3938d91c515f1..d9f9d8ecd9382 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductSetEditContentTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductSetEditContentTest.xml @@ -21,16 +21,16 @@ </annotations> <after> <!-- Delete grouped product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="GroupedProduct"/> </actionGroup> </after> <!-- Create product --> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="GroupedProduct"/> </actionGroup> - <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillProductNameAndSkuInProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="GroupedProduct"/> </actionGroup> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductsListTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductsListTest.xml index b59cf1e2175d8..d80e68e37c44c 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductsListTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductsListTest.xml @@ -39,10 +39,10 @@ <!-- Create product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="adminProductIndexPageAdd"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProductPage"> <argument name="product" value="GroupedProduct"/> </actionGroup> - <actionGroup ref="fillGroupedProductForm" stepKey="fillMainProductForm"> + <actionGroup ref="FillGroupedProductFormActionGroup" stepKey="fillMainProductForm"> <argument name="product" value="GroupedProduct"/> </actionGroup> <!-- Add two simple products to grouped product --> @@ -50,11 +50,11 @@ <conditionalClick selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" dependentSelector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" visible="false" stepKey="openGroupedProductSection"/> <click selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="clickAddProductsToGroup"/> <waitForElementVisible selector="{{AdminAddProductsToGroupPanel.filters}}" stepKey="waitForFilter"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku1"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku1"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToGroupPanel.firstCheckbox}}" stepKey="checkOption1"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku2"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToGroupPanel.firstCheckbox}}" stepKey="checkOption2"/> @@ -62,7 +62,7 @@ <waitForPageLoad stepKey="waitForPageLoad1"/> <!-- Save product --> - <actionGroup ref="saveProductForm" stepKey="saveProductForm"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> <waitForPageLoad stepKey="waitForPageLoad2"/> <scrollTo selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" x="0" y="-100" stepKey="scrollToProducts"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedSetEditRelatedProductsTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedSetEditRelatedProductsTest.xml index 8117d627a370c..fdffd286d632f 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedSetEditRelatedProductsTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedSetEditRelatedProductsTest.xml @@ -21,16 +21,16 @@ <before></before> <after> <!-- Delete grouped product --> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="GroupedProduct"/> </actionGroup> </after> <!-- Create product --> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="GroupedProduct"/> </actionGroup> - <actionGroup ref="fillGroupedProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillGroupedProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="GroupedProduct"/> </actionGroup> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml index da7cfaeb71566..452b55b835739 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultImageGroupedProductTest.xml @@ -38,11 +38,11 @@ <!-- Create product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPage"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="GroupedProduct"/> </actionGroup> - <actionGroup ref="fillGroupedProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillGroupedProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="GroupedProduct"/> </actionGroup> @@ -51,7 +51,7 @@ <click selector="body" stepKey="clickBodyToCorrectFocusGrouped"/> <click selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="clickAddProductsToGroup"/> <waitForElementVisible selector="{{AdminAddProductsToGroupPanel.filters}}" stepKey="waitForGroupedProductModal"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterGroupedProducts"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterGroupedProducts"> <argument name="sku" value="api-simple-product"/> </actionGroup> <checkOption selector="{{AdminAddProductsToGroupPanel.nThCheckbox('0')}}" stepKey="checkFilterResult1"/> @@ -60,26 +60,26 @@ <click selector="{{AdminAddProductsToGroupPanel.addSelectedProducts}}" stepKey="clickAddSelectedGroupProducts"/> <!-- Add image to product --> - <actionGroup ref="addProductImage" stepKey="addImageForProduct"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImageForProduct"> <argument name="image" value="MagentoLogo"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Remove image from product --> - <actionGroup ref="removeProductImage" stepKey="removeProductImage"/> + <actionGroup ref="RemoveProductImageActionGroup" stepKey="removeProductImage"/> - <actionGroup ref="saveProductForm" stepKey="saveProductFormAfterRemove"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductFormAfterRemove"/> <!-- Assert product image not in admin product form --> - <actionGroup ref="assertProductImageNotInAdminProductPage" stepKey="assertProductImageNotInAdminProductPage"/> + <actionGroup ref="AssertProductImageNotInAdminProductPageActionGroup" stepKey="assertProductImageNotInAdminProductPage"/> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPageAfterRemove"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPageAfterRemove"> <argument name="product" value="GroupedProduct"/> </actionGroup> <!-- Assert product image not in storefront product page --> - <actionGroup ref="assertProductImageNotInStorefrontProductPage" stepKey="assertProductImageNotInStorefrontProductPage"> + <actionGroup ref="AssertProductImageNotInStorefrontProductPageActionGroup" stepKey="assertProductImageNotInStorefrontProductPage"> <argument name="product" value="GroupedProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultVideoGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultVideoGroupedProductTest.xml index e322d4a1eb038..572ae3a44a953 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultVideoGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminRemoveDefaultVideoGroupedProductTest.xml @@ -29,10 +29,10 @@ <!-- Create a grouped product --> <!-- Replacing steps in base AdminRemoveDefaultVideoSimpleProductTest --> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProductPage"> <argument name="product" value="GroupedProduct"/> </actionGroup> - <actionGroup ref="fillGroupedProductForm" stepKey="fillMainProductForm"> + <actionGroup ref="FillGroupedProductFormActionGroup" stepKey="fillMainProductForm"> <argument name="product" value="GroupedProduct"/> </actionGroup> @@ -41,18 +41,18 @@ <conditionalClick selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" dependentSelector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" visible="false" stepKey="openGroupedProductSection" after="scrollToSection"/> <click selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="clickAddProductsToGroup" after="openGroupedProductSection"/> <waitForElementVisible selector="{{AdminAddProductsToGroupPanel.filters}}" stepKey="waitForFilter" after="clickAddProductsToGroup"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku1" after="waitForFilter"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku1" after="waitForFilter"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToGroupPanel.firstCheckbox}}" stepKey="checkOption1" after="filterProductGridBySku1"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku2" after="checkOption1"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku2" after="checkOption1"> <argument name="product" value="$$simpleProduct2$$"/> </actionGroup> <checkOption selector="{{AdminAddProductsToGroupPanel.firstCheckbox}}" stepKey="checkOption2" after="filterProductGridBySku2"/> <click selector="{{AdminAddProductsToGroupPanel.addSelectedProducts}}" stepKey="addSelectedProducts" before="saveProductForm"/> <!-- Assert product in storefront product page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="GroupedProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminSortingAssociatedProductsTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminSortingAssociatedProductsTest.xml index ad5fbbb30edeb..bac294d59ce51 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminSortingAssociatedProductsTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminSortingAssociatedProductsTest.xml @@ -94,7 +94,7 @@ </before> <after> <!--Delete created grouped product--> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="GroupedProduct"/> </actionGroup> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" @@ -129,11 +129,11 @@ <!--Create grouped Product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPage"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGridColumnsInitial"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="GroupedProduct"/> </actionGroup> - <actionGroup ref="fillGroupedProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillGroupedProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="GroupedProduct"/> </actionGroup> @@ -142,7 +142,7 @@ <click selector="body" stepKey="clickBodyToCorrectFocusGrouped"/> <click selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="clickAddProductsToGroup"/> <waitForElementVisible selector="{{AdminAddProductsToGroupPanel.filters}}" stepKey="waitForGroupedProductModal"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterGroupedProducts"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterGroupedProducts"> <argument name="sku" value="api-simple-product"/> </actionGroup> @@ -153,13 +153,13 @@ <click selector="{{AdminAddProductsToGroupPanel.addSelectedProducts}}" stepKey="clickAddSelectedGroupProducts"/> <waitForPageLoad stepKey="waitForProductsAdded"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!--Open created Product group--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetFiltersIfExist"/> - <actionGroup ref="searchProductGridByKeyword" stepKey="searchProductGridForm"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetFiltersIfExist"/> + <actionGroup ref="SearchProductGridByKeywordActionGroup" stepKey="searchProductGridForm"> <argument name="keyword" value="GroupedProduct.name"/> </actionGroup> <click selector="{{AdminProductGridSection.selectRowBasedOnName(GroupedProduct.name)}}" stepKey="openGroupedProduct"/> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/EndToEndB2CAdminTest.xml index dbe3dddfca81b..e73c8aa730ecb 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -11,11 +11,11 @@ <!--Create Grouped Product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPageGrouped" after="seeSimpleProductInGrid"/> <waitForPageLoad stepKey="waitForProductPageLoadGrouped" after="visitAdminProductPageGrouped"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateGroupedProduct" after="waitForProductPageLoadGrouped"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateGroupedProduct" after="waitForProductPageLoadGrouped"> <argument name="product" value="GroupedProduct"/> </actionGroup> - <actionGroup ref="checkRequiredFieldsInGroupedProductForm" stepKey="checkRequiredFieldsProductGrouped" after="goToCreateGroupedProduct"/> - <actionGroup ref="fillGroupedProductForm" stepKey="fillGroupedProductForm" after="checkRequiredFieldsProductGrouped"> + <actionGroup ref="CheckRequiredFieldsInGroupedProductFormActionGroup" stepKey="checkRequiredFieldsProductGrouped" after="goToCreateGroupedProduct"/> + <actionGroup ref="FillGroupedProductFormActionGroup" stepKey="fillGroupedProductForm" after="checkRequiredFieldsProductGrouped"> <argument name="product" value="GroupedProduct"/> </actionGroup> <scrollTo selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" x="0" y="-100" stepKey="scrollToGroupedSection" after="fillGroupedProductForm"/> @@ -23,20 +23,20 @@ <click selector="body" stepKey="clickBodyToCorrectFocusGrouped" after="openGroupedProductsSection"/> <click selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="clickAddProductsToGroup" after="clickBodyToCorrectFocusGrouped"/> <waitForElementVisible selector="{{AdminAddProductsToGroupPanel.filters}}" stepKey="waitForGroupedProductModal" after="clickAddProductsToGroup"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterGroupedProductOptions" after="waitForGroupedProductModal"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterGroupedProductOptions" after="waitForGroupedProductModal"> <argument name="product" value="SimpleProduct"/> </actionGroup> <checkOption selector="{{AdminAddProductsToGroupPanel.firstCheckbox}}" stepKey="checkFilterResult" after="filterGroupedProductOptions"/> <click selector="{{AdminAddProductsToGroupPanel.addSelectedProducts}}" stepKey="clickAddSelectedGroupProducts" after="checkFilterResult"/> - <actionGroup ref="saveProductForm" stepKey="saveGroupedProduct" after="clickAddSelectedGroupProducts"/> - <actionGroup ref="viewGroupedProductInAdminGrid" stepKey="viewGroupedProductInGrid" after="saveGroupedProduct"> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveGroupedProduct" after="clickAddSelectedGroupProducts"/> + <actionGroup ref="ViewGroupedProductInAdminGridActionGroup" stepKey="viewGroupedProductInGrid" after="saveGroupedProduct"> <argument name="product" value="GroupedProduct"/> </actionGroup> <!--@TODO Move cleanup to "after" when MQE-830 is resolved--> <comment userInput="Clean up grouped product" stepKey="cleanUpGroupedProduct" after="deleteSimpleProduct"/> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteGroupedProduct" after="cleanUpGroupedProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteGroupedProduct" after="cleanUpGroupedProduct"> <argument name="product" value="GroupedProduct"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/NewProductsListWidgetGroupedProductTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/NewProductsListWidgetGroupedProductTest.xml index a56512f84a9b8..d563796b21da9 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/NewProductsListWidgetGroupedProductTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/NewProductsListWidgetGroupedProductTest.xml @@ -52,7 +52,7 @@ <scrollTo selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="scrollToAddProductsToGroup"/> <click selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="clickAddProductsToGroup"/> <waitForElementVisible selector="{{AdminAddProductsToGroupPanel.filters}}" stepKey="waitForGroupedProductModal"/> - <actionGroup ref="filterProductGridBySku2" stepKey="filterGroupedProducts"> + <actionGroup ref="FilterProductGridBySku2ActionGroup" stepKey="filterGroupedProducts"> <argument name="sku" value="api-simple-product"/> </actionGroup> <checkOption selector="{{AdminAddProductsToGroupPanel.nThCheckbox('0')}}" stepKey="checkFilterResult1"/> diff --git a/app/code/Magento/GroupedProduct/registration.php b/app/code/Magento/GroupedProduct/registration.php index 8fd2f11a2f628..c3a7c4c12b1c6 100644 --- a/app/code/Magento/GroupedProduct/registration.php +++ b/app/code/Magento/GroupedProduct/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_GroupedProduct', __DIR__); diff --git a/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php b/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php index af5377a6227ca..55992c92226af 100644 --- a/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php +++ b/app/code/Magento/ImportExport/Block/Adminhtml/Import/Edit/Form.php @@ -242,9 +242,13 @@ protected function _prepareForm() 'class' => 'input-text', 'note' => __( $this->escapeHtml( - 'For Type "Local Server" use relative path to <Magento installation>/' + 'For Type "Local Server" use relative path to <Magento root directory>/' .$this->imagesDirectoryProvider->getDirectoryRelativePath() - .', e.g. product_images, import_images/batch1' + .', e.g. <i>product_images</i>, <i>import_images/batch1</i>.<br><br>' + .'For example, in case <i>product_images</i>, files should be placed into ' + .'<i><Magento root directory>/' + .$this->imagesDirectoryProvider->getDirectoryRelativePath() . '/product_images</i> folder.', + ['i', 'br'] ) ), ] diff --git a/app/code/Magento/ImportExport/Controller/Adminhtml/Export/Export.php b/app/code/Magento/ImportExport/Controller/Adminhtml/Export/Export.php index c5885f72474f9..dff6560ebf768 100644 --- a/app/code/Magento/ImportExport/Controller/Adminhtml/Export/Export.php +++ b/app/code/Magento/ImportExport/Controller/Adminhtml/Export/Export.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\ImportExport\Controller\Adminhtml\Export; use Magento\Backend\App\Action\Context; @@ -90,7 +92,10 @@ public function execute() $this->messagePublisher->publish('import_export.export', $dataObject); $this->messageManager->addSuccessMessage( - __('Message is added to queue, wait to get your file soon') + __( + 'Message is added to queue, wait to get your file soon.' + . ' Make sure your cron job is running to export the file' + ) ); } catch (\Exception $e) { $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); diff --git a/app/code/Magento/ImportExport/Files/Sample/catalog_product.csv b/app/code/Magento/ImportExport/Files/Sample/catalog_product.csv index 7ffd5b1dfb57c..c5d8df36b441c 100644 --- a/app/code/Magento/ImportExport/Files/Sample/catalog_product.csv +++ b/app/code/Magento/ImportExport/Files/Sample/catalog_product.csv @@ -1,7 +1,7 @@ -sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,deferred_stock_update,use_config_deferred_stock_update,related_skus,crosssell_skus,upsell_skus,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,associated_skus -24-WG085,,Default,simple,"Default Category/Gear,Default Category/Gear/Fitness Equipment",base,Sprite Yoga Strap 6 foot,"<p>The Sprite Yoga Strap is your untiring partner in demanding stretches, holds and alignment routines. The strap's 100% organic cotton fabric is woven tightly to form a soft, textured yet non-slip surface. The plastic clasp buckle is easily adjustable, lightweight and urable under strain.</p><ul><li>100% soft and durable cotton.<li>Plastic cinch buckle is easy to use.<li>Three natural colors made from phthalate and heavy metal free dyes.</ul>",,1,1,Taxable Goods,"Catalog, Search",14,,,,sprite-yoga-strap-6-foot,Meta Title,"meta1, meta2, meta3",meta description,"2015-10-25 03:34:20","2015-10-25 03:34:20",,,Block after Info Column,,,,,,,,,,,Use config,,"has_options=1,quantity_and_stock_status=In Stock,required_options=0",100,0,1,0,0,1,1,0,0,1,1,,1,0,1,1,0,1,0,0,1,0,1,"24-WG087,24-WG086","24-WG087,24-WG086","24-WG087,24-WG086",,"name=Custom Yoga Option,type=drop_down,required=0,price=10.0000,price_type=fixed,sku=,option_title=Gold|name=Custom Yoga Option,type=drop_down,required=0,price=10.0000,price_type=fixed,sku=,option_title=Silver|name=Custom Yoga Option,type=drop_down,required=0,price=10.0000,price_type=fixed,sku=yoga3sku,option_title=Platinum",,,,,, -24-WG086,,Default,simple,"Default Category/Gear,Default Category/Gear/Fitness Equipment",base,Sprite Yoga Strap 8 foot,"<p>The Sprite Yoga Strap is your untiring partner in demanding stretches, holds and alignment routines. The strap's 100% organic cotton fabric is woven tightly to form a soft, textured yet non-slip surface. The plastic clasp buckle is easily adjustable, lightweight and durable under strain.</p><ul><li>8' long x 1.0"" wide.<li>100% soft and durable cotton.<li>Plastic cinch buckle is easy to use.<li>Three natural colors made from phthalate and heavy metal free dyes.</ul>",,1,1,Taxable Goods,"Catalog, Search",17,,,,sprite-yoga-strap-8-foot,Meta Title,"meta1, meta2, meta4",meta description,"2015-10-25 03:34:20","2015-10-25 03:34:20",,,Block after Info Column,,,,,,,,,,,Use config,,"has_options=0,quantity_and_stock_status=In Stock,required_options=0",100,0,1,0,0,1,1,0,0,1,1,,1,0,1,1,0,1,0,0,1,0,1,24-WG087,24-WG087,24-WG087,,,,,,,, -24-WG087,,Default,simple,"Default Category/Gear,Default Category/Gear/Fitness Equipment",base,Sprite Yoga Strap 10 foot,"<p>The Sprite Yoga Strap is your untiring partner in demanding stretches, holds and alignment routines. The strap's 100% organic cotton fabric is woven tightly to form a soft, textured yet non-slip surface. The plastic clasp buckle is easily adjustable, lightweight and durable under strain.</p><ul><li>10' long x 1.0"" wide.<li>100% soft and durable cotton.<li>Plastic cinch buckle is easy to use.<li>Three natural colors made from phthalate and heavy metal free dyes.</ul>",,1,1,Taxable Goods,"Catalog, Search",21,,,,sprite-yoga-strap-10-foot,Meta Title,"meta1, meta2, meta5",meta description,"2015-10-25 03:34:20","2015-10-25 03:34:20",,,Block after Info Column,,,,,,,,,,,Use config,,"has_options=0,quantity_and_stock_status=In Stock,required_options=0",100,0,1,0,0,1,1,0,0,1,1,,1,0,1,1,0,1,0,0,1,0,1,24-WG086,24-WG086,24-WG086,,,,,,,, -24-WG085_Group,,Default,grouped,"Default Category/Gear,Default Category/Gear/Fitness Equipment",base,Set of Sprite Yoga Straps,"<p>Great set of Sprite Yoga Straps for every stretch and hold you need. There are three straps in this set: 6', 8' and 10'.</p><ul><li>100% soft and durable cotton.</li><li>Plastic cinch buckle is easy to use.</li><li>Choice of three natural colors made from phthalate and heavy metal free dyes.</li></ul>",,,1,,"Catalog, Search",,,,,set-of-sprite-yoga-straps,Meta Title,"meta1, meta2, meta6",meta description,"2015-10-25 03:34:20","2015-10-25 03:34:20",,,Block after Info Column,,,,,,,,,,,,,"has_options=0,quantity_and_stock_status=In Stock,required_options=0",0,0,1,0,0,1,1,0,0,1,1,,1,1,1,1,0,1,0,0,1,0,1,"24-WG087,24-WG086","24-WG087,24-WG086","24-WG087,24-WG086",,,,,,,,"24-WG085=5.0000,24-WG086=5.0000" -24-WG085-bundle-dynamic,,Default,bundle,"Default Category/Gear,Default Category/Gear/Fitness Equipment",base,Sprite Yoga Strap Dynamic Bundle,"<p>The Sprite Yoga Strap is your untiring partner in demanding stretches, holds and alignment routines. The strap's 100% organic cotton fabric is woven tightly to form a soft, textured yet non-slip surface. The plastic clasp buckle is easily adjustable, lightweight and durable under strain.</p><ul><li>100% soft and durable cotton.<li>Plastic cinch buckle is easy to use.<li>Three natural colors made from phthalate and heavy metal free dyes.</ul>",,1,1,Taxable Goods,"Catalog, Search",14,,,,sprite-yoga-strap2,Meta Title,"meta1, meta2, meta8",meta description,"2015-10-25 03:34:20","2015-10-25 03:34:20",,,Block after Info Column,,,,,,,,,,,Use config,,"has_options=1,shipment_type=together,quantity_and_stock_status=In Stock,required_options=0",0,0,1,0,0,1,1,0,0,1,1,,1,0,1,1,0,1,0,0,1,0,1,"24-WG087,24-WG086","24-WG087,24-WG086","24-WG087,24-WG086",,,dynamic,dynamic,Price range,fixed,"name=Bundle Option One1,type=select,required=1,sku=24-WG085,price=15.0000,default=0,default_qty=1.0000,price_type=fixed|name=Bundle Option One1,type=select,required=1,sku=24-WG086,price=10.0000,default=1,default_qty=1.0000,price_type=fixed", -24-WG085-bundle-fixed,,Default,bundle,"Default Category/Gear,Default Category/Gear/Fitness Equipment",base,Sprite Yoga Strap Fixed Bundle,"<p>The Sprite Yoga Strap is your untiring partner in demanding stretches, holds and alignment routines. The strap's 100% organic cotton fabric is woven tightly to form a soft, textured yet non-slip surface. The plastic clasp buckle is easily adjustable, lightweight and durable under strain.</p><ul><li>100% soft and durable cotton.<li>Plastic cinch buckle is easy to use.<li>Three natural colors made from phthalate and heavy metal free dyes.</ul>",,1,1,Taxable Goods,"Catalog, Search",14,,,,sprite-yoga-strap3,Meta Title,"meta1, meta2, meta9",meta description,"2015-10-25 03:34:20","2015-10-25 03:34:20",,,Block after Info Column,,,,,,,,,,,Use config,,"has_options=1,shipment_type=together,quantity_and_stock_status=In Stock,required_options=0",0,0,1,0,0,1,1,0,0,1,1,,1,0,1,1,0,1,0,0,1,0,1,"24-WG087,24-WG086","24-WG087,24-WG086","24-WG087,24-WG086",,,fixed,fixed,Price range,fixed,"name=Yoga Strap,type=radio,required=1,sku=24-WG086,price=0.0000,default=1,default_qty=3.0000,price_type=percent|name=Yoga Strap,type=radio,required=1,sku=24-WG085,price=0.0000,default=0,default_qty=3.0000,price_type=percent", \ No newline at end of file +sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,deferred_stock_update,use_config_deferred_stock_update,related_skus,crosssell_skus,upsell_skus,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,associated_skus +24-WG085,,Default,simple,"Default Category/Gear,Default Category/Gear/Fitness Equipment",base,Sprite Yoga Strap 6 foot,"<p>The Sprite Yoga Strap is your untiring partner in demanding stretches, holds and alignment routines. The strap's 100% organic cotton fabric is woven tightly to form a soft, textured yet non-slip surface. The plastic clasp buckle is easily adjustable, lightweight and urable under strain.</p><ul><li>100% soft and durable cotton.<li>Plastic cinch buckle is easy to use.<li>Three natural colors made from phthalate and heavy metal free dyes.</ul>",,1.23,1,Taxable Goods,"Catalog, Search",14,,,,sprite-yoga-strap-6-foot,Meta Title,"meta1, meta2, meta3",meta description,2015-10-25 3:34:20,2015-10-25 3:34:20,,,Block after Info Column,,,,,,,,,,,Use config,,"has_options=1,quantity_and_stock_status=In Stock,required_options=0",100,0,1,0,0,1,1,0,0,1,1,,1,0,1,1,0,1,0,0,1,0,1,"24-WG087,24-WG086","24-WG087,24-WG086","24-WG087,24-WG086",,"name=Custom Yoga Option,type=drop_down,required=0,price=10.0000,price_type=fixed,sku=,option_title=Gold|name=Custom Yoga Option,type=drop_down,required=0,price=10.0000,price_type=fixed,sku=,option_title=Silver|name=Custom Yoga Option,type=drop_down,required=0,price=10.0000,price_type=fixed,sku=yoga3sku,option_title=Platinum",,,,,, +24-WG086,,Default,simple,"Default Category/Gear,Default Category/Gear/Fitness Equipment",base,Sprite Yoga Strap 8 foot,"<p>The Sprite Yoga Strap is your untiring partner in demanding stretches, holds and alignment routines. The strap's 100% organic cotton fabric is woven tightly to form a soft, textured yet non-slip surface. The plastic clasp buckle is easily adjustable, lightweight and durable under strain.</p><ul><li>8' long x 1.0"" wide.<li>100% soft and durable cotton.<li>Plastic cinch buckle is easy to use.<li>Three natural colors made from phthalate and heavy metal free dyes.</ul>",,1,1,Taxable Goods,"Catalog, Search",17,,,,sprite-yoga-strap-8-foot,Meta Title,"meta1, meta2, meta4",meta description,2015-10-25 3:34:20,2015-10-25 3:34:20,,,Block after Info Column,,,,,,,,,,,Use config,,"has_options=0,quantity_and_stock_status=In Stock,required_options=0",100,0,1,0,0,1,1,0,0,1,1,,1,0,1,1,0,1,0,0,1,0,1,24-WG087,24-WG087,24-WG087,,,,,,,, +24-WG087,,Default,simple,"Default Category/Gear,Default Category/Gear/Fitness Equipment",base,Sprite Yoga Strap 10 foot,"<p>The Sprite Yoga Strap is your untiring partner in demanding stretches, holds and alignment routines. The strap's 100% organic cotton fabric is woven tightly to form a soft, textured yet non-slip surface. The plastic clasp buckle is easily adjustable, lightweight and durable under strain.</p><ul><li>10' long x 1.0"" wide.<li>100% soft and durable cotton.<li>Plastic cinch buckle is easy to use.<li>Three natural colors made from phthalate and heavy metal free dyes.</ul>",,1,1,Taxable Goods,"Catalog, Search",21,,,,sprite-yoga-strap-10-foot,Meta Title,"meta1, meta2, meta5",meta description,2015-10-25 3:34:20,2015-10-25 3:34:20,,,Block after Info Column,,,,,,,,,,,Use config,,"has_options=0,quantity_and_stock_status=In Stock,required_options=0",100,0,1,0,0,1,1,0,0,1,1,,1,0,1,1,0,1,0,0,1,0,1,24-WG086,24-WG086,24-WG086,,,,,,,, +24-WG085_Group,,Default,grouped,"Default Category/Gear,Default Category/Gear/Fitness Equipment",base,Set of Sprite Yoga Straps,"<p>Great set of Sprite Yoga Straps for every stretch and hold you need. There are three straps in this set: 6', 8' and 10'.</p><ul><li>100% soft and durable cotton.</li><li>Plastic cinch buckle is easy to use.</li><li>Choice of three natural colors made from phthalate and heavy metal free dyes.</li></ul>",,,1,,"Catalog, Search",,,,,set-of-sprite-yoga-straps,Meta Title,"meta1, meta2, meta6",meta description,2015-10-25 3:34:20,2015-10-25 3:34:20,,,Block after Info Column,,,,,,,,,,,,,"has_options=0,quantity_and_stock_status=In Stock,required_options=0",0,0,1,0,0,1,1,0,0,1,1,,1,1,1,1,0,1,0,0,1,0,1,"24-WG087,24-WG086","24-WG087,24-WG086","24-WG087,24-WG086",,,,,,,,"24-WG085=5.0000,24-WG086=5.0000" +24-WG085-bundle-dynamic,,Default,bundle,"Default Category/Gear,Default Category/Gear/Fitness Equipment",base,Sprite Yoga Strap Dynamic Bundle,"<p>The Sprite Yoga Strap is your untiring partner in demanding stretches, holds and alignment routines. The strap's 100% organic cotton fabric is woven tightly to form a soft, textured yet non-slip surface. The plastic clasp buckle is easily adjustable, lightweight and durable under strain.</p><ul><li>100% soft and durable cotton.<li>Plastic cinch buckle is easy to use.<li>Three natural colors made from phthalate and heavy metal free dyes.</ul>",,1.12,1,Taxable Goods,"Catalog, Search",14,,,,sprite-yoga-strap2,Meta Title,"meta1, meta2, meta8",meta description,2015-10-25 3:34:20,2015-10-25 3:34:20,,,Block after Info Column,,,,,,,,,,,Use config,,"has_options=1,shipment_type=together,quantity_and_stock_status=In Stock,required_options=0",0,0,1,0,0,1,1,0,0,1,1,,1,0,1,1,0,1,0,0,1,0,1,"24-WG087,24-WG086","24-WG087,24-WG086","24-WG087,24-WG086",,,dynamic,dynamic,Price range,fixed,"name=Bundle Option One1,type=select,required=1,sku=24-WG085,price=15.0000,default=0,default_qty=1.0000,price_type=fixed|name=Bundle Option One1,type=select,required=1,sku=24-WG086,price=10.0000,default=1,default_qty=1.0000,price_type=fixed", +24-WG085-bundle-fixed,,Default,bundle,"Default Category/Gear,Default Category/Gear/Fitness Equipment",base,Sprite Yoga Strap Fixed Bundle,"<p>The Sprite Yoga Strap is your untiring partner in demanding stretches, holds and alignment routines. The strap's 100% organic cotton fabric is woven tightly to form a soft, textured yet non-slip surface. The plastic clasp buckle is easily adjustable, lightweight and durable under strain.</p><ul><li>100% soft and durable cotton.<li>Plastic cinch buckle is easy to use.<li>Three natural colors made from phthalate and heavy metal free dyes.</ul>",,1,1,Taxable Goods,"Catalog, Search",14,,,,sprite-yoga-strap3,Meta Title,"meta1, meta2, meta9",meta description,2015-10-25 3:34:20,2015-10-25 3:34:20,,,Block after Info Column,,,,,,,,,,,Use config,,"has_options=1,shipment_type=together,quantity_and_stock_status=In Stock,required_options=0",0,0,1,0,0,1,1,0,0,1,1,,1,0,1,1,0,1,0,0,1,0,1,"24-WG087,24-WG086","24-WG087,24-WG086","24-WG087,24-WG086",,,fixed,fixed,Price range,fixed,"name=Yoga Strap,type=radio,required=1,sku=24-WG086,price=0.0000,default=1,default_qty=3.0000,price_type=percent|name=Yoga Strap,type=radio,required=1,sku=24-WG085,price=0.0000,default=0,default_qty=3.0000,price_type=percent", \ No newline at end of file diff --git a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminCheckDataForImportProductActionGroup.xml b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminCheckDataForImportProductActionGroup.xml new file mode 100644 index 0000000000000..8628d246248b1 --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminCheckDataForImportProductActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCheckDataForImportProductActionGroup"> + <arguments> + <argument name="behavior" type="string" defaultValue="Add/Update"/> + <argument name="importFile" type="string"/> + </arguments> + <amOnPage url="{{AdminImportIndexPage.url}}" stepKey="goToImportIndexPage"/> + <waitForPageLoad stepKey="adminImportMainSectionLoad"/> + <selectOption selector="{{AdminImportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> + <waitForElementVisible selector="{{AdminImportMainSection.importBehavior}}" stepKey="waitForImportBehaviorElementVisible"/> + <selectOption selector="{{AdminImportMainSection.importBehavior}}" userInput="{{behavior}}" stepKey="selectImportBehaviorOption"/> + <attachFile selector="{{AdminImportMainSection.selectFileToImport}}" userInput="{{importFile}}" stepKey="attachFileForImport"/> + <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml index 9063916e9f502..956822fc3cbef 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsActionGroup.xml @@ -17,7 +17,7 @@ <argument name="validationStrategy" type="string" defaultValue="Stop on Error"/> <argument name="allowedErrorsCount" type="string" defaultValue="10"/> <argument name="importFile" type="string"/> - <argument name="importNoticeMessage" type="string"/> + <argument name="importNoticeMessage" type="string" defaultValue=""/> <argument name="importMessageType" type="string" defaultValue="success"/> <argument name="importMessage" type="string" defaultValue="Import successfully done"/> </arguments> @@ -36,28 +36,4 @@ <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="{{importNoticeMessage}}" stepKey="seeNoticeMessage"/> <see selector="{{AdminImportValidationMessagesSection.messageByType(importMessageType)}}" userInput="{{importMessage}}" stepKey="seeImportMessage"/> </actionGroup> - - <actionGroup name="AdminImportProductsWithCheckValidationResultActionGroup" extends="AdminImportProductsActionGroup"> - <arguments> - <argument name="validationNoticeMessage" type="string"/> - <argument name="validationMessage" type="string" defaultValue="File is valid! To start import process press "Import" button"/> - </arguments> - <waitForElementVisible selector="{{AdminImportValidationMessagesSection.notice}}" after="clickCheckDataButton" stepKey="waitForValidationNoticeMessage"/> - <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="{{validationNoticeMessage}}" after="waitForValidationNoticeMessage" stepKey="seeValidationNoticeMessage"/> - <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="{{validationMessage}}" after="seeValidationNoticeMessage" stepKey="seeValidationMessage"/> - </actionGroup> - <actionGroup name="AdminCheckDataForImportProductActionGroup"> - <arguments> - <argument name="behavior" type="string" defaultValue="Add/Update"/> - <argument name="importFile" type="string"/> - </arguments> - <amOnPage url="{{AdminImportIndexPage.url}}" stepKey="goToImportIndexPage"/> - <waitForPageLoad stepKey="adminImportMainSectionLoad"/> - <selectOption selector="{{AdminImportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> - <waitForElementVisible selector="{{AdminImportMainSection.importBehavior}}" stepKey="waitForImportBehaviorElementVisible"/> - <selectOption selector="{{AdminImportMainSection.importBehavior}}" userInput="{{behavior}}" stepKey="selectImportBehaviorOption"/> - <attachFile selector="{{AdminImportMainSection.selectFileToImport}}" userInput="{{importFile}}" stepKey="attachFileForImport"/> - <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithCheckValidationResultActionGroup.xml b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithCheckValidationResultActionGroup.xml new file mode 100644 index 0000000000000..a631ec72a5a72 --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Mftf/ActionGroup/AdminImportProductsWithCheckValidationResultActionGroup.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="AdminImportProductsWithCheckValidationResultActionGroup" extends="AdminImportProductsActionGroup"> + <arguments> + <argument name="validationNoticeMessage" type="string"/> + <argument name="validationMessage" type="string" defaultValue="File is valid! To start import process press "Import" button"/> + </arguments> + <waitForElementVisible selector="{{AdminImportValidationMessagesSection.notice}}" after="clickCheckDataButton" stepKey="waitForValidationNoticeMessage"/> + <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="{{validationNoticeMessage}}" after="waitForValidationNoticeMessage" stepKey="seeValidationNoticeMessage"/> + <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="{{validationMessage}}" after="seeValidationNoticeMessage" stepKey="seeValidationMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminExportAttributeSection.xml b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminExportAttributeSection.xml index 528ad23aaf2bf..f9b07a59c8763 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminExportAttributeSection.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminExportAttributeSection.xml @@ -17,5 +17,6 @@ <element name="selectByIndex" type="button" selector="//tr[@data-repeat-index='{{var}}']//button" parameterized="true" timeout="30"/> <element name="download" type="button" selector="//tr[@data-repeat-index='{{var}}']//a[text()='Download']" parameterized="true" timeout="30"/> <element name="delete" type="button" selector="//tr[@data-repeat-index='{{var}}']//a[text()='Delete']" parameterized="true" timeout="30"/> + <element name="exportFileNameByPosition" type="text" selector="[data-role='grid'] tr[data-repeat-index='{{position}}'] div.data-grid-cell-content" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportHeaderSection.xml b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportHeaderSection.xml index 748580be09406..c39ebbe04f2e1 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportHeaderSection.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportHeaderSection.xml @@ -10,5 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminImportHeaderSection"> <element name="checkDataButton" type="button" selector="#upload_button" timeout="30"/> + <element name="messageNote" type="text" selector="#import_file-note" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportMainSection.xml b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportMainSection.xml index ba1deeebbd89a..0f647aa027c23 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportMainSection.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Section/AdminImportMainSection.xml @@ -17,5 +17,6 @@ <element name="messageError" type="text" selector=".messages div.message-error"/> <element name="validationStrategy" type="select" selector="#basic_behaviorvalidation_strategy"/> <element name="allowedErrorsCount" type="input" selector="#basic_behavior_allowed_error_count"/> + <element name="importImagesFileDirNote" type="input" selector="#import_images_file_dir-note"/> </section> </sections> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckDoubleImportOfProductsTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckDoubleImportOfProductsTest.xml index 909c6101fe53e..e710d9add4c21 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckDoubleImportOfProductsTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckDoubleImportOfProductsTest.xml @@ -33,11 +33,11 @@ <after> <!-- Delete all imported products --> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPage"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilter"/> - <actionGroup ref="adminDataGridSelectPerPage" stepKey="selectNumberOfProductsPerPage"> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/> + <actionGroup ref="AdminDataGridSelectPerPageActionGroup" stepKey="selectNumberOfProductsPerPage"> <argument name="perPage" value="100"/> </actionGroup> - <actionGroup ref="deleteProductsIfTheyExist" stepKey="deleteAllProducts"/> + <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteAllProducts"/> <!-- Delete additional store views --> <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteFirstStoreView"> @@ -48,7 +48,7 @@ </actionGroup> <!-- Delete category --> - <actionGroup ref="DeleteCategory" stepKey="deleteCategory"> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory"> <argument name="categoryEntity" value="Gear"/> </actionGroup> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml new file mode 100644 index 0000000000000..6a2f6ca60acf4 --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest"> + <annotations> + <features value="Import/Export"/> + <stories value="Attribute importing"/> + <title value="Check that some attributes changed the value to an empty after import CSV"/> + <description value="Check that some attributes changed the value to an empty after import CSV"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-11332"/> + <useCaseId value="MAGETWO-61593"/> + <group value="importExport"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToProductIndexPage"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridToDefaultView"/> + <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteAllProducts"/> + <createData entity="productDropDownAttribute" stepKey="productAttribute"/> + <createData entity="productAttributeOption2" stepKey="attributeOptionWithDefaultValue"> + <requiredEntity createDataKey="productAttribute"/> + </createData> + <!--Create category--> + <createData entity="_defaultCategory" stepKey="createCategory"/> + </before> + <after> + <!--Delete Product and Category--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductGridPageLoad"/> + <actionGroup ref="DeleteProductActionGroup" stepKey="deleteProduct1"> + <argument name="productName" value="simpleProductWithShortNameAndSku.name"/> + </actionGroup> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <!--Delete attribute--> + <deleteData createDataKey="productAttribute" stepKey="deleteProductAttribute"/> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + </after> + <!--Create product--> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"/> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductFieldsInAdmin"> + <argument name="product" value="simpleProductWithShortNameAndSku"/> + </actionGroup> + <actionGroup ref="SetCategoryByNameActionGroup" stepKey="addCategoryToProduct"> + <argument name="categoryName" value="$$createCategory.name$$"/> + </actionGroup> + <!--Select created attribute--> + <actionGroup ref="AddProductAttributeInProductModalActionGroup" stepKey="addAttributeToProduct"> + <argument name="attributeCode" value="$$productAttribute.attribute_code$$"/> + </actionGroup> + <!--Check that attribute value is selected--> + <scrollTo selector="{{AdminProductFormSection.attributeTab}}" stepKey="scrollToAttributeTitle1"/> + <conditionalClick selector="{{AdminProductFormSection.attributeTab}}" dependentSelector="{{AdminProductAttributeSection.dropDownAttribute($$productAttribute.attribute_code$$)}}" visible="false" stepKey="expandAttributeTab1"/> + <seeOptionIsSelected selector="{{AdminProductAttributeSection.dropDownAttribute($$productAttribute.attribute_code$$)}}" userInput="option2" stepKey="seeAttributeValueIsSelected1"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + <!--Import product with add/update behavior--> + <actionGroup ref="AdminImportProductsActionGroup" stepKey="adminImportProductsFirstTime"> + <argument name="behavior" value="Add/Update"/> + <argument name="importFile" value="import_simple_product.csv"/> + <argument name="importNoticeMessage" value="Created: 0, Updated: 1, Deleted: 0"/> + </actionGroup> + <!--Check that attribute value is empty after import--> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="filterAndSelectTheProduct2"> + <argument name="productSku" value="{{simpleProductWithShortNameAndSku.sku}}"/> + </actionGroup> + <scrollTo selector="{{AdminProductFormSection.attributeTab}}" stepKey="scrollToAttributeTitle2"/> + <conditionalClick selector="{{AdminProductFormSection.attributeTab}}" dependentSelector="{{AdminProductAttributeSection.dropDownAttribute($$productAttribute.attribute_code$$)}}" visible="false" stepKey="expandAttributeTab2"/> + <seeOptionIsSelected selector="{{AdminProductAttributeSection.dropDownAttribute($$productAttribute.attribute_code$$)}}" userInput="" stepKey="seeAttributeValueIsSelected2"/> + </test> +</tests> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImagesFileDirectoryCorrectExplanationTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImagesFileDirectoryCorrectExplanationTest.xml new file mode 100644 index 0000000000000..989c405324f06 --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImagesFileDirectoryCorrectExplanationTest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminImagesFileDirectoryCorrectExplanationTest"> + <annotations> + <features value="Import/Export"/> + <stories value="Import CSV file"/> + <title value="Images file directory correct explanation test"/> + <description value="Check correct explanation for the 'Images File Directory' option."/> + <severity value="MINOR"/> + <group value="importExport"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + </after> + <amOnPage url="{{AdminImportIndexPage.url}}" stepKey="goToImportIndexPage"/> + <waitForPageLoad stepKey="adminImportMainSectionLoad"/> + <selectOption selector="{{AdminImportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> + <waitForElementVisible selector="{{AdminImportMainSection.importBehavior}}" stepKey="waitForImportBehaviorElementVisible"/> + + <see selector="{{AdminImportMainSection.importImagesFileDirNote}}" userInput='For Type "Local Server" use relative path to <Magento root directory>/var/import/images, e.g. ' stepKey="seeCorrectExplanation"/> + </test> +</tests> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.xml new file mode 100644 index 0000000000000..38c1a09dc534c --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportCSVWithSpecialCharactersTest.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="AdminImportCSVWithSpecialCharactersTest"> + <annotations> + <features value="Import/Export"/> + <stories value="Import CSV file"/> + <title value="Import CSV with special characters"/> + <description value="Import CSV with special characters"/> + <severity value="MAJOR"/> + <testCaseId value="MC-6416"/> + <useCaseId value="MAGETWO-91569"/> + <group value="importExport"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + </after> + <actionGroup ref="AdminCheckDataForImportProductActionGroup" stepKey="adminImportProducts"> + <argument name="behavior" value="Add/Update"/> + <argument name="importFile" value="importSpecChars.csv"/> + </actionGroup> + <see selector="{{AdminImportHeaderSection.messageNote}}" userInput='File must be saved in UTF-8 encoding for proper import' stepKey="seeNoteMessage"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput='File is valid! To start import process press "Import" button' stepKey="seeSuccessMessage"/> + </test> +</tests> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithDeleteBehaviorTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithDeleteBehaviorTest.xml index 4cbb0603d9073..7ec48a3a7e8fd 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithDeleteBehaviorTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithDeleteBehaviorTest.xml @@ -47,8 +47,8 @@ <attachFile selector="{{AdminImportMainSection.selectFileToImport}}" userInput="catalog_products.csv" stepKey="attachFileForImport"/> <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> <click selector="{{AdminImportMainSection.importButton}}" stepKey="clickImportButton"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="Import successfully done" stepKey="assertSuccessMessage"/> - <see selector="{{AdminMessagesSection.notice}}" userInput="Created: 0, Updated: 0, Deleted: 3" stepKey="assertNotice"/> + <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="Import successfully done" stepKey="assertSuccessMessage"/> + <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="Created: 0, Updated: 0, Deleted: 3" stepKey="assertNotice"/> <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchSimpleProductOnBackend"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithErrorEntriesTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithErrorEntriesTest.xml index 94840a4ea6142..e3065f005218b 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithErrorEntriesTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminImportProductsWithErrorEntriesTest.xml @@ -25,7 +25,7 @@ <!--Clear products grid filters--> <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductsGridFilters"/> <!--Delete all imported products--> - <actionGroup ref="deleteProductsIfTheyExist" stepKey="deleteAllProducts"/> + <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteAllProducts"/> <!--Logout from Admin page--> <actionGroup ref="logout" stepKey="logoutFromAdminPage"/> </after> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductImportCSVFileCorrectDifferentFilesTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductImportCSVFileCorrectDifferentFilesTest.xml index eb84929ec8d93..56c1c43bc28d2 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductImportCSVFileCorrectDifferentFilesTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductImportCSVFileCorrectDifferentFilesTest.xml @@ -12,6 +12,7 @@ <annotations> <description value="Product import from CSV file correct from different files."/> <features value="Import/Export"/> + <stories value="Product Import"/> <title value="Product import from CSV file correct from different files."/> <severity value="MAJOR"/> <testCaseId value="MC-17104"/> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml new file mode 100644 index 0000000000000..f0d721075fdfd --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminProductVisibilityDifferentStoreViewsAfterImportTest.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminProductVisibilityDifferentStoreViewsAfterImportTest"> + <annotations> + <features value="Import/Export"/> + <stories value="Import Products"/> + <title value="Checking product visibility in different store views after product importing"/> + <description value="Checking product visibility in different store views after product importing"/> + <severity value="MAJOR"/> + <testCaseId value="MC-6406"/> + <useCaseId value="MAGETWO-59265"/> + <group value="importExport"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Create English and Chinese store views--> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createEnglishStoreView"> + <argument name="StoreGroup" value="_defaultStoreGroup"/> + <argument name="customStore" value="customStoreEN"/> + </actionGroup> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createChineseStoreView"> + <argument name="StoreGroup" value="_defaultStoreGroup"/> + <argument name="customStore" value="storeViewChinese"/> + </actionGroup> + </before> + <after> + <!--Delete all imported products--> + <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPage"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/> + <actionGroup ref="AdminDataGridSelectPerPageActionGroup" stepKey="selectNumberOfProductsPerPage"> + <argument name="perPage" value="100"/> + </actionGroup> + <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteAllProducts"/> + <!--Delete store views--> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteEnglishStoreView"> + <argument name="customStore" value="customStoreEN"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteChineseStoreView"> + <argument name="customStore" value="storeViewChinese"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + </after> + <!--Import products from file--> + <actionGroup ref="AdminImportProductsActionGroup" stepKey="importProducts"> + <argument name="behavior" value="Add/Update"/> + <argument name="importFile" value="import_productsoftwostoresdata.csv"/> + <argument name="importNoticeMessage" value="Created: 2, Updated: 0, Deleted: 0"/> + </actionGroup> + <!--Open imported name4 product--> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="openName4Product"> + <argument name="productSku" value="name4"/> + </actionGroup> + <!--Switch Chinese store view and assert visibility field--> + <comment userInput="Switch Chinese store view and assert visibility field" stepKey="commentAssertVisibilityChineseView"/> + <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="switchToCustomStoreView"> + <argument name="storeViewName" value="{{storeViewChinese.name}}"/> + </actionGroup> + <seeInField selector="{{AdminProductFormSection.visibility}}" userInput="Catalog" stepKey="seeVisibilityFieldForChineseStore"/> + <!--Switch English store view and assert visibility field--> + <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="switchToCustomEnglishView"> + <argument name="storeViewName" value="{{customStoreEN.name}}"/> + </actionGroup> + <seeInField selector="{{AdminProductFormSection.visibility}}" userInput="Catalog" stepKey="seeVisibilityFieldForEnglishView"/> + </test> +</tests> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml new file mode 100644 index 0000000000000..8d56d9d8dad9d --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminURLKeyWorksWhenUpdatingProductThroughImportingCSVTest"> + <annotations> + <features value="Import/Export"/> + <stories value="Import Products"/> + <title value="Check that new URL Key works after updating a product through importing CSV file"/> + <description value="Check that new URL Key works after updating a product through importing CSV file"/> + <severity value="MAJOR"/> + <testCaseId value="MC-6317"/> + <useCaseId value="MAGETWO-91544"/> + <group value="importExport"/> + </annotations> + <before> + <!--Create Product--> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProductBeforeUpdate" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!--Delete created data--> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + </after> + <!--Import product from CSV file--> + <actionGroup ref="AdminImportProductsActionGroup" stepKey="importProduct"> + <argument name="behavior" value="Add/Update"/> + <argument name="importFile" value="simpleProductUpdate.csv"/> + <argument name="importNoticeMessage" value="Created: 0, Updated: 1, Deleted: 0"/> + </actionGroup> + <!--Assert product's updated url--> + <amOnPage url="{{StorefrontProductPage.url('simpleprod')}}" stepKey="navigateToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <seeInCurrentUrl url="{{StorefrontProductPage.url('simpleprod')}}" stepKey="seeUpdatedUrl"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createProduct.name$$" stepKey="assertProductName"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="$$createProduct.sku$$" stepKey="assertProductSku"/> + </test> +</tests> diff --git a/app/code/Magento/ImportExport/Test/Unit/Helper/DataTest.php b/app/code/Magento/ImportExport/Test/Unit/Helper/DataTest.php new file mode 100644 index 0000000000000..85630d2106b45 --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Unit/Helper/DataTest.php @@ -0,0 +1,139 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ImportExport\Test\Unit\Helper; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\App\Helper\Context; +use Magento\Framework\File\Size as FileSize; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\ImportExport\Helper\Data as HelperData; +use PHPUnit\Framework\TestCase; + +/** + * Test class to cover Data Helper + * + * Class \Magento\ImportExport\Test\Unit\Helper\DataTest + */ +class DataTest extends TestCase +{ + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var FileSize|PHPUnit_Framework_MockObject_MockObject + */ + private $fileSizeMock; + + /** + * @var Context|PHPUnit_Framework_MockObject_MockObject + */ + private $contextMock; + + /** + * @var ScopeConfigInterface|PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfigMock; + + /** + * @var HelperData|PHPUnit_Framework_MockObject_MockObject + */ + private $helperData; + + /** + * Set up environment + */ + protected function setUp() + { + $this->contextMock = $this->createMock(Context::class); + $this->fileSizeMock = $this->createMock(FileSize::class); + $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + $this->contextMock->expects($this->any())->method('getScopeConfig')->willReturn($this->scopeConfigMock); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->helperData = $this->objectManagerHelper->getObject( + HelperData::class, + [ + 'context' => $this->contextMock, + 'fileSize' => $this->fileSizeMock + ] + ); + } + + /** + * Test getMaxUploadSizeMessage() with data provider below + * + * @param float $maxImageSize + * @param string $expected + * @return void + * @dataProvider getMaxUploadSizeMessageDataProvider + */ + public function testGetMaxUploadSizeMessage($maxImageSize, $expected) + { + $this->fileSizeMock->expects($this->any())->method('getMaxFileSizeInMb')->willReturn($maxImageSize); + $this->assertEquals($expected, $this->helperData->getMaxUploadSizeMessage()); + } + + /** + * DataProvider for testGetMaxUploadSizeMessage() function + * + * @return array + */ + public function getMaxUploadSizeMessageDataProvider() + { + return [ + 'Test with max image size = 10Mb' => [ + 'maxImageSize' => 10, + 'expected' => 'Make sure your file isn\'t more than 10M.', + ], + 'Test with max image size = 0' => [ + 'maxImageSize' => 0, + 'expected' => 'We can\'t provide the upload settings right now.', + ] + ]; + } + + /** + * Test getLocalValidPaths() + * + * @return void + */ + public function testGetLocalValidPaths() + { + $paths = [ + 'available' => [ + 'export_xml' => 'var/export/*/*.xml', + 'export_csv' => 'var/export/*/*.csv', + 'import_xml' => 'var/import/*/*.xml', + 'import_csv' => 'var/import/*/*.csv', + ] + ]; + $this->scopeConfigMock->expects($this->any())->method('getValue') + ->with(HelperData::XML_PATH_EXPORT_LOCAL_VALID_PATH) + ->willReturn($paths); + + $this->assertEquals($paths, $this->helperData->getLocalValidPaths()); + } + + /** + * Test getBunchSize() + * + * @return void + */ + public function testGetBunchSize() + { + $bunchSize = '100'; + + $this->scopeConfigMock->expects($this->any())->method('getValue') + ->with(HelperData::XML_PATH_BUNCH_SIZE) + ->willReturn($bunchSize); + + $this->assertEquals(100, $this->helperData->getBunchSize()); + } +} diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Source/Export/EntityTest.php b/app/code/Magento/ImportExport/Test/Unit/Model/Source/Export/EntityTest.php new file mode 100644 index 0000000000000..d839e97be8278 --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Unit/Model/Source/Export/EntityTest.php @@ -0,0 +1,95 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\ImportExport\Test\Unit\Model\Source\Export; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\ImportExport\Model\Export\ConfigInterface; +use Magento\ImportExport\Model\Source\Export\Entity; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class EntityTest extends TestCase +{ + /** + * @var ConfigInterface|MockObject + */ + private $exportConfigMock; + + /** + * @var Entity + */ + private $model; + + /** + * Setup environment for test + */ + protected function setUp() + { + $this->exportConfigMock = $this->createMock(ConfigInterface::class); + + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + Entity::class, + [ + 'exportConfig' => $this->exportConfigMock + ] + ); + } + + /** + * Test toOptionArray with data provider + * + * @param array $entities + * @param array $expected + * @dataProvider toOptionArrayDataProvider + */ + public function testToOptionArray($entities, $expected) + { + $this->exportConfigMock->expects($this->any())->method('getEntities')->willReturn($entities); + + $this->assertEquals($expected, $this->model->toOptionArray()); + } + + /** + * Data Provider for test toOptionArray + * + * @return array + */ + public function toOptionArrayDataProvider() + { + return [ + 'Empty Entity' => [ + [], + [ + [ + 'label' => (string)__('-- Please Select --'), + 'value' => '' + ] + ] + ], + 'Has entities' => [ + [ + 'entity1' => [ + 'label' => 'Entity 1' + ] + ], + [ + [ + 'label' => (string)__('-- Please Select --'), + 'value' => '' + ], + [ + 'label' => (string)__('Entity 1'), + 'value' => 'entity1' + ] + ] + ] + ]; + } +} diff --git a/app/code/Magento/ImportExport/Test/Unit/Model/Source/Export/FormatTest.php b/app/code/Magento/ImportExport/Test/Unit/Model/Source/Export/FormatTest.php new file mode 100644 index 0000000000000..416bab2c124f1 --- /dev/null +++ b/app/code/Magento/ImportExport/Test/Unit/Model/Source/Export/FormatTest.php @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\ImportExport\Test\Unit\Model\Source\Export; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\ImportExport\Model\Export\ConfigInterface; +use Magento\ImportExport\Model\Source\Export\Format; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class FormatTest extends TestCase +{ + /** + * @var ConfigInterface|MockObject + */ + private $exportConfigMock; + + /** + * @var Format + */ + private $model; + + /** + * Setup environment for test + */ + protected function setUp() + { + $this->exportConfigMock = $this->createMock(ConfigInterface::class); + + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + Format::class, + [ + 'exportConfig' => $this->exportConfigMock + ] + ); + } + + /** + * Test toOptionArray with data provider + * + * @param array $fileFormats + * @param array $expected + * @dataProvider toOptionArrayDataProvider + */ + public function testToOptionArray($fileFormats, $expected) + { + $this->exportConfigMock->expects($this->any())->method('getFileFormats')->willReturn($fileFormats); + + $this->assertEquals($expected, $this->model->toOptionArray()); + } + + /** + * Data Provider for test toOptionArray + * + * @return array + */ + public function toOptionArrayDataProvider() + { + return [ + 'Empty file format' => [ + [], + [] + ], + 'Has file format' => [ + [ + 'fileFormat1' => [ + 'label' => 'File Format 1' + ] + ], + [ + [ + 'label' => (string)__('File Format 1'), + 'value' => 'fileFormat1' + ] + ] + ] + ]; + } +} diff --git a/app/code/Magento/ImportExport/Ui/Component/Columns/ExportGridActions.php b/app/code/Magento/ImportExport/Ui/Component/Columns/ExportGridActions.php index b5e36ccd9fbab..2cdc39866a521 100644 --- a/app/code/Magento/ImportExport/Ui/Component/Columns/ExportGridActions.php +++ b/app/code/Magento/ImportExport/Ui/Component/Columns/ExportGridActions.php @@ -56,11 +56,17 @@ public function prepareDataSource(array $dataSource) $name = $this->getData('name'); if (isset($item['file_name'])) { $item[$name]['view'] = [ - 'href' => $this->urlBuilder->getUrl(Download::URL, ['filename' => $item['file_name']]), + 'href' => $this->urlBuilder->getUrl( + Download::URL, + ['_query' => ['filename' => $item['file_name']]] + ), 'label' => __('Download') ]; $item[$name]['delete'] = [ - 'href' => $this->urlBuilder->getUrl(Delete::URL, ['filename' => $item['file_name']]), + 'href' => $this->urlBuilder->getUrl( + Delete::URL, + ['_query' => ['filename' => $item['file_name']]] + ), 'label' => __('Delete'), 'confirm' => [ 'title' => __('Delete'), diff --git a/app/code/Magento/ImportExport/Ui/DataProvider/ExportFileDataProvider.php b/app/code/Magento/ImportExport/Ui/DataProvider/ExportFileDataProvider.php index f9f5f446ba429..57d1982d3500f 100644 --- a/app/code/Magento/ImportExport/Ui/DataProvider/ExportFileDataProvider.php +++ b/app/code/Magento/ImportExport/Ui/DataProvider/ExportFileDataProvider.php @@ -94,13 +94,13 @@ public function getData() return $emptyResponse; } - $files = $this->file->readDirectoryRecursively($directory->getAbsolutePath() . 'export/'); + $files = $this->getExportFiles($directory->getAbsolutePath() . 'export/'); if (empty($files)) { return $emptyResponse; } $result = []; foreach ($files as $file) { - $result['items'][]['file_name'] = $this->fileIO->getPathInfo($file)['basename']; + $result['items'][]['file_name'] = $this->getPathToExportFile($this->fileIO->getPathInfo($file)); } $pageSize = (int) $this->request->getParam('paging')['pageSize']; @@ -111,4 +111,55 @@ public function getData() return $result; } + + /** + * Return relative export file path after "var/export" + * + * @param mixed $file + * @return string + */ + private function getPathToExportFile($file): string + { + $directory = $this->fileSystem->getDirectoryRead(DirectoryList::VAR_DIR); + $delimiter = '/'; + $cutPath = explode( + $delimiter, + $directory->getAbsolutePath() . 'export' + ); + $filePath = explode( + $delimiter, + $file['dirname'] + ); + + return ltrim( + implode($delimiter, array_diff($filePath, $cutPath)) . $delimiter . $file['basename'], + $delimiter + ); + } + + /** + * Get files from directory path, sort them by date modified and return sorted array of full path to files + * + * @param string $directoryPath + * @return array + * @throws \Magento\Framework\Exception\FileSystemException + */ + private function getExportFiles(string $directoryPath): array + { + $sortedFiles = []; + $files = $this->file->readDirectoryRecursively($directoryPath); + if (empty($files)) { + return []; + } + foreach ($files as $filePath) { + if ($this->file->isFile($filePath)) { + //phpcs:ignore Magento2.Functions.DiscouragedFunction + $sortedFiles[filemtime($filePath)] = $filePath; + } + } + //sort array elements using key value + krsort($sortedFiles); + + return $sortedFiles; + } } diff --git a/app/code/Magento/ImportExport/i18n/en_US.csv b/app/code/Magento/ImportExport/i18n/en_US.csv index 5787d6f7d02b6..fae93c78baa09 100644 --- a/app/code/Magento/ImportExport/i18n/en_US.csv +++ b/app/code/Magento/ImportExport/i18n/en_US.csv @@ -29,9 +29,7 @@ Import,Import "File to Import","File to Import" "Select File to Import","Select File to Import" "Images File Directory","Images File Directory" -"For Type ""Local Server"" use relative path to Magento installation, - e.g. var/export, var/import, var/export/some/dir","For Type ""Local Server"" use relative path to Magento installation, - e.g. var/export, var/import, var/export/some/dir" +"For Type ""Local Server"" use relative path to <Magento root directory>/var/import/images, e.g. <i>product_images</i>, <i>import_images/batch1</i>.<br><br>For example, in case <i>product_images</i>, files should be placed into <i><Magento root directory>/var/import/images/product_images</i> folder.","For Type ""Local Server"" use relative path to <Magento root directory>/var/import/images, e.g. <i>product_images</i>, <i>import_images/batch1</i>.<br><br>For example, in case <i>product_images</i>, files should be placed into <i><Magento root directory>/var/import/images/product_images</i> folder." "Download Sample File","Download Sample File" "Please correct the data sent value.","Please correct the data sent value." Import/Export,Import/Export @@ -123,5 +121,6 @@ Summary,Summary "New product data is added to existing product data entries in the database. All fields except SKU can be updated.","New product data is added to existing product data entries in the database. All fields except SKU can be updated." "All existing product data is replaced with the imported new data. <b>Exercise caution when replacing data. All existing product data will be completely cleared and all references in the system will be lost.</b>","All existing product data is replaced with the imported new data. <b>Exercise caution when replacing data. All existing product data will be completely cleared and all references in the system will be lost.</b>" "Any entities in the import data that match existing entities in the database are deleted from the database.","Any entities in the import data that match existing entities in the database are deleted from the database." +"Message is added to queue, wait to get your file soon. Make sure your cron job is running to export the file","Message is added to queue, wait to get your file soon. Make sure your cron job is running to export the file" "Invalid data","Invalid data" -"Invalid response","Invalid response" \ No newline at end of file +"Invalid response","Invalid response" diff --git a/app/code/Magento/ImportExport/registration.php b/app/code/Magento/ImportExport/registration.php index 5ecc71fa8676c..85df9b374fb4c 100644 --- a/app/code/Magento/ImportExport/registration.php +++ b/app/code/Magento/ImportExport/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_ImportExport', __DIR__); diff --git a/app/code/Magento/Indexer/Console/Command/IndexerReindexCommand.php b/app/code/Magento/Indexer/Console/Command/IndexerReindexCommand.php index fffa4503e14a7..c7207c853b95e 100644 --- a/app/code/Magento/Indexer/Console/Command/IndexerReindexCommand.php +++ b/app/code/Magento/Indexer/Console/Command/IndexerReindexCommand.php @@ -6,16 +6,16 @@ namespace Magento\Indexer\Console\Command; +use Magento\Framework\App\ObjectManagerFactory; use Magento\Framework\Console\Cli; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Indexer\Config\DependencyInfoProvider; +use Magento\Framework\Indexer\ConfigInterface; use Magento\Framework\Indexer\IndexerInterface; use Magento\Framework\Indexer\IndexerRegistry; use Magento\Framework\Indexer\StateInterface; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Magento\Framework\Indexer\ConfigInterface; -use Magento\Framework\App\ObjectManagerFactory; /** * Command to run indexers @@ -78,6 +78,9 @@ protected function execute(InputInterface $input, OutputInterface $output) foreach ($this->getIndexers($input) as $indexer) { try { $this->validateIndexerStatus($indexer); + + $output->write($indexer->getTitle() . ' index '); + $startTime = microtime(true); $indexerConfig = $this->getConfig()->getIndexer($indexer->getId()); $sharedIndex = $indexerConfig['shared_index']; @@ -90,17 +93,21 @@ protected function execute(InputInterface $input, OutputInterface $output) } } $resultTime = microtime(true) - $startTime; + $output->writeln( - $indexer->getTitle() . ' index has been rebuilt successfully in ' . gmdate('H:i:s', $resultTime) + __('has been rebuilt successfully in %time', ['time' => gmdate('H:i:s', $resultTime)]) ); $returnValue = Cli::RETURN_SUCCESS; } catch (LocalizedException $e) { - $output->writeln($e->getMessage()); + $output->writeln(__('exception: %message', ['message' => $e->getMessage()])); } catch (\Exception $e) { - $output->writeln($indexer->getTitle() . ' indexer process unknown error:'); + $output->writeln('process unknown error:'); $output->writeln($e->getMessage()); + + $output->writeln($e->getTraceAsString(), OutputInterface::VERBOSITY_DEBUG); } } + return $returnValue; } @@ -111,25 +118,23 @@ protected function execute(InputInterface $input, OutputInterface $output) */ protected function getIndexers(InputInterface $input) { - $indexers = parent::getIndexers($input); + $indexers = parent::getIndexers($input); $allIndexers = $this->getAllIndexers(); if (!array_diff_key($allIndexers, $indexers)) { return $indexers; } - $relatedIndexers = []; - $dependentIndexers = []; + $relatedIndexers = [[]]; + $dependentIndexers = [[]]; + foreach ($indexers as $indexer) { - $relatedIndexers = array_merge( - $relatedIndexers, - $this->getRelatedIndexerIds($indexer->getId()) - ); - $dependentIndexers = array_merge( - $dependentIndexers, - $this->getDependentIndexerIds($indexer->getId()) - ); + $relatedIndexers[] = $this->getRelatedIndexerIds($indexer->getId()); + $dependentIndexers[] = $this->getDependentIndexerIds($indexer->getId()); } + $relatedIndexers = array_merge(...$relatedIndexers); + $dependentIndexers = array_merge(...$dependentIndexers); + $invalidRelatedIndexers = []; foreach (array_unique($relatedIndexers) as $relatedIndexer) { if ($allIndexers[$relatedIndexer]->isInvalid()) { @@ -157,18 +162,15 @@ protected function getIndexers(InputInterface $input) * @param string $indexerId * @return array */ - private function getRelatedIndexerIds(string $indexerId) + private function getRelatedIndexerIds(string $indexerId): array { - $relatedIndexerIds = []; + $relatedIndexerIds = [[]]; foreach ($this->getDependencyInfoProvider()->getIndexerIdsToRunBefore($indexerId) as $relatedIndexerId) { - $relatedIndexerIds = array_merge( - $relatedIndexerIds, - [$relatedIndexerId], - $this->getRelatedIndexerIds($relatedIndexerId) - ); + $relatedIndexerIds[] = [$relatedIndexerId]; + $relatedIndexerIds[] = $this->getRelatedIndexerIds($relatedIndexerId); } - return array_unique($relatedIndexerIds); + return array_unique(array_merge(...$relatedIndexerIds)); } /** @@ -177,21 +179,18 @@ private function getRelatedIndexerIds(string $indexerId) * @param string $indexerId * @return array */ - private function getDependentIndexerIds(string $indexerId) + private function getDependentIndexerIds(string $indexerId): array { - $dependentIndexerIds = []; + $dependentIndexerIds = [[]]; foreach (array_keys($this->getConfig()->getIndexers()) as $id) { $dependencies = $this->getDependencyInfoProvider()->getIndexerIdsToRunBefore($id); if (array_search($indexerId, $dependencies) !== false) { - $dependentIndexerIds = array_merge( - $dependentIndexerIds, - [$id], - $this->getDependentIndexerIds($id) - ); + $dependentIndexerIds[] = [$id]; + $dependentIndexerIds[] = $this->getDependentIndexerIds($id); } } - return array_unique($dependentIndexerIds); + return array_unique(array_merge(...$dependentIndexerIds)); } /** diff --git a/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php b/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php index cefb070f60b74..26feb38392e5f 100644 --- a/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php +++ b/app/code/Magento/Indexer/Console/Command/IndexerStatusCommand.php @@ -5,11 +5,11 @@ */ namespace Magento\Indexer\Console\Command; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; use Magento\Framework\Indexer; use Magento\Framework\Mview; use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; /** * Command for displaying status of indexers. @@ -17,7 +17,7 @@ class IndexerStatusCommand extends AbstractIndexerManageCommand { /** - * {@inheritdoc} + * @inheritdoc */ protected function configure() { @@ -29,12 +29,14 @@ protected function configure() } /** - * {@inheritdoc} + * @inheritdoc + * @param InputInterface $input + * @param OutputInterface $output */ protected function execute(InputInterface $input, OutputInterface $output) { $table = new Table($output); - $table->setHeaders(['Title', 'Status', 'Update On', 'Schedule Status', 'Schedule Updated']); + $table->setHeaders(['ID', 'Title', 'Status', 'Update On', 'Schedule Status', 'Schedule Updated']); $rows = []; @@ -43,6 +45,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $view = $indexer->getView(); $rowData = [ + 'ID' => $indexer->getId(), 'Title' => $indexer->getTitle(), 'Status' => $this->getStatus($indexer), 'Update On' => $indexer->isScheduled() ? 'Schedule' : 'Save', @@ -59,15 +62,20 @@ protected function execute(InputInterface $input, OutputInterface $output) $rows[] = $rowData; } - usort($rows, function ($comp1, $comp2) { - return strcmp($comp1['Title'], $comp2['Title']); - }); + usort( + $rows, + function (array $comp1, array $comp2) { + return strcmp($comp1['Title'], $comp2['Title']); + } + ); $table->addRows($rows); $table->render(); } /** + * Returns the current status of the indexer + * * @param Indexer\IndexerInterface $indexer * @return string */ @@ -89,6 +97,8 @@ private function getStatus(Indexer\IndexerInterface $indexer) } /** + * Returns the pending count of the view + * * @param Mview\ViewInterface $view * @return string */ diff --git a/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php b/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php index 829df74a1b0ed..fcfdce4f58620 100644 --- a/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php +++ b/app/code/Magento/Indexer/Model/Indexer/DependencyDecorator.php @@ -55,11 +55,15 @@ public function __construct( */ public function __call(string $method, array $args) { + //phpcs:ignore Magento2.Functions.DiscouragedFunction return call_user_func_array([$this->indexer, $method], $args); } /** + * Sleep magic. + * * @return array + * @SuppressWarnings(PHPMD.SerializationAware) */ public function __sleep() { @@ -218,9 +222,16 @@ public function isWorking(): bool public function invalidate() { $this->indexer->invalidate(); - $dependentIndexerIds = $this->dependencyInfoProvider->getIndexerIdsToRunAfter($this->indexer->getId()); - foreach ($dependentIndexerIds as $indexerId) { - $this->indexerRegistry->get($indexerId)->invalidate(); + $currentIndexerId = $this->indexer->getId(); + $idsToRunBefore = $this->dependencyInfoProvider->getIndexerIdsToRunBefore($currentIndexerId); + $idsToRunAfter = $this->dependencyInfoProvider->getIndexerIdsToRunAfter($currentIndexerId); + + $indexersToInvalidate = array_unique(array_merge($idsToRunBefore, $idsToRunAfter)); + foreach ($indexersToInvalidate as $indexerId) { + $indexer = $this->indexerRegistry->get($indexerId); + if (!$indexer->isInvalid()) { + $indexer->invalidate(); + } } } diff --git a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/AdminReindexAndFlushCacheActionGroup.xml b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/AdminReindexAndFlushCacheActionGroup.xml new file mode 100644 index 0000000000000..d474094dcd54b --- /dev/null +++ b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/AdminReindexAndFlushCacheActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminReindexAndFlushCache"> + <annotations> + <description>Run reindex and flush cache.</description> + </annotations> + + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup.xml b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup.xml deleted file mode 100644 index 642982f37866f..0000000000000 --- a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/IndexerActionGroup.xml +++ /dev/null @@ -1,44 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="updateIndexerBySchedule"> - <annotations> - <description>Goes to the Index Management page. Checks the provided Indexer Name. Selects 'Update by Schedule'. Clicks on Submit.</description> - </annotations> - <arguments> - <argument name="indexerName" type="string"/> - </arguments> - - <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/indexer/indexer/list/" stepKey="amOnIndexManagementPage"/> - <waitForPageLoad stepKey="waitForIndexManagementPageToLoad"/> - <click selector="{{AdminIndexManagementSection.indexerCheckbox(indexerName)}}" stepKey="selectIndexer1"/> - <selectOption selector="{{AdminIndexManagementSection.massActionSelect}}" userInput="change_mode_changelog" stepKey="selectUpdateBySchedule"/> - <click selector="{{AdminIndexManagementSection.massActionSubmit}}" stepKey="submitIndexerForm"/> - <!-- No re-indexing is done as part of this actionGroup since the test required no re-indexing --> - <waitForPageLoad stepKey="waitForSave"/> - </actionGroup> - - <actionGroup name="updateIndexerOnSave"> - <annotations> - <description>Goes to the Index Management page. Checks the provided Indexer Name. Selects 'Update on Save'. Clicks on Submit.</description> - </annotations> - <arguments> - <argument name="indexerName" type="string"/> - </arguments> - - <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/indexer/indexer/list/" stepKey="amOnIndexManagementPage2"/> - <waitForPageLoad stepKey="waitForIndexManagementPageToLoad2"/> - <click selector="{{AdminIndexManagementSection.indexerCheckbox(indexerName)}}" stepKey="selectIndexer2"/> - <selectOption selector="{{AdminIndexManagementSection.massActionSelect}}" userInput="change_mode_onthefly" stepKey="selectUpdateOnSave"/> - <click selector="{{AdminIndexManagementSection.massActionSubmit}}" stepKey="submitIndexerForm2"/> - <!-- No re-indexing is done as part of this actionGroup since the test required no re-indexing --> - <waitForPageLoad stepKey="waitForSave2"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/UpdateIndexerByScheduleActionGroup.xml b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/UpdateIndexerByScheduleActionGroup.xml new file mode 100644 index 0000000000000..3b6c8c1504a3a --- /dev/null +++ b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/UpdateIndexerByScheduleActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="UpdateIndexerByScheduleActionGroup"> + <annotations> + <description>Goes to the Index Management page. Checks the provided Indexer Name. Selects 'Update by Schedule'. Clicks on Submit.</description> + </annotations> + <arguments> + <argument name="indexerName" type="string"/> + </arguments> + + <amOnPage url="{{AdminIndexManagementPage.url}}" stepKey="amOnIndexManagementPage"/> + <waitForPageLoad stepKey="waitForIndexManagementPageToLoad"/> + <click selector="{{AdminIndexManagementSection.indexerCheckbox(indexerName)}}" stepKey="selectIndexer1"/> + <selectOption selector="{{AdminIndexManagementSection.massActionSelect}}" userInput="change_mode_changelog" stepKey="selectUpdateBySchedule"/> + <click selector="{{AdminIndexManagementSection.massActionSubmit}}" stepKey="submitIndexerForm"/> + <!-- No re-indexing is done as part of this actionGroup since the test required no re-indexing --> + <waitForPageLoad stepKey="waitForSave"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/UpdateIndexerOnSaveActionGroup.xml b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/UpdateIndexerOnSaveActionGroup.xml new file mode 100644 index 0000000000000..023b5b8e0aa0e --- /dev/null +++ b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/UpdateIndexerOnSaveActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="UpdateIndexerOnSaveActionGroup"> + <annotations> + <description>Goes to the Index Management page. Checks the provided Indexer Name. Selects 'Update on Save'. Clicks on Submit.</description> + </annotations> + <arguments> + <argument name="indexerName" type="string"/> + </arguments> + + <amOnPage url="{{AdminIndexManagementPage.url}}" stepKey="amOnIndexManagementPage2"/> + <waitForPageLoad stepKey="waitForIndexManagementPageToLoad2"/> + <click selector="{{AdminIndexManagementSection.indexerCheckbox(indexerName)}}" stepKey="selectIndexer2"/> + <selectOption selector="{{AdminIndexManagementSection.massActionSelect}}" userInput="change_mode_onthefly" stepKey="selectUpdateOnSave"/> + <click selector="{{AdminIndexManagementSection.massActionSubmit}}" stepKey="submitIndexerForm2"/> + <!-- No re-indexing is done as part of this actionGroup since the test required no re-indexing --> + <waitForPageLoad stepKey="waitForSave2"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerReindexCommandTest.php b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerReindexCommandTest.php index 4877ceaaec85b..3a1bf113b942a 100644 --- a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerReindexCommandTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerReindexCommandTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Indexer\Test\Unit\Console\Command; use Magento\Framework\Console\Cli; @@ -21,6 +22,7 @@ */ class IndexerReindexCommandTest extends AbstractIndexerCommandCommonSetup { + const STUB_INDEXER_NAME = 'Indexer Name'; /** * Command being tested * @@ -85,30 +87,41 @@ public function testGetOptions() $this->stateMock->expects($this->never())->method('setAreaCode'); $this->command = new IndexerReindexCommand($this->objectManagerFactory); $optionsList = $this->command->getInputList(); - $this->assertSame(1, sizeof($optionsList)); + $this->assertSame(1, count($optionsList)); $this->assertSame('index', $optionsList[0]->getName()); } public function testExecuteAll() { - $this->configMock->expects($this->once())->method('getIndexer')->will($this->returnValue([ - 'title' => 'Title_indexerOne', - 'shared_index' => null - ])); + $this->configMock->expects($this->once()) + ->method('getIndexer') + ->will( + $this->returnValue( + [ + 'title' => 'Title_indexerOne', + 'shared_index' => null + ] + ) + ); $this->configureAdminArea(); - $this->initIndexerCollectionByItems([ - $this->getIndexerMock( - ['reindexAll', 'getStatus'], - ['indexer_id' => 'id_indexerOne', 'title' => 'Title_indexerOne'] - ) - ]); + $this->initIndexerCollectionByItems( + [ + $this->getIndexerMock( + ['reindexAll', 'getStatus'], + ['indexer_id' => 'id_indexerOne', 'title' => self::STUB_INDEXER_NAME] + ) + ] + ); $this->indexerFactory->expects($this->never())->method('create'); $this->command = new IndexerReindexCommand($this->objectManagerFactory); $commandTester = new CommandTester($this->command); $commandTester->execute([]); $actualValue = $commandTester->getDisplay(); $this->assertSame(Cli::RETURN_SUCCESS, $commandTester->getStatusCode()); - $this->assertStringStartsWith('Title_indexerOne index has been rebuilt successfully in', $actualValue); + $this->assertStringStartsWith( + self::STUB_INDEXER_NAME . ' index has been rebuilt successfully in', + $actualValue + ); } /** @@ -118,6 +131,7 @@ public function testExecuteAll() * @param array $reindexAllCallMatchers * @param array $executedIndexers * @param array $executedSharedIndexers + * * @dataProvider executeWithIndexDataProvider */ public function testExecuteWithIndex( @@ -164,6 +178,7 @@ public function testExecuteWithIndex( $this->objectManagerFactory, $this->indexerRegistryMock ); + $commandTester = new CommandTester($this->command); $commandTester->execute(['index' => $inputIndexers]); $this->assertSame(Cli::RETURN_SUCCESS, $commandTester->getStatusCode()); @@ -210,6 +225,7 @@ private function addAllIndexersToConfigMock(array $indexers) /** * @param array|null $methods * @param array $data + * * @return \PHPUnit_Framework_MockObject_MockObject|StateInterface */ private function getStateMock(array $methods = null, array $data = []) @@ -329,11 +345,12 @@ public function executeWithIndexDataProvider() 'indexer_5' => $this->once(), ], 'executed_indexers' => ['indexer_3', 'indexer_1', 'indexer_5'], - 'executed_shared_indexers' => [['indexer_2'],['indexer_3']], + 'executed_shared_indexers' => [['indexer_2'], ['indexer_3']], ], 'With dependencies and multiple indexers in request' => [ 'inputIndexers' => [ - 'indexer_1', 'indexer_3' + 'indexer_1', + 'indexer_3' ], 'indexers' => [ 'indexer_2' => [ @@ -394,7 +411,10 @@ public function executeWithIndexDataProvider() public function testExecuteWithLocalizedException() { $this->configureAdminArea(); - $indexerOne = $this->getIndexerMock(['reindexAll', 'getStatus'], ['indexer_id' => 'indexer_1']); + $indexerOne = $this->getIndexerMock( + ['reindexAll', 'getStatus'], + ['indexer_id' => 'indexer_1', 'title' => self::STUB_INDEXER_NAME] + ); $localizedException = new LocalizedException(new Phrase('Some Exception Message')); $indexerOne->expects($this->once())->method('reindexAll')->will($this->throwException($localizedException)); $this->initIndexerCollectionByItems([$indexerOne]); @@ -403,7 +423,10 @@ public function testExecuteWithLocalizedException() $commandTester->execute(['index' => ['indexer_1']]); $actualValue = $commandTester->getDisplay(); $this->assertSame(Cli::RETURN_FAILURE, $commandTester->getStatusCode()); - $this->assertStringStartsWith('Some Exception Message', $actualValue); + $this->assertStringStartsWith( + self::STUB_INDEXER_NAME . ' index exception: Some Exception Message', + $actualValue + ); } public function testExecuteWithException() @@ -422,7 +445,7 @@ public function testExecuteWithException() $commandTester->execute(['index' => ['indexer_1']]); $actualValue = $commandTester->getDisplay(); $this->assertSame(Cli::RETURN_FAILURE, $commandTester->getStatusCode()); - $this->assertStringStartsWith('Title_indexer_1' . ' indexer process unknown error:', $actualValue); + $this->assertStringStartsWith('Title_indexer_1' . ' index process unknown error:', $actualValue); } public function testExecuteWithExceptionInGetIndexers() @@ -445,13 +468,16 @@ public function testExecuteWithExceptionInGetIndexers() "The following requested index types are not supported: '" . join("', '", $inputIndexers) . "'." . PHP_EOL . 'Supported types: ' - . join(", ", array_map( - function ($item) { - /** @var IndexerInterface $item */ - $item->getId(); - }, - $this->indexerCollectionMock->getItems() - )) + . join( + ", ", + array_map( + function ($item) { + /** @var IndexerInterface $item */ + $item->getId(); + }, + $this->indexerCollectionMock->getItems() + ) + ) ); $this->command = new IndexerReindexCommand($this->objectManagerFactory); $commandTester = new CommandTester($this->command); diff --git a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerSetModeCommandTest.php b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerSetModeCommandTest.php index ad3ae88816db4..da47753970169 100644 --- a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerSetModeCommandTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerSetModeCommandTest.php @@ -26,7 +26,7 @@ public function testGetOptions() $this->stateMock->expects($this->never())->method('setAreaCode')->with(FrontNameResolver::AREA_CODE); $this->command = new IndexerSetModeCommand($this->objectManagerFactory); $optionsList = $this->command->getInputList(); - $this->assertSame(2, sizeof($optionsList)); + $this->assertSame(2, count($optionsList)); $this->assertSame('mode', $optionsList[0]->getName()); $this->assertSame('index', $optionsList[1]->getName()); } diff --git a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerShowModeCommandTest.php b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerShowModeCommandTest.php index fe6020cb07167..ef8fb58c1ef41 100644 --- a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerShowModeCommandTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerShowModeCommandTest.php @@ -23,7 +23,7 @@ public function testGetOptions() $this->stateMock->expects($this->never())->method('setAreaCode')->with(FrontNameResolver::AREA_CODE); $this->command = new IndexerShowModeCommand($this->objectManagerFactory); $optionsList = $this->command->getInputList(); - $this->assertSame(1, sizeof($optionsList)); + $this->assertSame(1, count($optionsList)); $this->assertSame('index', $optionsList[0]->getName()); } diff --git a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php index 8498bd183af21..963a0b21c1f96 100644 --- a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerStatusCommandTest.php @@ -96,7 +96,8 @@ public function testExecuteAll(array $indexers) $linesOutput = array_filter(explode(PHP_EOL, $commandTester->getDisplay())); - $spacer = '+----------------+------------------+-----------+-------------------------+---------------------+'; + $spacer = '+-----------+----------------+------------------+-----------+-------------------------+' + . '---------------------+'; $this->assertCount(8, $linesOutput, 'There should be 8 lines output. 3 Spacers, 1 header, 4 content.'); $this->assertEquals($linesOutput[0], $spacer, "Lines 0, 2, 7 should be spacer lines"); @@ -104,39 +105,44 @@ public function testExecuteAll(array $indexers) $this->assertEquals($linesOutput[7], $spacer, "Lines 0, 2, 7 should be spacer lines"); $headerValues = array_values(array_filter(explode('|', $linesOutput[1]))); - $this->assertEquals('Title', trim($headerValues[0])); - $this->assertEquals('Status', trim($headerValues[1])); - $this->assertEquals('Update On', trim($headerValues[2])); - $this->assertEquals('Schedule Status', trim($headerValues[3])); - $this->assertEquals('Schedule Updated', trim($headerValues[4])); + $this->assertEquals('ID', trim($headerValues[0])); + $this->assertEquals('Title', trim($headerValues[1])); + $this->assertEquals('Status', trim($headerValues[2])); + $this->assertEquals('Update On', trim($headerValues[3])); + $this->assertEquals('Schedule Status', trim($headerValues[4])); + $this->assertEquals('Schedule Updated', trim($headerValues[5])); $indexer1 = array_values(array_filter(explode('|', $linesOutput[3]))); - $this->assertEquals('Title_indexer1', trim($indexer1[0])); - $this->assertEquals('Ready', trim($indexer1[1])); - $this->assertEquals('Schedule', trim($indexer1[2])); - $this->assertEquals('idle (10 in backlog)', trim($indexer1[3])); - $this->assertEquals('2017-01-01 11:11:11', trim($indexer1[4])); + $this->assertEquals('indexer_1', trim($indexer1[0])); + $this->assertEquals('Title_indexer1', trim($indexer1[1])); + $this->assertEquals('Ready', trim($indexer1[2])); + $this->assertEquals('Schedule', trim($indexer1[3])); + $this->assertEquals('idle (10 in backlog)', trim($indexer1[4])); + $this->assertEquals('2017-01-01 11:11:11', trim($indexer1[5])); $indexer2 = array_values(array_filter(explode('|', $linesOutput[4]))); - $this->assertEquals('Title_indexer2', trim($indexer2[0])); - $this->assertEquals('Reindex required', trim($indexer2[1])); - $this->assertEquals('Save', trim($indexer2[2])); - $this->assertEquals('', trim($indexer2[3])); + $this->assertEquals('indexer_2', trim($indexer2[0])); + $this->assertEquals('Title_indexer2', trim($indexer2[1])); + $this->assertEquals('Reindex required', trim($indexer2[2])); + $this->assertEquals('Save', trim($indexer2[3])); $this->assertEquals('', trim($indexer2[4])); + $this->assertEquals('', trim($indexer2[5])); $indexer3 = array_values(array_filter(explode('|', $linesOutput[5]))); - $this->assertEquals('Title_indexer3', trim($indexer3[0])); - $this->assertEquals('Processing', trim($indexer3[1])); - $this->assertEquals('Schedule', trim($indexer3[2])); - $this->assertEquals('idle (100 in backlog)', trim($indexer3[3])); - $this->assertEquals('2017-01-01 11:11:11', trim($indexer3[4])); + $this->assertEquals('indexer_3', trim($indexer3[0])); + $this->assertEquals('Title_indexer3', trim($indexer3[1])); + $this->assertEquals('Processing', trim($indexer3[2])); + $this->assertEquals('Schedule', trim($indexer3[3])); + $this->assertEquals('idle (100 in backlog)', trim($indexer3[4])); + $this->assertEquals('2017-01-01 11:11:11', trim($indexer3[5])); $indexer4 = array_values(array_filter(explode('|', $linesOutput[6]))); - $this->assertEquals('Title_indexer4', trim($indexer4[0])); - $this->assertEquals('unknown', trim($indexer4[1])); - $this->assertEquals('Schedule', trim($indexer4[2])); - $this->assertEquals('running (20 in backlog)', trim($indexer4[3])); - $this->assertEquals('2017-01-01 11:11:11', trim($indexer4[4])); + $this->assertEquals('indexer_4', trim($indexer4[0])); + $this->assertEquals('Title_indexer4', trim($indexer4[1])); + $this->assertEquals('unknown', trim($indexer4[2])); + $this->assertEquals('Schedule', trim($indexer4[3])); + $this->assertEquals('running (20 in backlog)', trim($indexer4[4])); + $this->assertEquals('2017-01-01 11:11:11', trim($indexer4[5])); } /** diff --git a/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php b/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php index ca2da9585f934..a43ea2b55514c 100644 --- a/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Model/IndexerTest.php @@ -154,15 +154,7 @@ public function testGetLatestUpdated($getViewIsEnabled, $getViewGetUpdated, $get $this->stateFactoryMock->expects($this->once())->method('create')->will($this->returnValue($stateMock)); if ($getViewIsEnabled && $getViewGetUpdated) { - if (!$getStateGetUpdated) { - $this->assertEquals($getViewGetUpdated, $this->model->getLatestUpdated()); - } else { - if ($getViewGetUpdated == $getStateGetUpdated) { - $this->assertEquals($getViewGetUpdated, $this->model->getLatestUpdated()); - } else { - $this->assertEquals($getViewGetUpdated, $this->model->getLatestUpdated()); - } - } + $this->assertEquals($getViewGetUpdated, $this->model->getLatestUpdated()); } else { $getLatestUpdated = $this->model->getLatestUpdated(); $this->assertEquals($getStateGetUpdated, $getLatestUpdated); diff --git a/app/code/Magento/Indexer/registration.php b/app/code/Magento/Indexer/registration.php index dda2240601779..0a5f37e97ac82 100644 --- a/app/code/Magento/Indexer/registration.php +++ b/app/code/Magento/Indexer/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Indexer', __DIR__); diff --git a/app/code/Magento/InstantPurchase/Test/Unit/Block/ButtonTest.php b/app/code/Magento/InstantPurchase/Test/Unit/Block/ButtonTest.php new file mode 100644 index 0000000000000..37d2729cd2a28 --- /dev/null +++ b/app/code/Magento/InstantPurchase/Test/Unit/Block/ButtonTest.php @@ -0,0 +1,138 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\InstantPurchase\Test\Unit\Block; + +use Magento\InstantPurchase\Block\Button; +use Magento\InstantPurchase\Model\Config; +use Magento\Framework\View\Element\Template\Context; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Store\Api\Data\StoreInterface; + +/** + * Test class for button block + * + * Class \Magento\InstantPurchase\Test\Unit\Block\ButtonTest + */ +class ButtonTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var Button | \PHPUnit_Framework_MockObject_MockObject + */ + private $block; + + /** + * @var Config | \PHPUnit_Framework_MockObject_MockObject + */ + private $config; + + /** + * @var StoreManagerInterface | \PHPUnit_Framework_MockObject_MockObject + */ + private $storeManager; + + /** + * @var StoreInterface | \PHPUnit_Framework_MockObject_MockObject + */ + private $store; + + /** + * @var Context | \PHPUnit_Framework_MockObject_MockObject + */ + private $context; + + /** + * Setup environment for testing + */ + protected function setUp() + { + $this->context = $this->createMock(Context::class); + $this->storeManager = $this->createMock(StoreManagerInterface::class); + $this->store = $this->createMock(StoreInterface::class); + + $this->storeManager->expects($this->any())->method('getStore') + ->willReturn($this->store); + + $this->config = $this->createMock(Config::class); + + $this->context->expects($this->any())->method('getStoreManager') + ->willReturn($this->storeManager); + + $this->block = $this->getMockBuilder(Button::class) + ->setConstructorArgs( + [ + 'context' => $this->context, + 'instantPurchaseConfig' => $this->config + ] + ) + ->setMethods(['getUrl']) + ->getMock(); + } + + /** + * Test isEnabled() function + * + * @param $currentStoreId + * @param $isModuleEnabled + * @param $expected + * @dataProvider isEnabledDataProvider + */ + public function testIsEnabled($currentStoreId, $isModuleEnabled, $expected) + { + $this->store->expects($this->any())->method('getId') + ->willReturn($currentStoreId); + + $this->config->expects($this->any())->method('isModuleEnabled') + ->willReturn($isModuleEnabled); + + $this->assertEquals($expected, $this->block->isEnabled()); + } + + /** + * Data Provider for test isEnabled() + * + * @return array + */ + public function isEnabledDataProvider() + { + return [ + 'Store With ID = 1 and enable module' => [ + 1, + true, + true + ], + 'Store With ID = 1 and disable module' => [ + 1, + false, + false + ] + ]; + } + + /** + * Test getJsLayout() function + */ + public function testGetJsLayout() + { + $currentStoreId = 1; + $buttonText = 'Instant Purchased'; + $url = 'https://magento2.com/instantpurchase/button/placeOrder'; + $expected = '{"components":{"instant-purchase":{"config":{"buttonText":"Instant Purchased",' . + '"purchaseUrl":"https:\/\/magento2.com\/instantpurchase\/button\/placeOrder"}}}}'; + + $this->store->expects($this->any())->method('getId') + ->willReturn($currentStoreId); + $this->config->expects($this->any())->method('getButtonText') + ->willReturn($buttonText); + $this->block->expects($this->any())->method('getUrl') + ->with('instantpurchase/button/placeOrder', ['_secure' => true]) + ->willReturn($url); + + $this->assertEquals($expected, $this->block->getJsLayout()); + } +} diff --git a/app/code/Magento/InstantPurchase/Test/Unit/CustomerData/InstantPurchaseTest.php b/app/code/Magento/InstantPurchase/Test/Unit/CustomerData/InstantPurchaseTest.php new file mode 100644 index 0000000000000..c608338fd10c5 --- /dev/null +++ b/app/code/Magento/InstantPurchase/Test/Unit/CustomerData/InstantPurchaseTest.php @@ -0,0 +1,187 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\InstantPurchase\Test\Unit\CustomerData; + +use Magento\InstantPurchase\CustomerData\InstantPurchase as CustomerData; +use Magento\Customer\Model\Session; +use Magento\InstantPurchase\Model\InstantPurchaseInterface as InstantPurchaseModel; +use Magento\InstantPurchase\Model\Ui\CustomerAddressesFormatter; +use Magento\InstantPurchase\Model\Ui\PaymentTokenFormatter; +use Magento\InstantPurchase\Model\Ui\ShippingMethodFormatter; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Store\Model\Store; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\InstantPurchase\Model\InstantPurchaseOption; +use Magento\Customer\Model\Customer; + +/** + * Test class for InstantPurchase Customer Data + * + * Class \Magento\InstantPurchase\Test\Unit\CustomerData\InstantPurchaseTest + */ +class InstantPurchaseTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var objectManagerHelper + */ + private $objectManager; + + /** + * @var CustomerData | \PHPUnit_Framework_MockObject_MockObject + */ + private $customerData; + + /** + * @var Session | \PHPUnit_Framework_MockObject_MockObject + */ + private $customerSession; + + /** + * @var StoreManagerInterface | \PHPUnit_Framework_MockObject_MockObject + */ + private $storeManager; + + /** + * @var InstantPurchaseModel | \PHPUnit_Framework_MockObject_MockObject + */ + private $instantPurchase; + + /** + * @var PaymentTokenFormatter | \PHPUnit_Framework_MockObject_MockObject + */ + private $paymentTokenFormatter; + + /** + * @var CustomerAddressesFormatter | \PHPUnit_Framework_MockObject_MockObject + */ + private $customerAddressesFormatter; + + /** + * @var ShippingMethodFormatter | \PHPUnit_Framework_MockObject_MockObject + */ + private $shippingMethodFormatter; + + /** + * @var Store | \PHPUnit_Framework_MockObject_MockObject + */ + private $store; + + /** + * @var Customer | \PHPUnit_Framework_MockObject_MockObject + */ + private $customer; + + /** + * @var InstantPurchaseOption | \PHPUnit_Framework_MockObject_MockObject + */ + private $instantPurchaseOption; + + /** + * Setup environment for testing + */ + protected function setUp() + { + $this->customerSession = $this->createMock(Session::class); + $this->storeManager = $this->createMock(StoreManagerInterface::class); + $this->instantPurchase = $this->createMock(InstantPurchaseModel::class); + $this->paymentTokenFormatter = $this->createMock(PaymentTokenFormatter::class); + $this->customerAddressesFormatter = $this->createMock(CustomerAddressesFormatter::class); + $this->shippingMethodFormatter = $this->createMock(ShippingMethodFormatter::class); + $this->store = $this->createMock(Store::class); + $this->customer = $this->createMock(Customer::class); + $this->instantPurchaseOption = $this->createMock(InstantPurchaseOption::class); + + $this->objectManager = new ObjectManagerHelper($this); + $this->customerData = $this->objectManager->getObject( + CustomerData::class, + [ + 'customerSession' => $this->customerSession, + 'storeManager' => $this->storeManager, + 'instantPurchase' => $this->instantPurchase, + 'paymentTokenFormatter' => $this->paymentTokenFormatter, + 'customerAddressesFormatter' => $this->customerAddressesFormatter, + 'shippingMethodFormatter' => $this->shippingMethodFormatter + ] + ); + } + + /** + * Test getSectionData() + * + * @param $isLogin + * @param $isAvailable + * @param $expected + * @dataProvider getSectionDataProvider + */ + public function testGetSectionData($isLogin, $isAvailable, $expected) + { + $this->customerSession->expects($this->any())->method('isLoggedIn')->willReturn($isLogin); + + $this->storeManager->expects($this->any())->method('getStore')->willReturn($this->store); + + $this->customerSession->expects($this->any())->method('getCustomer') + ->willReturn($this->customer); + + $this->instantPurchase->expects($this->any())->method('getOption') + ->with($this->store, $this->customer) + ->willReturn($this->instantPurchaseOption); + + $this->instantPurchaseOption->expects($this->any())->method('isAvailable') + ->willReturn($isAvailable); + + $this->assertEquals($expected, $this->customerData->getSectionData()); + } + + /** + * Data Provider for test getSectionData() + * + * @return array + */ + public function getSectionDataProvider() + { + return [ + 'No Login and available instant purchase' => [ + false, + true, + ['available' => false] + ], + + 'Login and no available instant purchase option' => [ + true, + false, + ['available' => false] + ], + + 'Login and available instant purchase option' => [ + true, + true, + [ + 'available' => true, + 'paymentToken' => [ + 'publicHash' => '', + 'summary' => '' + ], + 'shippingAddress' => [ + 'id' => null, + 'summary' => '' + ], + 'billingAddress' => [ + 'id' => null, + 'summary' => '' + ], + 'shippingMethod' => [ + 'carrier' => null, + 'method' => null, + 'summary' => '' + ] + ] + ] + ]; + } +} diff --git a/app/code/Magento/InstantPurchase/Test/Unit/Model/Ui/CustomerAddressesFormatterTest.php b/app/code/Magento/InstantPurchase/Test/Unit/Model/Ui/CustomerAddressesFormatterTest.php new file mode 100644 index 0000000000000..2a53a36a46cd6 --- /dev/null +++ b/app/code/Magento/InstantPurchase/Test/Unit/Model/Ui/CustomerAddressesFormatterTest.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InstantPurchase\Test\Unit\Model\Ui; + +use Magento\InstantPurchase\Model\Ui\CustomerAddressesFormatter; +use Magento\Customer\Model\Address; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Directory\Model\Country; +use PHPUnit\Framework\TestCase; + +class CustomerAddressesFormatterTest extends TestCase +{ + /** + * @var CustomerAddressesFormatter|\PHPUnit_Framework_MockObject_MockObject + */ + private $customerAddressesFormatter; + + /** + * Setup environment for testing + */ + protected function setUp() + { + $objectManager = new ObjectManagerHelper($this); + $this->customerAddressesFormatter = $objectManager->getObject(CustomerAddressesFormatter::class); + } + + /** + * Test format() + */ + public function testFormat() + { + $addressMock = $this->createPartialMock( + Address::class, + ['getName', 'getStreetFull', 'getCity', 'getRegion', 'getPostcode', 'getCountryModel'] + ); + $countryMock = $this->createMock(Country::class); + + $countryMock->expects($this->any())->method('getName')->willReturn('USA'); + $addressMock->expects($this->any())->method('getName')->willReturn('Address Name'); + $addressMock->expects($this->any())->method('getStreetFull')->willReturn('Address Street Full'); + $addressMock->expects($this->any())->method('getCity')->willReturn('Address City'); + $addressMock->expects($this->any())->method('getRegion')->willReturn('California'); + $addressMock->expects($this->any())->method('getPostcode')->willReturn('12345'); + $addressMock->expects($this->any())->method('getCountryModel')->willReturn($countryMock); + + $this->assertEquals( + 'Address Name, Address Street Full, Address City, California 12345, USA', + $this->customerAddressesFormatter->format($addressMock) + ); + } +} diff --git a/app/code/Magento/InstantPurchase/Test/Unit/Model/Ui/ShippingMethodFormatterTest.php b/app/code/Magento/InstantPurchase/Test/Unit/Model/Ui/ShippingMethodFormatterTest.php new file mode 100644 index 0000000000000..632392bcb35e3 --- /dev/null +++ b/app/code/Magento/InstantPurchase/Test/Unit/Model/Ui/ShippingMethodFormatterTest.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InstantPurchase\Test\Unit\Model\Ui; + +use Magento\InstantPurchase\Model\Ui\ShippingMethodFormatter; +use Magento\Quote\Api\Data\ShippingMethodInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use PHPUnit\Framework\TestCase; + +class ShippingMethodFormatterTest extends TestCase +{ + /** + * @var ShippingMethodFormatter|\PHPUnit_Framework_MockObject_MockObject + */ + private $shippingMethodFormatter; + + /** + * Setup environment for testing + */ + protected function setUp() + { + $objectManager = new ObjectManagerHelper($this); + $this->shippingMethodFormatter = $objectManager->getObject(ShippingMethodFormatter::class); + } + + /** + * Test format() + */ + public function testFormat() + { + $shippingMethodMock = $this->createMock(ShippingMethodInterface::class, ['getCarrierTitle', 'getMethodTitle']); + + $shippingMethodMock->expects($this->any())->method('getCarrierTitle')->willReturn('flatrate'); + $shippingMethodMock->expects($this->any())->method('getMethodTitle')->willReturn('flatrate'); + + $this->assertEquals( + 'flatrate - flatrate', + $this->shippingMethodFormatter->format($shippingMethodMock) + ); + } +} diff --git a/app/code/Magento/InstantPurchase/registration.php b/app/code/Magento/InstantPurchase/registration.php index 74b4f3e92cf44..1f2fce8c12c07 100644 --- a/app/code/Magento/InstantPurchase/registration.php +++ b/app/code/Magento/InstantPurchase/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_InstantPurchase', __DIR__); diff --git a/app/code/Magento/Integration/Block/Adminhtml/Integration/Edit/Tab/Info.php b/app/code/Magento/Integration/Block/Adminhtml/Integration/Edit/Tab/Info.php index 4042c2ebde87d..89cad471933e6 100644 --- a/app/code/Magento/Integration/Block/Adminhtml/Integration/Edit/Tab/Info.php +++ b/app/code/Magento/Integration/Block/Adminhtml/Integration/Edit/Tab/Info.php @@ -179,7 +179,7 @@ protected function _addGeneralFieldset($form, $integrationData) 'label' => __('Your Password'), 'id' => self::DATA_CONSUMER_PASSWORD, 'title' => __('Your Password'), - 'class' => 'input-text validate-current-password required-entry', + 'class' => 'validate-current-password required-entry', 'required' => true ] ); diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.xml new file mode 100644 index 0000000000000..c039a70293269 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminCreatesNewIntegrationActionGroup.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"> + + <!--Fill Required Fields --> + <actionGroup name="AdminCreatesNewIntegrationActionGroup"> + <arguments> + <argument name="name" type="string"/> + <argument name="password" type="string"/> + </arguments> + <fillField stepKey="fillNameField" selector="{{AdminNewIntegrationSection.name}}" userInput="{{name}}"/> + <fillField stepKey="fillAdminPasswordField" selector="{{AdminNewIntegrationSection.password}}" userInput="{{password}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminDeleteIntegrationEntityActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminDeleteIntegrationEntityActionGroup.xml new file mode 100644 index 0000000000000..4a73f6ce4b8df --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminDeleteIntegrationEntityActionGroup.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="AdminDeleteIntegrationEntityActionGroup"> + <click stepKey="clickRemoveButon" selector="{{AdminIntegrationsGridSection.remove}}"/> + <waitForElementVisible selector="{{AdminIntegrationsGridSection.submitButton}}" stepKey="waitForConfirmButtonVisible"/> + <click stepKey="clickSubmitButton" selector="{{AdminIntegrationsGridSection.submitButton}}"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminNavigateToCreateIntegrationPageActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminNavigateToCreateIntegrationPageActionGroup.xml new file mode 100644 index 0000000000000..18deddb3170aa --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminNavigateToCreateIntegrationPageActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + + <!--Click the "Add New Integration" Button --> + + <actionGroup name="AdminNavigateToCreateIntegrationPageActionGroup"> + <click stepKey="clickAddNewIntegrationButton" selector="{{AdminIntegrationsGridSection.add}}"/> + <waitForPageLoad stepKey="waitForNewNIntegrationPageLoaded"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminSearchIntegrationInGridActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminSearchIntegrationInGridActionGroup.xml new file mode 100644 index 0000000000000..dcd60a0479db4 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminSearchIntegrationInGridActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSearchIntegrationInGridActionGroup"> + <arguments> + <argument name="name" type="string"/> + </arguments> + <!--Reset Search Filters --> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <!--Fill Integration Name Field --> + <fillField selector="{{AdminIntegrationsGridSection.name}}" userInput="{{name}}" stepKey="filterByName"/> + <!--Click "Search" Button --> + <click selector="{{AdminIntegrationsGridSection.search}}" stepKey="doFilter"/> + <waitForPageLoad stepKey="waitForSitemapPageLoadedAfterFiltering"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminSubmitNewIntegrationFormActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminSubmitNewIntegrationFormActionGroup.xml new file mode 100644 index 0000000000000..f1dcd5da77c85 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AdminSubmitNewIntegrationFormActionGroup.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="AdminSubmitNewIntegrationFormActionGroup"> + <!--Click the "Save" Button --> + <click stepKey="clickSaveButton" selector="{{AdminNewIntegrationSection.saveButton}}"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertAdminMessageCreateIntegrationEntityActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertAdminMessageCreateIntegrationEntityActionGroup.xml new file mode 100644 index 0000000000000..f233c1d9a7f74 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertAdminMessageCreateIntegrationEntityActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertAdminMessageCreateIntegrationEntityActionGroup"> + <arguments> + <argument name="message" type="string" defaultValue="The integration '{{name}}' has been saved."/> + <argument name="messageType" type="string" defaultValue="success"/> + </arguments> + <waitForElementVisible selector="{{AdminIntegrationsGridSection.messageByType(messageType)}}" stepKey="waitForMessage"/> + <see userInput="{{message}}" selector="{{AdminIntegrationsGridSection.messageByType(messageType)}}" stepKey="verifyMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertDeletedIntegrationIsNotInGridActionGroup.xml b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertDeletedIntegrationIsNotInGridActionGroup.xml new file mode 100644 index 0000000000000..a4438b1eb70a7 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/ActionGroup/AssertDeletedIntegrationIsNotInGridActionGroup.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="AssertDeletedIntegrationIsNotInGridActionGroup"> + <arguments> + <argument name="name" type="string"/> + </arguments> + <dontSee userInput="{{name}}" selector="{{AdminIntegrationsGridSection.rowByIndex('1')}}" stepKey="donSeeIntegration"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsGridSection.xml b/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsGridSection.xml new file mode 100644 index 0000000000000..ae601542f3b37 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/Section/AdminIntegrationsGridSection.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + + <section name="AdminIntegrationsGridSection"> + <element name="add" type="button" selector=".page-actions .add"/> + <element name="messageByType" type="block" selector="#messages .message-{{messageType}}" parameterized="true"/> + <element name="name" type="input" selector=".data-grid-filters #integrationGrid_filter_name"/> + <element name="search" type="input" selector=".admin__filter-actions button[title=Search]"/> + <element name="remove" type="button" selector=".data-grid .delete"/> + <element name="submitButton" type="button" selector=".action-primary.action-accept" timeout="30"/> + <element name="rowByIndex" type="text" selector="tr[data-role='row']:nth-of-type({{var1}})" parameterized="true" timeout="30"/> + <element name="edit" type="button" selector=".data-grid .edit"/> + </section> +</sections> diff --git a/app/code/Magento/Integration/Test/Mftf/Section/AdminNewIntegrationSection.xml b/app/code/Magento/Integration/Test/Mftf/Section/AdminNewIntegrationSection.xml new file mode 100644 index 0000000000000..3e7214784c2b5 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/Section/AdminNewIntegrationSection.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminNewIntegrationSection"> + <element name="name" type="input" selector="#integration_properties_name"/> + <element name="password" type="input" selector="#integration_properties_current_password"/> + <element name="saveButton" type="button" selector=".page-actions #save-split-button-button"/> + <element name="endpoint" type="input" selector="#integration_properties_endpoint"/> + <element name="linkUrl" type="input" selector="#integration_properties_identity_link_url"/> + <element name="save" type="button" selector=".page-actions-buttons .save"/> + </section> +</sections> diff --git a/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml b/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml new file mode 100644 index 0000000000000..6f46bbf99d218 --- /dev/null +++ b/app/code/Magento/Integration/Test/Mftf/Test/AdminDeleteIntegrationEntityTest.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteIntegrationEntityTest"> + <annotations> + <features value="Integration"/> + <stories value="System Integration"/> + <title value="Admin system integration"/> + <description value="Admin Deletes Created Integration"/> + <group value="integration"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Login As Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <!-- Navigate To Integrations Page --> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToIntegrationsPage"> + <argument name="menuUiId" value="{{AdminMenuSystem.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuSystemExtensionsIntegrations.dataUiId}}"/> + </actionGroup> + <!-- Click the "Add New Integration" button --> + <actionGroup ref="AdminNavigateToCreateIntegrationPageActionGroup" stepKey="clickAddNewIntegrationButton"/> + <!-- Create New Integration --> + <actionGroup ref="AdminCreatesNewIntegrationActionGroup" stepKey="createIntegration"> + <argument name="name" value="Integration1"/> + <argument name="password" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> + </actionGroup> + <!-- Submit The Form --> + <actionGroup ref="AdminSubmitNewIntegrationFormActionGroup" stepKey="submitTheForm"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- TEST BODY --> + <!-- Find Created Integration In Grid --> + <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="findCreatedIntegration"> + <argument name="name" value="Integration1"/> + </actionGroup> + <!-- Delete Created Integration Entity --> + <actionGroup ref="AdminDeleteIntegrationEntityActionGroup" stepKey="deleteIntegration"/> + <!-- Assert Success Message --> + <actionGroup ref="AssertAdminMessageCreateIntegrationEntityActionGroup" stepKey="seeSuccessMessage"> + <argument name="message" value="The integration 'Integration1' has been deleted."/> + <argument value="success" name="messageType"/> + </actionGroup> + <!-- Assert Deleted Integration Is Not In Grid --> + <actionGroup ref="AdminSearchIntegrationInGridActionGroup" stepKey="findDeletedIntegration"> + <argument name="name" value="Integration1"/> + </actionGroup> + <actionGroup ref="AssertDeletedIntegrationIsNotInGridActionGroup" stepKey="dontSeeIntegration"> + <argument name="name" value="Integration1"/> + </actionGroup> + <!-- END TEST BODY --> + </test> +</tests> diff --git a/app/code/Magento/Integration/registration.php b/app/code/Magento/Integration/registration.php index d73b6ae57eeb5..6d0fea09e2a40 100644 --- a/app/code/Magento/Integration/registration.php +++ b/app/code/Magento/Integration/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Integration', __DIR__); diff --git a/app/code/Magento/Integration/view/adminhtml/templates/resourcetree.phtml b/app/code/Magento/Integration/view/adminhtml/templates/resourcetree.phtml index 7d6c27954d836..1737f66ce4a1b 100644 --- a/app/code/Magento/Integration/view/adminhtml/templates/resourcetree.phtml +++ b/app/code/Magento/Integration/view/adminhtml/templates/resourcetree.phtml @@ -38,13 +38,13 @@ <label class="label"><span><?= $block->escapeHtml(__('Resources')) ?></span></label> <div class="control"> - <div class="tree x-tree" data-role="resource-tree" data-mage-init='<?= /* @noEscape */ - $block->getJsonSerializer()->serialize([ + <div class="tree x-tree" data-role="resource-tree" data-mage-init='<?= + $block->escapeHtmlAttr($block->getJsonSerializer()->serialize([ 'rolesTree' => [ "treeInitData" => $block->getTree(), "treeInitSelectedData" => $block->getSelectedResources(), ], - ]); ?>'> + ])); ?>'> </div> </div> </div> diff --git a/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection.xml b/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection.xml index 1e4137beacd88..b3e0c430b12e7 100644 --- a/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection.xml +++ b/app/code/Magento/LayeredNavigation/Test/Mftf/Section/LayeredNavigationSection.xml @@ -9,6 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="LayeredNavigationSection"> + <element name="filterOptionContent" type="text" selector="//div[contains(text(), '{{attribute}}')]//following-sibling::div//a[contains(text(), '{{option}}')]" parameterized="true"/> <element name="layeredNavigation" type="select" selector="#catalog_layered_navigation-head"/> <element name="layeredNavigationBlock" type="block" selector="#catalog_layered_navigation"/> <element name="CheckIfTabExpand" type="button" selector="#catalog_layered_navigation-head:not(.open)"/> diff --git a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml new file mode 100644 index 0000000000000..7b78b5193ef7c --- /dev/null +++ b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.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="AdminCheckResultsOfColorAndOtherFiltersTest"> + <!-- Open a category on storefront --> + <actionGroup ref="StorefrontGoToCategoryPageActionGroup" after="flushCache" stepKey="goToCategoryPage"> + <argument name="categoryName" value="$$createCategory.name$$"/> + </actionGroup> + <!-- Choose First attribute filter --> + <waitForElementVisible selector="{{StorefrontCategorySidebarSection.filterOptionTitle('$$createConfigProductAttribute.default_frontend_label$$')}}" stepKey="waitForCartRuleButton"/> + <click selector="{{StorefrontCategorySidebarSection.filterOptionTitle('$$createConfigProductAttribute.default_frontend_label$$')}}" stepKey="expandFirstAttribute"/> + <waitForPageLoad stepKey="waitForFilterLoad"/> + <click selector="{{LayeredNavigationSection.filterOptionContent('$$createConfigProductAttribute.default_frontend_label$$','option2')}}" stepKey="expandFirstAttributeOption"/> + <waitForPageLoad stepKey="waitForAttributeOption"/> + <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo($$createFirstConfigurableProduct.name$$)}}" stepKey="seeFirstProduct"/> + <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo($$createSecondConfigurableProduct.name$$)}}" stepKey="seeSecondProduct"/> + <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo($$createThirdConfigurableProduct.name$$)}}" stepKey="seeSimpleProduct"/> + <actionGroup ref="StorefrontGoToCategoryPageActionGroup" stepKey="goToCategoryPageAgain"> + <argument name="categoryName" value="$$createCategory.name$$"/> + </actionGroup> + <!-- Choose Second attribute filter --> + <click selector="{{StorefrontCategorySidebarSection.filterOptionTitle('$$createConfigProductAttribute2.default_frontend_label$$')}}" stepKey="expandSecondAttributeOption"/> + <waitForPageLoad stepKey="waitForFilterPageLoad"/> + <click selector="{{LayeredNavigationSection.filterOptionContent('$$createConfigProductAttribute2.default_frontend_label$$','option1')}}" stepKey="expandSecondAttribute"/> + <waitForPageLoad stepKey="waitForProductListLoad"/> + <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo($$createFirstConfigurableProduct.name$$)}}" stepKey="seeFourthProduct"/> + <seeElement selector="{{StorefrontCategoryMainSection.specifiedProductItemInfo($$createSecondConfigurableProduct.name$$)}}" stepKey="seeFifthProduct"/> + </test> +</tests> diff --git a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobile.xml b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobile.xml index 6d182d0b7a5e2..d2b462d0467a2 100644 --- a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobile.xml +++ b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobile.xml @@ -47,7 +47,7 @@ <comment userInput="Go to simple product edit page and set the product attribute to a value" stepKey="commentProductAttributeEdit" /> <amOnPage url="{{AdminProductEditPage.url($$simpleProduct1.id$$)}}" stepKey="goToEditPage"/> <selectOption selector="{{AdminProductFormSection.customSelectField($$attribute.attribute[attribute_code]$$)}}" userInput="option1" stepKey="selectAttribute"/> - <actionGroup ref="saveProductForm" stepKey="saveSimpleProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSimpleProduct"/> <!-- Check storefront mobile view for shop by button is functioning as expected --> <comment userInput="Check storefront mobile view for shop by button is functioning as expected" stepKey="commentCheckShopByButton" /> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePage"/> diff --git a/app/code/Magento/LayeredNavigation/Test/Unit/Observer/Grid/ProductAttributeGridBuildObserverTest.php b/app/code/Magento/LayeredNavigation/Test/Unit/Observer/Grid/ProductAttributeGridBuildObserverTest.php new file mode 100644 index 0000000000000..f21908d11ad44 --- /dev/null +++ b/app/code/Magento/LayeredNavigation/Test/Unit/Observer/Grid/ProductAttributeGridBuildObserverTest.php @@ -0,0 +1,102 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\LayeredNavigation\Test\Unit\Observer\Grid; + +use Magento\Catalog\Block\Adminhtml\Product\Attribute\Grid; +use Magento\Framework\Event\Observer; +use Magento\Framework\Module\Manager; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\LayeredNavigation\Observer\Grid\ProductAttributeGridBuildObserver; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Class ProductAttributeGridBuildObserverTest + * + * Testing adding new grid column for Layered Navigation + */ +class ProductAttributeGridBuildObserverTest extends TestCase +{ + /** + * @var ProductAttributeGridBuildObserver + */ + private $observer; + + /** + * @var Manager|MockObject + */ + private $moduleManagerMock; + + /** + * @var Observer|MockObject + */ + private $observerMock; + + /** + * @var Grid|MockObject + */ + private $gridMock; + + /** + * Set Up + */ + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->moduleManagerMock = $this->createMock(Manager::class); + $this->gridMock = $this->createMock(Grid::class); + $this->observerMock = $this->getMockBuilder(Observer::class) + ->disableOriginalConstructor() + ->setMethods(['getGrid']) + ->getMock(); + + $this->observer = $objectManager->getObject( + ProductAttributeGridBuildObserver::class, + [ + 'moduleManager' => $this->moduleManagerMock, + ] + ); + } + + /** + * Testing the column adding if the output is not enabled + */ + public function testColumnAddingOnDisabledOutput() + { + $enabledOutput = false; + + $this->moduleManagerMock->expects($this->once()) + ->method('isOutputEnabled') + ->with('Magento_LayeredNavigation') + ->willReturn($enabledOutput); + + $this->observerMock->expects($this->never()) + ->method('getGrid'); + + $this->observer->execute($this->observerMock); + } + + /** + * Testing the column adding if the output is enabled + */ + public function testColumnAddingOnEnabledOutput() + { + $enabledOutput = true; + + $this->moduleManagerMock->expects($this->once()) + ->method('isOutputEnabled') + ->with('Magento_LayeredNavigation') + ->willReturn($enabledOutput); + + $this->observerMock->expects($this->once()) + ->method('getGrid') + ->willReturn($this->gridMock); + + $this->observer->execute($this->observerMock); + } +} diff --git a/app/code/Magento/LayeredNavigation/registration.php b/app/code/Magento/LayeredNavigation/registration.php index 7c4f8dff9c920..9df0a63ae945f 100644 --- a/app/code/Magento/LayeredNavigation/registration.php +++ b/app/code/Magento/LayeredNavigation/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_LayeredNavigation', __DIR__); diff --git a/app/code/Magento/Marketplace/registration.php b/app/code/Magento/Marketplace/registration.php index 7d4bbca27f15f..6ccf343d76427 100644 --- a/app/code/Magento/Marketplace/registration.php +++ b/app/code/Magento/Marketplace/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Marketplace', __DIR__); diff --git a/app/code/Magento/MediaGallery/LICENSE.txt b/app/code/Magento/MediaGallery/LICENSE.txt new file mode 100644 index 0000000000000..49525fd99da9c --- /dev/null +++ b/app/code/Magento/MediaGallery/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/MediaGallery/LICENSE_AFL.txt b/app/code/Magento/MediaGallery/LICENSE_AFL.txt new file mode 100644 index 0000000000000..f39d641b18a19 --- /dev/null +++ b/app/code/Magento/MediaGallery/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/MediaGallery/Model/Asset.php b/app/code/Magento/MediaGallery/Model/Asset.php new file mode 100644 index 0000000000000..f6e1c00044a79 --- /dev/null +++ b/app/code/Magento/MediaGallery/Model/Asset.php @@ -0,0 +1,123 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\MediaGallery\Model; + +use Magento\MediaGalleryApi\Api\Data\AssetExtensionInterface; +use Magento\MediaGalleryApi\Api\Data\AssetInterface; +use Magento\Framework\Model\AbstractExtensibleModel; + +/** + * Media Gallery Asset + */ +class Asset extends AbstractExtensibleModel implements AssetInterface +{ + private const ID = 'id'; + private const PATH = 'path'; + private const TITLE = 'title'; + private const SOURCE = 'source'; + private const CONTENT_TYPE = 'content_type'; + private const WIDTH = 'width'; + private const HEIGHT = 'height'; + private const CREATED_AT = 'created_at'; + private const UPDATED_AT = 'updated_at'; + + /** + * @inheritdoc + */ + public function getId(): ?int + { + $id = $this->getData(self::ID); + + if (!$id) { + return null; + } + + return (int) $id; + } + + /** + * @inheritdoc + */ + public function getPath(): string + { + return (string) $this->getData(self::PATH); + } + + /** + * @inheritdoc + */ + public function getTitle(): ?string + { + return $this->getData(self::TITLE); + } + + /** + * @inheritdoc + */ + public function getSource(): ?string + { + return $this->getData(self::SOURCE); + } + + /** + * @inheritdoc + */ + public function getContentType(): string + { + return (string) $this->getData(self::CONTENT_TYPE); + } + + /** + * @inheritdoc + */ + public function getWidth(): int + { + return (int) $this->getData(self::WIDTH); + } + + /** + * @inheritdoc + */ + public function getHeight(): int + { + return (int) $this->getData(self::HEIGHT); + } + + /** + * @inheritdoc + */ + public function getCreatedAt(): string + { + return (string) $this->getData(self::CREATED_AT); + } + + /** + * @inheritdoc + */ + public function getUpdatedAt(): string + { + return (string) $this->getData(self::UPDATED_AT); + } + + /** + * @inheritdoc + */ + public function getExtensionAttributes(): AssetExtensionInterface + { + return $this->_getExtensionAttributes(); + } + + /** + * @inheritdoc + */ + public function setExtensionAttributes(AssetExtensionInterface $extensionAttributes): void + { + $this->_setExtensionAttributes($extensionAttributes); + } +} diff --git a/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByPath.php b/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByPath.php new file mode 100644 index 0000000000000..c05a08149bfca --- /dev/null +++ b/app/code/Magento/MediaGallery/Model/Asset/Command/DeleteByPath.php @@ -0,0 +1,73 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Model\Asset\Command; + +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Exception\CouldNotDeleteException; +use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByPathInterface; +use Psr\Log\LoggerInterface; + +/** + * Class DeleteByPath + */ +class DeleteByPath implements DeleteByPathInterface +{ + private const TABLE_MEDIA_GALLERY_ASSET = 'media_gallery_asset'; + + private const MEDIA_GALLERY_ASSET_PATH = 'path'; + + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * DeleteById constructor. + * + * @param ResourceConnection $resourceConnection + * @param LoggerInterface $logger + */ + public function __construct( + ResourceConnection $resourceConnection, + LoggerInterface $logger + ) { + $this->resourceConnection = $resourceConnection; + $this->logger = $logger; + } + + /** + * Delete media asset by path + * + * @param string $mediaAssetPath + * + * @return void + * @throws CouldNotDeleteException + */ + public function execute(string $mediaAssetPath): void + { + try { + /** @var AdapterInterface $connection */ + $connection = $this->resourceConnection->getConnection(); + $tableName = $this->resourceConnection->getTableName(self::TABLE_MEDIA_GALLERY_ASSET); + $connection->delete($tableName, [self::MEDIA_GALLERY_ASSET_PATH . ' = ?' => $mediaAssetPath]); + } catch (\Exception $exception) { + $this->logger->critical($exception); + $message = __( + 'Could not delete media asset with path %path: %error', + ['path' => $mediaAssetPath, 'error' => $exception->getMessage()] + ); + throw new CouldNotDeleteException($message, $exception); + } + } +} diff --git a/app/code/Magento/MediaGallery/Model/Asset/Command/GetById.php b/app/code/Magento/MediaGallery/Model/Asset/Command/GetById.php new file mode 100644 index 0000000000000..6be11610ac197 --- /dev/null +++ b/app/code/Magento/MediaGallery/Model/Asset/Command/GetById.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Model\Asset\Command; + +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Exception\IntegrationException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\MediaGalleryApi\Api\Data\AssetInterface; +use Magento\MediaGalleryApi\Api\Data\AssetInterfaceFactory; +use Magento\MediaGalleryApi\Model\Asset\Command\GetByIdInterface; +use Psr\Log\LoggerInterface; + +/** + * Class GetById + */ +class GetById implements GetByIdInterface +{ + private const TABLE_MEDIA_GALLERY_ASSET = 'media_gallery_asset'; + + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @var AssetInterface + */ + private $assetFactory; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * GetById constructor. + * + * @param ResourceConnection $resourceConnection + * @param AssetInterfaceFactory $assetFactory + * @param LoggerInterface $logger + */ + public function __construct( + ResourceConnection $resourceConnection, + AssetInterfaceFactory $assetFactory, + LoggerInterface $logger + ) { + $this->resourceConnection = $resourceConnection; + $this->assetFactory = $assetFactory; + $this->logger = $logger; + } + + /** + * Get media asset. + * + * @param int $mediaAssetId + * + * @return AssetInterface + * @throws NoSuchEntityException + * @throws IntegrationException + */ + public function execute(int $mediaAssetId): AssetInterface + { + try { + $mediaAssetTable = $this->resourceConnection->getTableName(self::TABLE_MEDIA_GALLERY_ASSET); + $connection = $this->resourceConnection->getConnection(); + $select = $connection->select() + ->from(['amg' => $mediaAssetTable]) + ->where('amg.id = ?', $mediaAssetId); + $mediaAssetData = $connection->query($select)->fetch(); + } catch (\Exception $exception) { + $this->logger->critical($exception); + $message = __( + 'En error occurred during get media asset data by id %id: %error', + ['id' => $mediaAssetId, 'error' => $exception->getMessage()] + ); + throw new IntegrationException($message, $exception); + } + + if (empty($mediaAssetData)) { + $message = __('There is no such media asset with id %id', ['id' => $mediaAssetId]); + throw new NoSuchEntityException($message); + } + + try { + return $this->assetFactory->create(['data' => $mediaAssetData]); + } catch (\Exception $exception) { + $this->logger->critical($exception); + $message = __( + 'En error occurred during initialize media asset with id %id: %error', + ['id' => $mediaAssetId, 'error' => $exception->getMessage()] + ); + throw new IntegrationException($message, $exception); + } + } +} diff --git a/app/code/Magento/MediaGallery/Model/Asset/Command/GetByPath.php b/app/code/Magento/MediaGallery/Model/Asset/Command/GetByPath.php new file mode 100644 index 0000000000000..db8482d3399ba --- /dev/null +++ b/app/code/Magento/MediaGallery/Model/Asset/Command/GetByPath.php @@ -0,0 +1,90 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Model\Asset\Command; + +use Magento\MediaGalleryApi\Api\Data\AssetInterface; +use Magento\MediaGalleryApi\Api\Data\AssetInterfaceFactory; +use Magento\MediaGalleryApi\Model\Asset\Command\GetByPathInterface; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Exception\IntegrationException; +use Magento\Framework\Exception\NoSuchEntityException; +use Psr\Log\LoggerInterface; + +/** + * Class GetListByIds + */ +class GetByPath implements GetByPathInterface +{ + private const TABLE_MEDIA_GALLERY_ASSET = 'media_gallery_asset'; + + private const MEDIA_GALLERY_ASSET_PATH = 'path'; + + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @var AssetInterface + */ + private $mediaAssetFactory; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * GetByPath constructor. + * + * @param ResourceConnection $resourceConnection + * @param AssetInterfaceFactory $mediaAssetFactory + * @param LoggerInterface $logger + */ + public function __construct( + ResourceConnection $resourceConnection, + AssetInterfaceFactory $mediaAssetFactory, + LoggerInterface $logger + ) { + $this->resourceConnection = $resourceConnection; + $this->mediaAssetFactory = $mediaAssetFactory; + $this->logger = $logger; + } + + /** + * Return media asset asset list + * + * @param string $mediaFilePath + * + * @return AssetInterface + * @throws IntegrationException + */ + public function execute(string $mediaFilePath): AssetInterface + { + try { + $connection = $this->resourceConnection->getConnection(); + $select = $connection->select() + ->from($this->resourceConnection->getTableName(self::TABLE_MEDIA_GALLERY_ASSET)) + ->where(self::MEDIA_GALLERY_ASSET_PATH . ' = ?', $mediaFilePath); + $data = $connection->query($select)->fetch(); + + if (empty($data)) { + $message = __('There is no such media asset with path "%1"', $mediaFilePath); + throw new NoSuchEntityException($message); + } + + $mediaAssets = $this->mediaAssetFactory->create(['data' => $data]); + + return $mediaAssets; + } catch (\Exception $exception) { + $this->logger->critical($exception); + $message = __('An error occurred during get media asset list: %1', $exception->getMessage()); + throw new IntegrationException($message, $exception); + } + } +} diff --git a/app/code/Magento/MediaGallery/Model/Asset/Command/Save.php b/app/code/Magento/MediaGallery/Model/Asset/Command/Save.php new file mode 100644 index 0000000000000..7cb2f73169642 --- /dev/null +++ b/app/code/Magento/MediaGallery/Model/Asset/Command/Save.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Model\Asset\Command; + +use Magento\MediaGalleryApi\Model\DataExtractorInterface; +use Magento\MediaGalleryApi\Api\Data\AssetInterface; +use Magento\MediaGalleryApi\Model\Asset\Command\SaveInterface; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Exception\CouldNotSaveException; +use Psr\Log\LoggerInterface; + +/** + * Class Save + */ +class Save implements SaveInterface +{ + private const TABLE_MEDIA_GALLERY_ASSET = 'media_gallery_asset'; + + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @var DataExtractorInterface + */ + private $extractor; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * Save constructor. + * + * @param ResourceConnection $resourceConnection + * @param DataExtractorInterface $extractor + * @param LoggerInterface $logger + */ + public function __construct( + ResourceConnection $resourceConnection, + DataExtractorInterface $extractor, + LoggerInterface $logger + ) { + $this->resourceConnection = $resourceConnection; + $this->extractor = $extractor; + $this->logger = $logger; + } + + /** + * Save media assets + * + * @param AssetInterface $mediaAsset + * + * @return int + * @throws CouldNotSaveException + */ + public function execute(AssetInterface $mediaAsset): int + { + try { + /** @var \Magento\Framework\DB\Adapter\Pdo\Mysql $connection */ + $connection = $this->resourceConnection->getConnection(); + $tableName = $this->resourceConnection->getTableName(self::TABLE_MEDIA_GALLERY_ASSET); + + $connection->insertOnDuplicate($tableName, $this->extractor->extract($mediaAsset, AssetInterface::class)); + return (int) $connection->lastInsertId($tableName); + } catch (\Exception $exception) { + $this->logger->critical($exception); + $message = __('An error occurred during media asset save: %1', $exception->getMessage()); + throw new CouldNotSaveException($message, $exception); + } + } +} diff --git a/app/code/Magento/MediaGallery/Model/DataExtractor.php b/app/code/Magento/MediaGallery/Model/DataExtractor.php new file mode 100644 index 0000000000000..92cf237022c28 --- /dev/null +++ b/app/code/Magento/MediaGallery/Model/DataExtractor.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Model; + +use Magento\MediaGalleryApi\Model\DataExtractorInterface; + +/** + * Extract data from an object using available getters + */ +class DataExtractor implements DataExtractorInterface +{ + /** + * Extract data from an object using available getters (does not process extension attributes) + * + * @param object $object + * @param string|null $interface + * + * @return array + * @throws \ReflectionException + */ + public function extract($object, string $interface = null): array + { + $data = []; + + $reflectionClass = new \ReflectionClass($interface ?? $object); + + foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { + $methodName = $method->getName(); + if (strpos($methodName, 'get') !== 0 + || !empty($method->getParameters()) + || strpos($methodName, 'getExtensionAttributes') !== false + ) { + continue; + } + $value = $object->$methodName(); + if (!empty($value)) { + $key = strtolower(preg_replace("/([a-z])([A-Z])/", "$1_$2", substr($methodName, 3))); + $data[$key] = $value; + } + } + return $data; + } +} diff --git a/app/code/Magento/MediaGallery/Model/Keyword.php b/app/code/Magento/MediaGallery/Model/Keyword.php new file mode 100644 index 0000000000000..c5c60d3152846 --- /dev/null +++ b/app/code/Magento/MediaGallery/Model/Keyword.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\MediaGallery\Model; + +use Magento\MediaGalleryApi\Api\Data\KeywordExtensionInterface; +use Magento\MediaGalleryApi\Api\Data\KeywordInterface; +use Magento\Framework\Model\AbstractExtensibleModel; + +/** + * Asset's Keyword + */ +class Keyword extends AbstractExtensibleModel implements KeywordInterface +{ + private const ID = 'id'; + + private const KEYWORD = 'keyword'; + + /** + * @inheritdoc + */ + public function getId(): ?int + { + $id = $this->getData(self::ID); + + if (!$id) { + return null; + } + + return (int) $id; + } + + /** + * @inheritdoc + */ + public function getKeyword(): string + { + return (string)$this->getData(self::KEYWORD); + } + + /** + * @inheritdoc + */ + public function getExtensionAttributes(): KeywordExtensionInterface + { + return $this->_getExtensionAttributes(); + } + + /** + * @inheritdoc + */ + public function setExtensionAttributes(KeywordExtensionInterface $extensionAttributes): void + { + $this->_setExtensionAttributes($extensionAttributes); + } +} diff --git a/app/code/Magento/MediaGallery/Model/Keyword/Command/GetAssetKeywords.php b/app/code/Magento/MediaGallery/Model/Keyword/Command/GetAssetKeywords.php new file mode 100644 index 0000000000000..5b826a26e937d --- /dev/null +++ b/app/code/Magento/MediaGallery/Model/Keyword/Command/GetAssetKeywords.php @@ -0,0 +1,88 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Model\Keyword\Command; + +use Magento\Framework\Exception\IntegrationException; +use Magento\MediaGalleryApi\Api\Data\KeywordInterface; +use Magento\MediaGalleryApi\Api\Data\KeywordInterfaceFactory; +use Magento\MediaGalleryApi\Model\Keyword\Command\GetAssetKeywordsInterface; +use Magento\Framework\App\ResourceConnection; +use Psr\Log\LoggerInterface; + +/** + * ClassGetAssetKeywords + */ +class GetAssetKeywords implements GetAssetKeywordsInterface +{ + private const TABLE_KEYWORD = 'media_gallery_keyword'; + private const TABLE_ASSET_KEYWORD = 'media_gallery_asset_keyword'; + + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @var KeywordInterfaceFactory + */ + private $assetKeywordFactory; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * GetAssetKeywords constructor. + * + * @param ResourceConnection $resourceConnection + * @param KeywordInterfaceFactory $assetKeywordFactory + * @param LoggerInterface $logger + */ + public function __construct( + ResourceConnection $resourceConnection, + KeywordInterfaceFactory $assetKeywordFactory, + LoggerInterface $logger + ) { + $this->resourceConnection = $resourceConnection; + $this->assetKeywordFactory = $assetKeywordFactory; + $this->logger = $logger; + } + + /** + * Get asset related keywords. + * + * @param int $assetId + * + * @return KeywordInterface[]|[] + * @throws IntegrationException + */ + public function execute(int $assetId): array + { + try { + $connection = $this->resourceConnection->getConnection(); + + $select = $connection->select() + ->from(['k' => $this->resourceConnection->getTableName(self::TABLE_KEYWORD)]) + ->join(['ak' => self::TABLE_ASSET_KEYWORD], 'k.id = ak.keyword_id') + ->where('ak.asset_id = ?', $assetId); + $data = $connection->query($select)->fetchAll(); + + $keywords = []; + foreach ($data as $keywordData) { + $keywords[] = $this->assetKeywordFactory->create(['data' => $keywordData]); + } + + return $keywords; + } catch (\Exception $exception) { + $this->logger->critical($exception); + $message = __('An error occurred during get asset keywords: %1', $exception->getMessage()); + throw new IntegrationException($message, $exception); + } + } +} diff --git a/app/code/Magento/MediaGallery/Model/Keyword/Command/SaveAssetKeywords.php b/app/code/Magento/MediaGallery/Model/Keyword/Command/SaveAssetKeywords.php new file mode 100644 index 0000000000000..b355a9a651cd4 --- /dev/null +++ b/app/code/Magento/MediaGallery/Model/Keyword/Command/SaveAssetKeywords.php @@ -0,0 +1,111 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Model\Keyword\Command; + +use Magento\MediaGalleryApi\Api\Data\KeywordInterface; +use Magento\MediaGalleryApi\Model\Keyword\Command\SaveAssetKeywordsInterface; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Adapter\Pdo\Mysql; +use Magento\Framework\Exception\CouldNotSaveException; +use Psr\Log\LoggerInterface; + +/** + * Class SaveAssetKeywords + */ +class SaveAssetKeywords implements SaveAssetKeywordsInterface +{ + private const TABLE_KEYWORD = 'media_gallery_keyword'; + private const ID = 'id'; + private const KEYWORD = 'keyword'; + + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @var SaveAssetLinks + */ + private $saveAssetLinks; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * SaveAssetKeywords constructor. + * + * @param ResourceConnection $resourceConnection + * @param SaveAssetLinks $saveAssetLinks + * @param LoggerInterface $logger + */ + public function __construct( + ResourceConnection $resourceConnection, + SaveAssetLinks $saveAssetLinks, + LoggerInterface $logger + ) { + $this->resourceConnection = $resourceConnection; + $this->saveAssetLinks = $saveAssetLinks; + $this->logger = $logger; + } + + /** + * Save asset keywords. + * + * @param KeywordInterface[] $keywords + * @param int $assetId + * @throws CouldNotSaveException + */ + public function execute(array $keywords, int $assetId): void + { + try { + $data = []; + /** @var KeywordInterface $keyword */ + foreach ($keywords as $keyword) { + $data[] = $keyword->getKeyword(); + } + + if (!empty($data)) { + /** @var Mysql $connection */ + $connection = $this->resourceConnection->getConnection(); + $connection->insertArray( + $this->resourceConnection->getTableName(self::TABLE_KEYWORD), + [self::KEYWORD], + $data, + AdapterInterface::INSERT_IGNORE + ); + + $this->saveAssetLinks->execute($assetId, $this->getKeywordIds($data)); + } + } catch (\Exception $exception) { + $this->logger->critical($exception); + $message = __('An error occurred during save asset keyword: %1', $exception->getMessage()); + throw new CouldNotSaveException($message, $exception); + } + } + + /** + * Select keywords by names + * + * @param string[] $keywords + * + * @return int[] + */ + private function getKeywordIds(array $keywords): array + { + $connection = $this->resourceConnection->getConnection(); + $select = $connection->select() + ->from(['k' => $this->resourceConnection->getTableName(self::TABLE_KEYWORD)]) + ->columns(self::ID) + ->where('k.' . self::KEYWORD . ' in (?)', $keywords); + + return $connection->fetchCol($select); + } +} diff --git a/app/code/Magento/MediaGallery/Model/Keyword/Command/SaveAssetLinks.php b/app/code/Magento/MediaGallery/Model/Keyword/Command/SaveAssetLinks.php new file mode 100644 index 0000000000000..4d3fd2bb5c30d --- /dev/null +++ b/app/code/Magento/MediaGallery/Model/Keyword/Command/SaveAssetLinks.php @@ -0,0 +1,83 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Model\Keyword\Command; + +use Magento\MediaGalleryApi\Api\Data\KeywordInterface; +use Magento\MediaGalleryApi\Model\Keyword\Command\SaveAssetLinksInterface; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Adapter\Pdo\Mysql; +use Magento\Framework\Exception\CouldNotSaveException; +use Psr\Log\LoggerInterface; + +/** + * Class SaveAssetLinks + */ +class SaveAssetLinks +{ + private const TABLE_ASSET_KEYWORD = 'media_gallery_asset_keyword'; + private const FIELD_ASSET_ID = 'asset_id'; + private const FIELD_KEYWORD_ID = 'keyword_id'; + + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * SaveAssetLinks constructor. + * + * @param ResourceConnection $resourceConnection + * @param LoggerInterface $logger + */ + public function __construct( + ResourceConnection $resourceConnection, + LoggerInterface $logger + ) { + $this->resourceConnection = $resourceConnection; + $this->logger = $logger; + } + + /** + * Save asset keywords links + * + * @param int $assetId + * @param KeywordInterface[] $keywordIds + * + * @throws CouldNotSaveException + */ + public function execute(int $assetId, array $keywordIds): void + { + try { + $values = []; + foreach ($keywordIds as $keywordId) { + $values[] = [$assetId, $keywordId]; + } + + if (!empty($values)) { + /** @var Mysql $connection */ + $connection = $this->resourceConnection->getConnection(); + $connection->insertArray( + $this->resourceConnection->getTableName(self::TABLE_ASSET_KEYWORD), + [self::FIELD_ASSET_ID, self::FIELD_KEYWORD_ID], + $values, + AdapterInterface::INSERT_IGNORE + ); + } + } catch (\Exception $exception) { + $this->logger->critical($exception); + $message = __('An error occurred during save asset keyword links: %1', $exception->getMessage()); + throw new CouldNotSaveException($message, $exception); + } + } +} diff --git a/app/code/Magento/MediaGallery/Plugin/Product/Gallery/Processor.php b/app/code/Magento/MediaGallery/Plugin/Product/Gallery/Processor.php new file mode 100644 index 0000000000000..3fbe4e3a91a2b --- /dev/null +++ b/app/code/Magento/MediaGallery/Plugin/Product/Gallery/Processor.php @@ -0,0 +1,75 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\MediaGallery\Plugin\Product\Gallery; + +use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByPathInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Gallery\Processor as ProcessorSubject; +use Psr\Log\LoggerInterface; + +/** + * Ensures that metadata is removed from the database when a product image has been deleted. + */ +class Processor +{ + /** + * @var DeleteByPathInterface + */ + private $deleteMediaAssetByPath; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * Processor constructor. + * + * @param DeleteByPathInterface $deleteMediaAssetByPath + * @param LoggerInterface $logger + */ + public function __construct( + DeleteByPathInterface $deleteMediaAssetByPath, + LoggerInterface $logger + ) { + $this->deleteMediaAssetByPath = $deleteMediaAssetByPath; + $this->logger = $logger; + } + + /** + * Remove media asset image after the product gallery image remove + * + * @param ProcessorSubject $subject + * @param ProcessorSubject $result + * @param Product $product + * @param string $file + * + * @return ProcessorSubject + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterRemoveImage( + ProcessorSubject $subject, + ProcessorSubject $result, + Product $product, + $file + ): ProcessorSubject { + if (!is_string($file)) { + return $result; + } + + try { + $this->deleteMediaAssetByPath->execute($file); + } catch (\Exception $exception) { + $this->logger->critical($exception); + } + + return $result; + } +} diff --git a/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php b/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php new file mode 100644 index 0000000000000..ff0e1528e0597 --- /dev/null +++ b/app/code/Magento/MediaGallery/Plugin/Wysiwyg/Images/Storage.php @@ -0,0 +1,95 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\MediaGallery\Plugin\Wysiwyg\Images; + +use Magento\MediaGalleryApi\Model\Asset\Command\GetByPathInterface; +use Magento\MediaGalleryApi\Model\Asset\Command\DeleteByPathInterface; +use Magento\Cms\Model\Wysiwyg\Images\Storage as StorageSubject; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Filesystem; +use Magento\Framework\Exception\ValidatorException; +use Psr\Log\LoggerInterface; + +/** + * Ensures that metadata is removed from the database when a file is deleted and it is an image + */ +class Storage +{ + /** + * @var GetByPathInterface + */ + private $getMediaAssetByPath; + + /** + * @var DeleteByPathInterface + */ + private $deleteMediaAssetByPath; + + /** + * @var Filesystem + */ + private $filesystem; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * Storage constructor. + * + * @param GetByPathInterface $getMediaAssetByPath + * @param DeleteByPathInterface $deleteMediaAssetByPath + * @param Filesystem $filesystem + * @param LoggerInterface $logger + */ + public function __construct( + GetByPathInterface $getMediaAssetByPath, + DeleteByPathInterface $deleteMediaAssetByPath, + Filesystem $filesystem, + LoggerInterface $logger + ) { + $this->getMediaAssetByPath = $getMediaAssetByPath; + $this->deleteMediaAssetByPath = $deleteMediaAssetByPath; + $this->filesystem = $filesystem; + $this->logger = $logger; + } + + /** + * Delete media data after the image delete action from Wysiwyg + * + * @param StorageSubject $subject + * @param StorageSubject $result + * @param string $target + * + * @return StorageSubject + * @throws ValidatorException + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterDeleteFile(StorageSubject $subject, StorageSubject $result, $target): StorageSubject + { + if (!is_string($target)) { + return $result; + } + + $relativePath = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA)->getRelativePath($target); + if (!$relativePath) { + return $result; + } + + try { + $this->deleteMediaAssetByPath->execute($relativePath); + } catch (\Exception $exception) { + $this->logger->critical($exception); + } + + return $result; + } +} diff --git a/app/code/Magento/MediaGallery/README.md b/app/code/Magento/MediaGallery/README.md new file mode 100644 index 0000000000000..ed97b5df98fc2 --- /dev/null +++ b/app/code/Magento/MediaGallery/README.md @@ -0,0 +1,23 @@ +# Magento_MediaGallery module + +The Magento_MediaGallery module is responsible for storing and managing media gallery assets attributes. + +## Installation details + +The Magento_MediaGallery module creates the following tables in the database: + +- `media_gallery_asset` +- `media_gallery_keyword` +- `media_gallery_asset_keyword` + +For information about module installation in Magento 2, see [Enable or disable modules](https://devdocs.magento.com/guides/v2.3/install-gde/install/cli/install-cli-subcommands-enable.html). + +## Extensibility + +Extension developers can interact with the Magento_MediaGallery module. For more information about the Magento extension mechanism, see [Magento plug-ins](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/plugins.html). + +[The Magento dependency injection mechanism](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/depend-inj.html) enables you to override the functionality of the Magento_MediaGallery module. + +## Additional information + +For information about significant changes in patch releases, see [2.3.x Release information](https://devdocs.magento.com/guides/v2.3/release-notes/bk-release-notes.html). diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/DeleteByPathTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/DeleteByPathTest.php new file mode 100644 index 0000000000000..e6de7232ac153 --- /dev/null +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/DeleteByPathTest.php @@ -0,0 +1,96 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Test\Unit\Model\Asset\Command; + +use Magento\MediaGallery\Model\Asset\Command\DeleteByPath; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Exception\CouldNotDeleteException; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Psr\Log\LoggerInterface; + +/** + * Test the DeleteByPath command model + */ +class DeleteByPathTest extends TestCase +{ + private const TABLE_NAME = 'media_gallery_asset'; + private const FILE_PATH = 'test-file-path/test.jpg'; + + /** + * @var DeleteByPath + */ + private $deleteMediaAssetByPath; + + /** + * @var AdapterInterface|MockObject + */ + private $adapter; + + /** + * @var LoggerInterface|MockObject + */ + private $logger; + + /** + * Initialize basic test class mocks + */ + protected function setUp(): void + { + $this->logger = $this->createMock(LoggerInterface::class); + $resourceConnection = $this->createMock(ResourceConnection::class); + + $this->deleteMediaAssetByPath = (new ObjectManager($this))->getObject( + DeleteByPath::class, + [ + 'resourceConnection' => $resourceConnection, + 'logger' => $this->logger, + ] + ); + + $this->adapter = $this->createMock(AdapterInterface::class); + $resourceConnection->expects($this->once()) + ->method('getConnection') + ->willReturn($this->adapter); + $resourceConnection->expects($this->once()) + ->method('getTableName') + ->with(self::TABLE_NAME) + ->willReturn('prefix_' . self::TABLE_NAME); + } + + /** + * Test delete media asset by path command + */ + public function testSuccessfulDeleteByIdExecution(): void + { + $this->adapter->expects($this->once()) + ->method('delete') + ->with('prefix_' . self::TABLE_NAME, ['path = ?' => self::FILE_PATH]); + + $this->deleteMediaAssetByPath->execute(self::FILE_PATH); + } + + /** + * Assume that delete action will thrown an Exception + */ + public function testExceptionOnDeleteExecution(): void + { + $this->adapter->expects($this->once()) + ->method('delete') + ->with('prefix_' . self::TABLE_NAME, ['path = ?' => self::FILE_PATH]) + ->willThrowException(new \Exception()); + + $this->expectException(CouldNotDeleteException::class); + $this->logger->expects($this->once()) + ->method('critical') + ->willReturnSelf(); + $this->deleteMediaAssetByPath->execute(self::FILE_PATH); + } +} diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionDuringMediaAssetInitializationTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionDuringMediaAssetInitializationTest.php new file mode 100644 index 0000000000000..0d0bfc510b518 --- /dev/null +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionDuringMediaAssetInitializationTest.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Test\Unit\Model\Asset\Command; + +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Select; +use Magento\Framework\Exception\IntegrationException; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\MediaGallery\Model\Asset\Command\GetById; +use Magento\MediaGalleryApi\Api\Data\AssetInterfaceFactory; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Zend\Db\Adapter\Driver\Pdo\Statement; + +/** + * Test the GetById command with exception during media asset initialization + */ +class GetByIdExceptionDuringMediaAssetInitializationTest extends \PHPUnit\Framework\TestCase +{ + private const MEDIA_ASSET_STUB_ID = 1; + + private const MEDIA_ASSET_DATA = ['id' => 1]; + + /** + * @var GetById|MockObject + */ + private $getMediaAssetById; + + /** + * @var AssetInterfaceFactory|MockObject + */ + private $assetFactory; + + /** + * @var AdapterInterface|MockObject + */ + private $adapter; + + /** + * @var Select|MockObject + */ + private $selectStub; + + /** + * @var Statement|MockObject + */ + private $statementMock; + + /** + * @var LoggerInterface|MockObject + */ + private $logger; + + /** + * Initialize basic test class mocks + */ + protected function setUp(): void + { + $resourceConnection = $this->createMock(ResourceConnection::class); + $this->assetFactory = $this->createMock(AssetInterfaceFactory::class); + $this->logger = $this->createMock(LoggerInterface::class); + + $this->getMediaAssetById = (new ObjectManager($this))->getObject( + GetById::class, + [ + 'resourceConnection' => $resourceConnection, + 'assetFactory' => $this->assetFactory, + 'logger' => $this->logger, + ] + ); + $this->adapter = $this->createMock(AdapterInterface::class); + $resourceConnection->method('getConnection')->willReturn($this->adapter); + + $this->selectStub = $this->createMock(Select::class); + $this->selectStub->method('from')->willReturnSelf(); + $this->selectStub->method('where')->willReturnSelf(); + $this->adapter->method('select')->willReturn($this->selectStub); + + $this->statementMock = $this->getMockBuilder(\Zend_Db_Statement_Interface::class)->getMock(); + } + + /** + * Test case when a problem occurred during asset initialization from received data. + */ + public function testErrorDuringMediaAssetInitializationException(): void + { + $this->statementMock->method('fetch')->willReturn(self::MEDIA_ASSET_DATA); + $this->adapter->method('query')->willReturn($this->statementMock); + + $this->assetFactory->expects($this->once())->method('create')->willThrowException(new \Exception()); + + $this->expectException(IntegrationException::class); + $this->logger->expects($this->any()) + ->method('critical') + ->willReturnSelf(); + + $this->getMediaAssetById->execute(self::MEDIA_ASSET_STUB_ID); + } +} diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php new file mode 100644 index 0000000000000..0ca9b3a3ffc8a --- /dev/null +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionNoSuchEntityTest.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Test\Unit\Model\Asset\Command; + +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Select; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\MediaGallery\Model\Asset\Command\GetById; +use Magento\MediaGalleryApi\Api\Data\AssetInterfaceFactory; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Zend\Db\Adapter\Driver\Pdo\Statement; + +/** + * Test the GetById command with exception thrown in case when there is no such entity + */ +class GetByIdExceptionNoSuchEntityTest extends \PHPUnit\Framework\TestCase +{ + private const MEDIA_ASSET_STUB_ID = 1; + + /** + * @var GetById|MockObject + */ + private $getMediaAssetById; + + /** + * @var AssetInterfaceFactory|MockObject + */ + private $assetFactory; + + /** + * @var AdapterInterface|MockObject + */ + private $adapter; + + /** + * @var Select|MockObject + */ + private $selectStub; + + /** + * @var Statement|MockObject + */ + private $statementMock; + + /** + * Initialize basic test class mocks + */ + protected function setUp(): void + { + $resourceConnection = $this->createMock(ResourceConnection::class); + $this->assetFactory = $this->createMock(AssetInterfaceFactory::class); + $logger = $this->createMock(LoggerInterface::class); + + $this->getMediaAssetById = (new ObjectManager($this))->getObject( + GetById::class, + [ + 'resourceConnection' => $resourceConnection, + 'assetFactory' => $this->assetFactory, + 'logger' => $logger, + ] + ); + $this->adapter = $this->createMock(AdapterInterface::class); + $resourceConnection->method('getConnection')->willReturn($this->adapter); + + $this->selectStub = $this->createMock(Select::class); + $this->selectStub->method('from')->willReturnSelf(); + $this->selectStub->method('where')->willReturnSelf(); + $this->adapter->method('select')->willReturn($this->selectStub); + + $this->statementMock = $this->getMockBuilder(\Zend_Db_Statement_Interface::class)->getMock(); + } + + /** + * Test case when there is no found media asset by id. + */ + public function testNotFoundMediaAssetException(): void + { + $this->statementMock->method('fetch')->willReturn([]); + $this->adapter->method('query')->willReturn($this->statementMock); + + $this->expectException(NoSuchEntityException::class); + + $this->getMediaAssetById->execute(self::MEDIA_ASSET_STUB_ID); + } +} diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionOnGetDataTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionOnGetDataTest.php new file mode 100644 index 0000000000000..a709c2d214bda --- /dev/null +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdExceptionOnGetDataTest.php @@ -0,0 +1,97 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Test\Unit\Model\Asset\Command; + +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Select; +use Magento\Framework\Exception\IntegrationException; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\MediaGallery\Model\Asset\Command\GetById; +use Magento\MediaGalleryApi\Api\Data\AssetInterfaceFactory; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Zend\Db\Adapter\Driver\Pdo\Statement; + +/** + * Test the GetById command with exception during get media data + */ +class GetByIdExceptionOnGetDataTest extends \PHPUnit\Framework\TestCase +{ + private const MEDIA_ASSET_STUB_ID = 1; + + private const MEDIA_ASSET_DATA = ['id' => 1]; + + /** + * @var GetById|MockObject + */ + private $getMediaAssetById; + /** + * @var AdapterInterface|MockObject + */ + private $adapter; + + /** + * @var Select|MockObject + */ + private $selectStub; + + /** + * @var LoggerInterface|MockObject + */ + private $logger; + + /** + * @var Statement|MockObject + */ + private $statementMock; + + /** + * Initialize basic test class mocks + */ + protected function setUp(): void + { + $resourceConnection = $this->createMock(ResourceConnection::class); + $assetFactory = $this->createMock(AssetInterfaceFactory::class); + $this->logger = $this->createMock(LoggerInterface::class); + + $this->getMediaAssetById = (new ObjectManager($this))->getObject( + GetById::class, + [ + 'resourceConnection' => $resourceConnection, + 'assetFactory' => $assetFactory, + 'logger' => $this->logger, + ] + ); + $this->adapter = $this->createMock(AdapterInterface::class); + $resourceConnection->method('getConnection')->willReturn($this->adapter); + + $this->selectStub = $this->createMock(Select::class); + $this->selectStub->method('from')->willReturnSelf(); + $this->selectStub->method('where')->willReturnSelf(); + $this->adapter->method('select')->willReturn($this->selectStub); + + $this->statementMock = $this->getMockBuilder(\Zend_Db_Statement_Interface::class)->getMock(); + } + + /** + * Test an exception during the get asset data query. + */ + public function testExceptionDuringGetMediaAssetData(): void + { + $this->statementMock->method('fetch')->willReturn(self::MEDIA_ASSET_DATA); + $this->adapter->method('query')->willThrowException(new \Exception()); + + $this->expectException(IntegrationException::class); + $this->logger->expects($this->any()) + ->method('critical') + ->willReturnSelf(); + + $this->getMediaAssetById->execute(self::MEDIA_ASSET_STUB_ID); + } +} diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdSuccessfulTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdSuccessfulTest.php new file mode 100644 index 0000000000000..c300d4f121bd2 --- /dev/null +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/GetByIdSuccessfulTest.php @@ -0,0 +1,99 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Test\Unit\Model\Asset\Command; + +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Select; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\MediaGallery\Model\Asset\Command\GetById; +use Magento\MediaGalleryApi\Api\Data\AssetInterface; +use Magento\MediaGalleryApi\Api\Data\AssetInterfaceFactory; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Zend\Db\Adapter\Driver\Pdo\Statement; + +/** + * Test the GetById command successful scenario + */ +class GetByIdSuccessfulTest extends \PHPUnit\Framework\TestCase +{ + private const MEDIA_ASSET_STUB_ID = 1; + + private const MEDIA_ASSET_DATA = ['id' => 1]; + + /** + * @var GetById|MockObject + */ + private $getMediaAssetById; + + /** + * @var AssetInterfaceFactory|MockObject + */ + private $assetFactory; + + /** + * @var AdapterInterface|MockObject + */ + private $adapter; + + /** + * @var Select|MockObject + */ + private $selectStub; + + /** + * @var Statement|MockObject + */ + private $statementMock; + + /** + * Initialize basic test class mocks + */ + protected function setUp(): void + { + $resourceConnection = $this->createMock(ResourceConnection::class); + $this->assetFactory = $this->createMock(AssetInterfaceFactory::class); + $logger = $this->createMock(LoggerInterface::class); + + $this->getMediaAssetById = (new ObjectManager($this))->getObject( + GetById::class, + [ + 'resourceConnection' => $resourceConnection, + 'assetFactory' => $this->assetFactory, + 'logger' => $logger, + ] + ); + $this->adapter = $this->createMock(AdapterInterface::class); + $resourceConnection->method('getConnection')->willReturn($this->adapter); + + $this->selectStub = $this->createMock(Select::class); + $this->selectStub->method('from')->willReturnSelf(); + $this->selectStub->method('where')->willReturnSelf(); + $this->adapter->method('select')->willReturn($this->selectStub); + + $this->statementMock = $this->getMockBuilder(\Zend_Db_Statement_Interface::class)->getMock(); + } + + /** + * Test successful get media asset by id command execution. + */ + public function testSuccessfulGetByIdExecution(): void + { + $this->statementMock->method('fetch')->willReturn(self::MEDIA_ASSET_DATA); + $this->adapter->method('query')->willReturn($this->statementMock); + + $mediaAssetStub = $this->getMockBuilder(AssetInterface::class)->getMock(); + $this->assetFactory->expects($this->once())->method('create')->willReturn($mediaAssetStub); + + $this->assertEquals( + $mediaAssetStub, + $this->getMediaAssetById->execute(self::MEDIA_ASSET_STUB_ID) + ); + } +} diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/SaveTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/SaveTest.php new file mode 100644 index 0000000000000..2f736fb832eac --- /dev/null +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Asset/Command/SaveTest.php @@ -0,0 +1,179 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Test\Unit\Model\Asset\Command; + +use Magento\MediaGallery\Model\Asset\Command\Save; +use Magento\MediaGalleryApi\Api\Data\AssetInterface; +use Magento\MediaGalleryApi\Model\DataExtractorInterface; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Adapter\Pdo\Mysql; +use Magento\Framework\Exception\CouldNotSaveException; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + +/** + * Tests the Save model using PHPUnit + */ +class SaveTest extends TestCase +{ + + /** + * Constant for tablename of media gallery assets + */ + private const TABLE_MEDIA_GALLERY_ASSET = 'media_gallery_asset'; + + /** + * Constant for prefixed tablename of media gallery assets + */ + private const PREFIXED_TABLE_MEDIA_GALLERY_ASSET = 'prefix_' . self::TABLE_MEDIA_GALLERY_ASSET; + + /** + * Constant for last ID generated after data insertion + */ + private const INSERT_ID = '1'; + + /** + * Constant for affected rows count after data insertion + */ + private const AFFECTED_ROWS = 1; + + /** + * Constant for image data + */ + private const IMAGE_DATA = [ + 'path' => '/test/path', + 'title' => 'Test Title', + 'source' => 'Adobe Stock', + 'content_type' => 'image/jpeg', + 'height' => 4863, + 'width' => 12129 + ]; + + /** + * @var MockObject | ResourceConnection + */ + private $resourceConnectionMock; + + /** + * @var MockObject | DataExtractorInterface + */ + private $loggerMock; + + /** + * @var MockObject | LoggerInterface + */ + private $extractorMock; + + /** + * @var MockObject | AdapterInterface + */ + private $adapterMock; + + /** + * @var MockObject | AssetInterface + */ + private $mediaAssetMock; + + /** + * @var Save + */ + private $save; + + /** + * Set up test mocks + */ + protected function setUp(): void + { + /* Intermediary mocks */ + $this->adapterMock = $this->createMock(Mysql::class); + $this->mediaAssetMock = $this->createMock(AssetInterface::class); + + /* Save constructor mocks */ + $this->extractorMock = $this->createMock(DataExtractorInterface::class); + $this->loggerMock = $this->createMock(LoggerInterface::class); + $this->resourceConnectionMock = $this->createConfiguredMock( + ResourceConnection::class, + [ + 'getConnection' => $this->adapterMock, + 'getTableName' => self::PREFIXED_TABLE_MEDIA_GALLERY_ASSET + ] + ); + + /* Create Save instance with mocks */ + $this->save = (new ObjectManager($this))->getObject( + Save::class, + [ + 'resourceConnection' => $this->resourceConnectionMock, + 'extractor' => $this->extractorMock, + 'logger' => $this->loggerMock + ] + ); + } + + /** + * Tests a successful Save::execute method + */ + public function testSuccessfulExecute(): void + { + $this->resourceConnectionMock->expects(self::once())->method('getConnection'); + $this->resourceConnectionMock->expects(self::once())->method('getTableName'); + + $this->extractorMock + ->expects(self::once()) + ->method('extract') + ->with($this->mediaAssetMock, AssetInterface::class) + ->willReturn(self::IMAGE_DATA); + + $this->adapterMock + ->expects(self::once()) + ->method('insertOnDuplicate') + ->with(self::PREFIXED_TABLE_MEDIA_GALLERY_ASSET, self::IMAGE_DATA) + ->willReturn(self::AFFECTED_ROWS); + + $this->adapterMock + ->expects(self::once()) + ->method('lastInsertId') + ->with(self::PREFIXED_TABLE_MEDIA_GALLERY_ASSET) + ->willReturn(self::INSERT_ID); + + $this->save->execute($this->mediaAssetMock); + } + + /** + * Tests Save::execute method with an exception thrown + */ + public function testExceptionExecute(): void + { + $this->resourceConnectionMock->expects(self::once())->method('getConnection'); + $this->resourceConnectionMock->expects(self::once())->method('getTableName'); + + $this->extractorMock + ->expects(self::once()) + ->method('extract') + ->with($this->mediaAssetMock, AssetInterface::class) + ->willReturn(self::IMAGE_DATA); + + $this->adapterMock + ->expects(self::once()) + ->method('insertOnDuplicate') + ->with(self::PREFIXED_TABLE_MEDIA_GALLERY_ASSET, self::IMAGE_DATA) + ->willThrowException(new \Zend_Db_Exception()); + + $this->loggerMock + ->expects(self::once()) + ->method('critical') + ->willReturnSelf(); + + $this->expectException(CouldNotSaveException::class); + + $this->save->execute($this->mediaAssetMock); + } +} diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php new file mode 100644 index 0000000000000..7d57f32449f56 --- /dev/null +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/DataExtractorTest.php @@ -0,0 +1,139 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Test\Unit\Model; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\MediaGallery\Model\Asset; +use Magento\MediaGallery\Model\Keyword; +use Magento\MediaGalleryApi\Api\Data\AssetInterface; +use Magento\MediaGalleryApi\Api\Data\KeywordInterface; +use Magento\MediaGallery\Model\DataExtractor; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class DataExtractorTest extends TestCase +{ + /** + * @var DataExtractor|MockObject + */ + private $dataExtractor; + + /** + * Initialize basic test class mocks + */ + protected function setUp(): void + { + $this->dataExtractor = new DataExtractor(); + } + + /** + * Test extract object data by interface + * + * @dataProvider assetProvider + * + * @param string $class + * @param string|null $interfaceClass + * @param array $expectedData + * + * @throws \ReflectionException + */ + public function testExtractData(string $class, $interfaceClass, array $expectedData): void + { + $data = []; + foreach ($expectedData as $expectedDataKey => $expectedDataItem) { + $data[$expectedDataKey] = $expectedDataItem['value']; + } + $model = (new ObjectManager($this))->getObject( + $class, + [ + 'data' => $data, + ] + ); + $receivedData = $this->dataExtractor->extract($model, $interfaceClass); + $this->checkValues($expectedData, $receivedData, $model); + } + + /** + * @param array $expectedData + * @param array $data + * @param object $model + */ + protected function checkValues(array $expectedData, array $data, $model) + { + foreach ($expectedData as $expectedDataKey => $expectedDataItem) { + $this->assertEquals($data[$expectedDataKey] ?? null, $model->{$expectedDataItem['method']}()); + $this->assertEquals($data[$expectedDataKey] ?? null, $expectedDataItem['value']); + } + $this->assertEquals(array_keys($expectedData), array_keys($expectedData)); + } + + /** + * @return array + */ + public function assetProvider() + { + return [ + 'Asset conversion with interface' => [ + Asset::class, + AssetInterface::class, + [ + 'id' => [ + 'value' => 2, + 'method' => 'getId', + ], + 'path' => [ + 'value' => 'path', + 'method' => 'getPath', + ], + 'title' => [ + 'value' => 'title', + 'method' => 'getTitle', + ], + 'source' => [ + 'value' => 'source', + 'method' => 'getSource', + ], + 'content_type' => [ + 'value' => 'content_type', + 'method' => 'getContentType', + ], + 'width' => [ + 'value' => 3, + 'method' => 'getWidth', + ], + 'height' => [ + 'value' => 4, + 'method' => 'getHeight', + ], + 'created_at' => [ + 'value' => '2019-11-28 10:40:09', + 'method' => 'getCreatedAt', + ], + 'updated_at' => [ + 'value' => '2019-11-28 10:41:08', + 'method' => 'getUpdatedAt', + ], + ], + ], + 'Keyword conversion without interface' => [ + Keyword::class, + null, + [ + 'id' => [ + 'value' => 2, + 'method' => 'getId', + ], + 'keyword' => [ + 'value' => 'keyword', + 'method' => 'getKeyword', + ], + ], + ] + ]; + } +} diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/GetAssetKeywordsTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/GetAssetKeywordsTest.php new file mode 100644 index 0000000000000..2ccac4eac8343 --- /dev/null +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/GetAssetKeywordsTest.php @@ -0,0 +1,150 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Test\Unit\Model\Keyword\Command; + +use Magento\Framework\Exception\IntegrationException; +use Magento\MediaGallery\Model\Keyword\Command\GetAssetKeywords; +use Magento\MediaGalleryApi\Api\Data\KeywordInterface; +use Magento\MediaGalleryApi\Api\Data\KeywordInterfaceFactory; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Select; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + +/** + * GetAssetKeywordsTest + */ +class GetAssetKeywordsTest extends TestCase +{ + /** + * @var GetAssetKeywords + */ + private $sut; + + /** + * @var ResourceConnection | MockObject + */ + private $resourceConnectionStub; + + /** + * @var KeywordInterfaceFactory | MockObject + */ + private $assetKeywordFactoryStub; + + /** + * @var LoggerInterface|MockObject + */ + private $loggerMock; + + protected function setUp(): void + { + $this->resourceConnectionStub = $this->createMock(ResourceConnection::class); + $this->assetKeywordFactoryStub = $this->createMock(KeywordInterfaceFactory::class); + $this->loggerMock = $this->createMock(LoggerInterface::class); + + $this->sut = new GetAssetKeywords( + $this->resourceConnectionStub, + $this->assetKeywordFactoryStub, + $this->loggerMock + ); + } + + /** + * Posive test for the main case + * + * @dataProvider casesProvider() + * @param array $databaseQueryResult + * @param int $expectedNumberOfFoundKeywords + */ + public function testFind(array $databaseQueryResult, int $expectedNumberOfFoundKeywords): void + { + $randomAssetId = 12345; + $this->configureResourceConnectionStub($databaseQueryResult); + $this->configureAssetKeywordFactoryStub(); + + /** @var KeywordInterface[] $keywords */ + $keywords = $this->sut->execute($randomAssetId); + + $this->assertCount($expectedNumberOfFoundKeywords, $keywords); + } + + /** + * Data provider for testFind + * + * @return array + */ + public function casesProvider(): array + { + return [ + 'not_found' => [[],0], + 'find_one_keyword' => [['keywordRawData'],1], + 'find_several_keywords' => [['keywordRawData', 'keywordRawData'],2], + ]; + } + + /** + * Test case when an error occured during get data request. + * + * @throws IntegrationException + */ + public function testNotFoundBecauseOfError(): void + { + $randomAssetId = 1; + + $this->resourceConnectionStub + ->method('getConnection') + ->willThrowException((new \Exception())); + + $this->expectException(IntegrationException::class); + $this->loggerMock->expects($this->once()) + ->method('critical') + ->willReturnSelf(); + + $this->sut->execute($randomAssetId); + } + + /** + * Very fragile and coupled to the implementation + * + * @param array $queryResult + */ + private function configureResourceConnectionStub(array $queryResult): void + { + $statementMock = $this->getMockBuilder(\Zend_Db_Statement_Interface::class)->getMock(); + $statementMock + ->method('fetchAll') + ->willReturn($queryResult); + + $selectStub = $this->createMock(Select::class); + $selectStub->method('from')->willReturnSelf(); + $selectStub->method('join')->willReturnSelf(); + $selectStub->method('where')->willReturnSelf(); + + $connectionMock = $this->getMockBuilder(AdapterInterface::class)->getMock(); + $connectionMock + ->method('select') + ->willReturn($selectStub); + $connectionMock + ->method('query') + ->willReturn($statementMock); + + $this->resourceConnectionStub + ->method('getConnection') + ->willReturn($connectionMock); + } + + private function configureAssetKeywordFactoryStub(): void + { + $keywordStub = $this->getMockBuilder(KeywordInterface::class)->getMock(); + $this->assetKeywordFactoryStub + ->method('create') + ->willReturn($keywordStub); + } +} diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetKeywordsTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetKeywordsTest.php new file mode 100644 index 0000000000000..a55c60024c08d --- /dev/null +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetKeywordsTest.php @@ -0,0 +1,170 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Test\Unit\Model\Keyword\Command; + +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DataObject; +use Magento\Framework\DB\Adapter\Pdo\Mysql; +use Magento\Framework\DB\Select; +use Magento\Framework\Exception\CouldNotSaveException; +use Magento\MediaGallery\Model\Keyword\Command\SaveAssetKeywords; +use Magento\MediaGallery\Model\Keyword\Command\SaveAssetLinks; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + +/** + * SaveAssetKeywordsTest. + */ +class SaveAssetKeywordsTest extends TestCase +{ + /** + * @var SaveAssetKeywords + */ + private $sut; + + /** + * @var ResourceConnection|MockObject + */ + private $resourceConnectionMock; + + /** + * @var Mysql|MockObject + */ + private $connectionMock; + + /** + * @var SaveAssetLinks|MockObject + */ + private $saveAssetLinksMock; + + /** + * @var Select|MockObject + */ + private $selectMock; + + /** + * @var LoggerInterface|MockObject + */ + private $loggerMock; + + /** + * SetUp + */ + public function setUp(): void + { + $this->resourceConnectionMock = $this->createMock(ResourceConnection::class); + $this->saveAssetLinksMock = $this->createMock(SaveAssetLinks::class); + $this->connectionMock = $this->getMockBuilder(Mysql::class) + ->disableOriginalConstructor() + ->getMock(); + $this->selectMock = $this->createMock(Select::class); + $this->loggerMock = $this->createMock(LoggerInterface::class); + + $this->sut = new SaveAssetKeywords( + $this->resourceConnectionMock, + $this->saveAssetLinksMock, + $this->loggerMock + ); + } + + /** + * Test saving the asset keywords + * + * @dataProvider assetKeywordsDataProvider + * + * @param array $keywords + * @param int $assetId + * @param array $items + */ + public function testAssetKeywordsSave(array $keywords, int $assetId, array $items): void + { + $expectedCalls = (int) (count($keywords)); + + if ($expectedCalls) { + $this->prepareResourceConnection(); + $this->connectionMock->expects($this->once()) + ->method('insertArray') + ->with( + 'prefix_media_gallery_keyword', + ['keyword'], + $items, + 2 + ); + } + + $this->sut->execute($keywords, $assetId); + } + + /** + * Testing throwing exception handling + * + * @throws CouldNotSaveException + */ + public function testAssetNotSavingCausedByError(): void + { + $keyword = new DataObject(['keyword' => 'keyword-1']); + + $this->resourceConnectionMock + ->method('getConnection') + ->willThrowException((new \Exception())); + $this->expectException(CouldNotSaveException::class); + $this->loggerMock->expects($this->once()) + ->method('critical') + ->willReturnSelf(); + + $this->sut->execute([$keyword], 1); + } + + /** + * Preparing the resource connection + */ + private function prepareResourceConnection(): void + { + $this->selectMock->method('from')->willReturnSelf(); + $this->selectMock->method('columns')->with('id')->willReturnSelf(); + $this->selectMock->method('where')->willReturnSelf(); + + $this->connectionMock + ->method('select') + ->willReturn($this->selectMock); + $this->connectionMock + ->method('fetchCol') + ->willReturn([['id'=> 1], ['id' => 2]]); + $this->resourceConnectionMock->expects($this->any()) + ->method('getConnection') + ->willReturn($this->connectionMock); + $this->resourceConnectionMock->expects($this->any()) + ->method('getTableName') + ->with('media_gallery_keyword') + ->willReturn('prefix_media_gallery_keyword'); + } + + /** + * Providing asset keywords + * + * @return array + */ + public function assetKeywordsDataProvider(): array + { + return [ + [ + [], + 1, + [] + ], [ + [ + new DataObject(['keyword' => 'keyword-1']), + new DataObject(['keyword' => 'keyword-2']), + ], + 1, + ['keyword-1', 'keyword-2'] + ] + ]; + } +} diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetLinksTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetLinksTest.php new file mode 100644 index 0000000000000..2981c534586e2 --- /dev/null +++ b/app/code/Magento/MediaGallery/Test/Unit/Model/Keyword/Command/SaveAssetLinksTest.php @@ -0,0 +1,142 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGallery\Test\Unit\Model\Keyword\Command; + +use Magento\MediaGallery\Model\Keyword\Command\SaveAssetLinks; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Exception\CouldNotSaveException; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + +/** + * SaveAssetLinksTest. + */ +class SaveAssetLinksTest extends TestCase +{ + /** + * @var SaveAssetLinks + */ + private $sut; + + /** + * @var AdapterInterface|MockObject + */ + private $connectionMock; + + /** + * @var ResourceConnection|MockObject + */ + private $resourceConnectionMock; + + /** + * @var LoggerInterface|MockObject + */ + private $loggerMock; + + /** + * Prepare test objects. + */ + public function setUp(): void + { + $this->connectionMock = $this->createMock(AdapterInterface::class); + $this->resourceConnectionMock = $this->createMock(ResourceConnection::class); + $this->loggerMock = $this->createMock(LoggerInterface::class); + + $this->sut = new SaveAssetLinks( + $this->resourceConnectionMock, + $this->loggerMock + ); + } + + /** + * Test saving the asset keyword links + * + * @dataProvider assetLinksDataProvider + * + * @param int $assetId + * @param array $keywordIds + * @param array $values + */ + public function testAssetKeywordsSave(int $assetId, array $keywordIds, array $values): void + { + $expectedCalls = (int) (count($keywordIds)); + + if ($expectedCalls) { + $this->resourceConnectionMock->expects($this->once()) + ->method('getConnection') + ->willReturn($this->connectionMock); + $this->resourceConnectionMock->expects($this->once()) + ->method('getTableName') + ->with('media_gallery_asset_keyword') + ->willReturn('prefix_media_gallery_asset_keyword'); + $this->connectionMock->expects($this->once()) + ->method('insertArray') + ->with( + 'prefix_media_gallery_asset_keyword', + ['asset_id', 'keyword_id'], + $values, + 2 + ); + } + + $this->sut->execute($assetId, $keywordIds); + } + + /** + * Testing throwing exception handling + * + * @throws CouldNotSaveException + */ + public function testAssetNotSavingCausedByError(): void + { + $this->resourceConnectionMock->expects($this->once()) + ->method('getConnection') + ->willReturn($this->connectionMock); + $this->connectionMock->expects($this->once()) + ->method('insertArray') + ->willThrowException((new \Exception())); + $this->expectException(CouldNotSaveException::class); + $this->loggerMock->expects($this->once()) + ->method('critical') + ->willReturnSelf(); + + $this->sut->execute(1, [1, 2]); + } + + /** + * Providing asset links + * + * @return array + */ + public function assetLinksDataProvider(): array + { + return [ + [ + 12, + [], + [] + ], + [ + 12, + [1], + [ + [12, 1] + ] + ], [ + 12, + [1, 2], + [ + [12, 1], + [12, 2], + ] + ] + ]; + } +} diff --git a/app/code/Magento/MediaGallery/composer.json b/app/code/Magento/MediaGallery/composer.json new file mode 100644 index 0000000000000..977277d993061 --- /dev/null +++ b/app/code/Magento/MediaGallery/composer.json @@ -0,0 +1,24 @@ +{ + "name": "magento/module-media-gallery", + "description": "Magento module responsible for media handling", + "require": { + "php": "~7.1.3||~7.2.0||~7.3.0", + "magento/framework": "*", + "magento/module-media-gallery-api": "*", + "magento/module-cms": "*", + "magento/module-catalog": "*" + }, + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\MediaGallery\\": "" + } + } +} diff --git a/app/code/Magento/MediaGallery/etc/db_schema.xml b/app/code/Magento/MediaGallery/etc/db_schema.xml new file mode 100644 index 0000000000000..fac1342528f2c --- /dev/null +++ b/app/code/Magento/MediaGallery/etc/db_schema.xml @@ -0,0 +1,58 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd"> + <table name="media_gallery_asset" resource="default" engine="innodb" comment="Media Gallery Asset"> + <column xsi:type="int" name="id" padding="10" unsigned="true" nullable="false" identity="true" comment="Entity ID"/> + <column xsi:type="varchar" name="path" length="255" nullable="true" comment="Path"/> + <column xsi:type="varchar" name="title" length="255" nullable="true" comment="Title"/> + <column xsi:type="varchar" name="source" length="255" nullable="true" comment="Source"/> + <column xsi:type="varchar" name="content_type" length="255" nullable="true" comment="Content Type"/> + <column xsi:type="int" name="width" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Width"/> + <column xsi:type="int" name="height" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Height"/> + <column xsi:type="timestamp" name="created_at" on_update="false" nullable="false" default="CURRENT_TIMESTAMP" comment="Created At"/> + <column xsi:type="timestamp" name="updated_at" on_update="true" nullable="false" default="CURRENT_TIMESTAMP" comment="Updated At"/> + <constraint xsi:type="primary" referenceId="PRIMARY"> + <column name="id"/> + </constraint> + <index referenceId="MEDIA_GALLERY_ID" indexType="btree"> + <column name="id"/> + </index> + <constraint xsi:type="unique" referenceId="MEDIA_GALLERY_ID_PATH_TITLE_CONTENT_TYPE_WIDTH_HEIGHT"> + <column name="path"/> + </constraint> + </table> + <table name="media_gallery_keyword" resource="default" engine="innodb" comment="Media Gallery Keyword"> + <column xsi:type="int" name="id" padding="10" unsigned="true" nullable="false" identity="true" comment="Keyword ID"/> + <column xsi:type="varchar" length="255" name="keyword" nullable="false" comment="Keyword"/> + <constraint xsi:type="primary" referenceId="PRIMARY"> + <column name="id"/> + </constraint> + <index referenceId="MEDIA_GALLERY_KEYWORD" indexType="btree"> + <column name="id"/> + </index> + <constraint xsi:type="unique" referenceId="MEDIA_GALLERY_KEYWORD_KEYWORD_UNIQUE"> + <column name="keyword"/> + </constraint> + </table> + <table name="media_gallery_asset_keyword" resource="default" engine="innodb" comment="Media Gallery Asset Keyword"> + <column xsi:type="int" name="keyword_id" padding="10" unsigned="true" nullable="false" identity="false" comment="Keyword Id"/> + <column xsi:type="int" name="asset_id" padding="10" unsigned="true" nullable="false" identity="false" comment="Asset ID"/> + <index referenceId="MEDIA_GALLERY_ASSET_KEYWORD_ASSET_ID_INDEX" indexType="btree"> + <column name="asset_id"/> + </index> + <index referenceId="MEDIA_GALLERY_ASSET_KEYWORD_KEYWORD_ID_INDEX" indexType="btree"> + <column name="keyword_id"/> + </index> + <constraint xsi:type="primary" referenceId="PRIMARY"> + <column name="keyword_id"/> + <column name="asset_id"/> + </constraint> + <constraint xsi:type="foreign" referenceId="MEDIA_GALLERY_KEYWORD_KEYWORD_ID_MEDIA_GALLERY_KEYWORD_ID" table="media_gallery_asset_keyword" column="keyword_id" referenceTable="media_gallery_keyword" referenceColumn="id" onDelete="CASCADE"/> + <constraint xsi:type="foreign" referenceId="MEDIA_GALLERY_KEYWORD_ASSET_ID_ASSET_ID" table="media_gallery_asset_keyword" column="asset_id" referenceTable="media_gallery_asset" referenceColumn="id" onDelete="CASCADE"/> + </table> +</schema> diff --git a/app/code/Magento/MediaGallery/etc/db_schema_whitelist.json b/app/code/Magento/MediaGallery/etc/db_schema_whitelist.json new file mode 100644 index 0000000000000..10db10d5dd5db --- /dev/null +++ b/app/code/Magento/MediaGallery/etc/db_schema_whitelist.json @@ -0,0 +1,56 @@ +{ + "media_gallery_asset": { + "column": { + "id": true, + "path": true, + "title": true, + "source": true, + "content_type": true, + "width": true, + "height": true, + "created_at": true, + "updated_at": true + }, + "index": { + "MEDIA_GALLERY_ID": true, + "MEDIA_GALLERY_ASSET_ID": true + }, + "constraint": { + "MEDIA_GALLERY_ID_PATH_TITLE_CONTENT_TYPE_WIDTH_HEIGHT": true, + "PRIMARY": true, + "MEDIA_GALLERY_ASSET_PATH": true + } + }, + "media_gallery_keyword": { + "column": { + "id": true, + "keyword": true + }, + "index": { + "MEDIA_GALLERY_KEYWORD_ID": true + }, + "constraint": { + "MEDIA_GALLERY_KEYWORD_KEYWORD": true, + "PRIMARY": true + } + }, + "media_gallery_asset_keyword": { + "column": { + "keyword_id": true, + "asset_id": true + }, + "index": { + "MEDIA_GALLERY_ASSET_KEYWORD_ASSET_ID_INDEX": true, + "MEDIA_GALLERY_ASSET_KEYWORD_KEYWORD_ID_INDEX": true, + "MEDIA_GALLERY_ASSET_KEYWORD_ASSET_ID": true, + "MEDIA_GALLERY_ASSET_KEYWORD_KEYWORD_ID": true + }, + "constraint": { + "PRIMARY": true, + "MEDIA_GALLERY_KEYWORD_KEYWORD_ID_MEDIA_GALLERY_KEYWORD_ID": true, + "MEDIA_GALLERY_KEYWORD_ASSET_ID_ASSET_ID": true, + "MEDIA_GALLERY_ASSET_KEYWORD_KEYWORD_ID_MEDIA_GALLERY_KEYWORD_ID": true, + "MEDIA_GALLERY_ASSET_KEYWORD_ASSET_ID_MEDIA_GALLERY_ASSET_ID": true + } + } +} \ No newline at end of file diff --git a/app/code/Magento/MediaGallery/etc/di.xml b/app/code/Magento/MediaGallery/etc/di.xml new file mode 100644 index 0000000000000..8c4a856852e1a --- /dev/null +++ b/app/code/Magento/MediaGallery/etc/di.xml @@ -0,0 +1,31 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <preference for="Magento\MediaGalleryApi\Api\Data\KeywordInterface" type="Magento\MediaGallery\Model\Keyword"/> + <preference for="Magento\MediaGalleryApi\Api\Data\AssetInterface" type="Magento\MediaGallery\Model\Asset"/> + + <preference for="Magento\MediaGalleryApi\Model\Asset\Command\GetByIdInterface" type="Magento\MediaGallery\Model\Asset\Command\GetById"/> + <preference for="Magento\MediaGalleryApi\Model\Asset\Command\SaveInterface" type="Magento\MediaGallery\Model\Asset\Command\Save"/> + <preference for="Magento\MediaGalleryApi\Model\Asset\Command\GetByPathInterface" type="Magento\MediaGallery\Model\Asset\Command\GetByPath"/> + <preference for="Magento\MediaGalleryApi\Model\Asset\Command\DeleteByPathInterface" type="Magento\MediaGallery\Model\Asset\Command\DeleteByPath"/> + + <preference for="Magento\MediaGalleryApi\Model\Keyword\Command\GetAssetKeywordsInterface" type="Magento\MediaGallery\Model\Keyword\Command\GetAssetKeywords"/> + <preference for="Magento\MediaGalleryApi\Model\Keyword\Command\SaveAssetKeywordsInterface" type="Magento\MediaGallery\Model\Keyword\Command\SaveAssetKeywords"/> + <preference for="Magento\MediaGalleryApi\Model\Keyword\Command\SaveAssetLinksInterface" type="Magento\MediaGallery\Model\Keyword\Command\SaveAssetLinks"/> + + <preference for="Magento\MediaGalleryApi\Model\DataExtractorInterface" type="Magento\MediaGallery\Model\DataExtractor"/> + + <type name="Magento\Catalog\Model\Product\Gallery\Processor"> + <plugin name="media_gallery_image_remove_metadata" type="Magento\MediaGallery\Plugin\Product\Gallery\Processor" + sortOrder="10" disabled="false"/> + </type> + <type name="Magento\Cms\Model\Wysiwyg\Images\Storage"> + <plugin name="media_gallery_image_remove_metadata_after_wysiwyg" type="Magento\MediaGallery\Plugin\Wysiwyg\Images\Storage" + sortOrder="10" disabled="false"/> + </type> +</config> diff --git a/app/code/Magento/MediaGallery/etc/module.xml b/app/code/Magento/MediaGallery/etc/module.xml new file mode 100644 index 0000000000000..bf731d899c15b --- /dev/null +++ b/app/code/Magento/MediaGallery/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_MediaGallery"/> +</config> diff --git a/app/code/Magento/MediaGallery/registration.php b/app/code/Magento/MediaGallery/registration.php new file mode 100644 index 0000000000000..a243eee924894 --- /dev/null +++ b/app/code/Magento/MediaGallery/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_MediaGallery', __DIR__); diff --git a/app/code/Magento/MediaGalleryApi/Api/Data/AssetInterface.php b/app/code/Magento/MediaGalleryApi/Api/Data/AssetInterface.php new file mode 100644 index 0000000000000..affae296ca530 --- /dev/null +++ b/app/code/Magento/MediaGalleryApi/Api/Data/AssetInterface.php @@ -0,0 +1,96 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\MediaGalleryApi\Api\Data; + +use Magento\Framework\Api\ExtensibleDataInterface; + +/** + * Represents a media gallery asset which contains information about a media asset entity such + * as path to the media storage, media asset title and its content type, etc. + */ +interface AssetInterface extends ExtensibleDataInterface +{ + /** + * Get ID + * + * @return int|null + */ + public function getId(): ?int; + + /** + * Get Path + * + * @return string + */ + public function getPath(): string; + + /** + * Get title + * + * @return string|null + */ + public function getTitle(): ?string; + + /** + * Get source of the file + * + * @return string|null + */ + public function getSource(): ?string; + + /** + * Get content type + * + * @return string + */ + public function getContentType(): string; + + /** + * Retrieve full licensed asset's height + * + * @return int + */ + public function getHeight(): int; + + /** + * Retrieve full licensed asset's width + * + * @return int + */ + public function getWidth(): int; + + /** + * Get created at + * + * @return string + */ + public function getCreatedAt(): string; + + /** + * Get updated at + * + * @return string + */ + public function getUpdatedAt(): string; + + /** + * Retrieve existing extension attributes object or create a new one. + * + * @return \Magento\MediaGalleryApi\Api\Data\AssetExtensionInterface|null + */ + public function getExtensionAttributes(): AssetExtensionInterface; + + /** + * Set extension attributes + * + * @param \Magento\MediaGalleryApi\Api\Data\AssetExtensionInterface $extensionAttributes + * @return void + */ + public function setExtensionAttributes(AssetExtensionInterface $extensionAttributes): void; +} diff --git a/app/code/Magento/MediaGalleryApi/Api/Data/KeywordInterface.php b/app/code/Magento/MediaGalleryApi/Api/Data/KeywordInterface.php new file mode 100644 index 0000000000000..ae3b7dbd76291 --- /dev/null +++ b/app/code/Magento/MediaGalleryApi/Api/Data/KeywordInterface.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\MediaGalleryApi\Api\Data; + +use Magento\Framework\Api\ExtensibleDataInterface; + +/** + * Represents a media gallery keyword. This object contains information about a media asset keyword entity. + */ +interface KeywordInterface extends ExtensibleDataInterface +{ + /** + * Get ID + * + * @return int|null + */ + public function getId(): ?int; + + /** + * Get the keyword + * + * @return string + */ + public function getKeyword(): string; + + /** + * Get extension attributes + * + * @return \Magento\MediaGalleryApi\Api\Data\KeywordExtensionInterface|null + */ + public function getExtensionAttributes(): KeywordExtensionInterface; + + /** + * Set extension attributes + * + * @param \Magento\MediaGalleryApi\Api\Data\KeywordExtensionInterface $extensionAttributes + * @return void + */ + public function setExtensionAttributes(KeywordExtensionInterface $extensionAttributes): void; +} diff --git a/app/code/Magento/MediaGalleryApi/LICENSE.txt b/app/code/Magento/MediaGalleryApi/LICENSE.txt new file mode 100644 index 0000000000000..49525fd99da9c --- /dev/null +++ b/app/code/Magento/MediaGalleryApi/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/MediaGalleryApi/LICENSE_AFL.txt b/app/code/Magento/MediaGalleryApi/LICENSE_AFL.txt new file mode 100644 index 0000000000000..f39d641b18a19 --- /dev/null +++ b/app/code/Magento/MediaGalleryApi/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/MediaGalleryApi/Model/Asset/Command/DeleteByPathInterface.php b/app/code/Magento/MediaGalleryApi/Model/Asset/Command/DeleteByPathInterface.php new file mode 100644 index 0000000000000..b3612a67ed536 --- /dev/null +++ b/app/code/Magento/MediaGalleryApi/Model/Asset/Command/DeleteByPathInterface.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\MediaGalleryApi\Model\Asset\Command; + +/** + * A command represents the media gallery asset delete action. A media gallery asset is filtered by path value. + */ +interface DeleteByPathInterface +{ + /** + * Delete media asset by path + * + * @param string $mediaAssetPath + * + * @return void + */ + public function execute(string $mediaAssetPath): void; +} diff --git a/app/code/Magento/MediaGalleryApi/Model/Asset/Command/GetByIdInterface.php b/app/code/Magento/MediaGalleryApi/Model/Asset/Command/GetByIdInterface.php new file mode 100644 index 0000000000000..ef2ceb5ffbfe6 --- /dev/null +++ b/app/code/Magento/MediaGalleryApi/Model/Asset/Command/GetByIdInterface.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\MediaGalleryApi\Model\Asset\Command; + +/** + * A command represents the get media gallery asset by using media gallery asset id as a filter parameter. + */ +interface GetByIdInterface +{ + /** + * Get media asset by id + * + * @param int $mediaAssetId + * + * @return \Magento\MediaGalleryApi\Api\Data\AssetInterface + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws \Magento\Framework\Exception\IntegrationException + */ + public function execute(int $mediaAssetId): \Magento\MediaGalleryApi\Api\Data\AssetInterface; +} diff --git a/app/code/Magento/MediaGalleryApi/Model/Asset/Command/GetByPathInterface.php b/app/code/Magento/MediaGalleryApi/Model/Asset/Command/GetByPathInterface.php new file mode 100644 index 0000000000000..547b0dc695dae --- /dev/null +++ b/app/code/Magento/MediaGalleryApi/Model/Asset/Command/GetByPathInterface.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\MediaGalleryApi\Model\Asset\Command; + +/** + * A command represents the get media gallery asset by using media gallery asset path as a filter parameter. + */ +interface GetByPathInterface +{ + /** + * Get media asset list + * + * @param string $mediaFilePath + * + * @return \Magento\MediaGalleryApi\Api\Data\AssetInterface + */ + public function execute(string $mediaFilePath): \Magento\MediaGalleryApi\Api\Data\AssetInterface; +} diff --git a/app/code/Magento/MediaGalleryApi/Model/Asset/Command/SaveInterface.php b/app/code/Magento/MediaGalleryApi/Model/Asset/Command/SaveInterface.php new file mode 100644 index 0000000000000..b3e3607e6e822 --- /dev/null +++ b/app/code/Magento/MediaGalleryApi/Model/Asset/Command/SaveInterface.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\MediaGalleryApi\Model\Asset\Command; + +use Magento\MediaGalleryApi\Api\Data\AssetInterface; + +/** + * A command which executes the media gallery asset save operation. + */ +interface SaveInterface +{ + /** + * Save media asset + * + * @param \Magento\MediaGalleryApi\Api\Data\AssetInterface $mediaAsset + * + * @return int + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function execute(AssetInterface $mediaAsset): int; +} diff --git a/app/code/Magento/MediaGalleryApi/Model/DataExtractorInterface.php b/app/code/Magento/MediaGalleryApi/Model/DataExtractorInterface.php new file mode 100644 index 0000000000000..6570cd2235412 --- /dev/null +++ b/app/code/Magento/MediaGalleryApi/Model/DataExtractorInterface.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGalleryApi\Model; + +/** + * Extract data from an object using available getters + */ +interface DataExtractorInterface +{ + /** + * Extract data from an object using available getters (does not process extension attributes) + * + * @param object $object + * @param string|null $interface + * @return array + */ + public function extract($object, string $interface = null): array; +} diff --git a/app/code/Magento/MediaGalleryApi/Model/Keyword/Command/GetAssetKeywordsInterface.php b/app/code/Magento/MediaGalleryApi/Model/Keyword/Command/GetAssetKeywordsInterface.php new file mode 100644 index 0000000000000..d449df5684c4b --- /dev/null +++ b/app/code/Magento/MediaGalleryApi/Model/Keyword/Command/GetAssetKeywordsInterface.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGalleryApi\Model\Keyword\Command; + +/** + * A command represents functionality to get a media gallery asset keywords filtered by media gallery asset id. + */ +interface GetAssetKeywordsInterface +{ + /** + * Get asset related keywords. + * + * @param int $assetId + * + * @return \Magento\MediaGalleryApi\Api\Data\KeywordInterface[] + */ + public function execute(int $assetId): array; +} diff --git a/app/code/Magento/MediaGalleryApi/Model/Keyword/Command/SaveAssetKeywordsInterface.php b/app/code/Magento/MediaGalleryApi/Model/Keyword/Command/SaveAssetKeywordsInterface.php new file mode 100644 index 0000000000000..9c0d89c3456f8 --- /dev/null +++ b/app/code/Magento/MediaGalleryApi/Model/Keyword/Command/SaveAssetKeywordsInterface.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGalleryApi\Model\Keyword\Command; + +/** + * A command represents the media gallery asset keywords save operation. + */ +interface SaveAssetKeywordsInterface +{ + /** + * Save asset keywords. + * + * @param \Magento\MediaGalleryApi\Api\Data\KeywordInterface[] $keywords + * @param int $assetId + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function execute(array $keywords, int $assetId): void; +} diff --git a/app/code/Magento/MediaGalleryApi/README.md b/app/code/Magento/MediaGalleryApi/README.md new file mode 100644 index 0000000000000..978a14691597b --- /dev/null +++ b/app/code/Magento/MediaGalleryApi/README.md @@ -0,0 +1,13 @@ +# Magento_MediaGalleryApi module + +The Magento_MediaGalleryApi module serves as application program interface (API) responsible for storing and managing media gallery asset attributes. + +## Extensibility + +Extension developers can interact with the Magento_MediaGallery module. For more information about the Magento extension mechanism, see [Magento plug-ins](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/plugins.html). + +[The Magento dependency injection mechanism](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/depend-inj.html) enables you to override the functionality of the Magento_MediaGalleryApi module. + +## Additional information + +For information about significant changes in patch releases, see [2.3.x Release information](https://devdocs.magento.com/guides/v2.3/release-notes/bk-release-notes.html). diff --git a/app/code/Magento/MediaGalleryApi/composer.json b/app/code/Magento/MediaGalleryApi/composer.json new file mode 100644 index 0000000000000..33ba72c3f98dd --- /dev/null +++ b/app/code/Magento/MediaGalleryApi/composer.json @@ -0,0 +1,21 @@ +{ + "name": "magento/module-media-gallery-api", + "description": "Magento module responsible for media gallery asset attributes storage and management", + "require": { + "php": "~7.1.3||~7.2.0||~7.3.0", + "magento/framework": "*" + }, + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\MediaGalleryApi\\": "" + } + } +} diff --git a/app/code/Magento/MediaGalleryApi/etc/module.xml b/app/code/Magento/MediaGalleryApi/etc/module.xml new file mode 100644 index 0000000000000..36da50a22b8bc --- /dev/null +++ b/app/code/Magento/MediaGalleryApi/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_MediaGalleryApi" /> +</config> diff --git a/app/code/Magento/MediaGalleryApi/registration.php b/app/code/Magento/MediaGalleryApi/registration.php new file mode 100644 index 0000000000000..11b0200b46e30 --- /dev/null +++ b/app/code/Magento/MediaGalleryApi/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_MediaGalleryApi', __DIR__); diff --git a/app/code/Magento/MediaStorage/App/Media.php b/app/code/Magento/MediaStorage/App/Media.php index e1644ebaf5a48..15bf7bb62e970 100644 --- a/app/code/Magento/MediaStorage/App/Media.php +++ b/app/code/Magento/MediaStorage/App/Media.php @@ -5,22 +5,31 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\MediaStorage\App; +use Closure; +use Exception; +use LogicException; use Magento\Catalog\Model\View\Asset\PlaceholderFactory; +use Magento\Framework\App; +use Magento\Framework\App\Area; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\ResponseInterface; use Magento\Framework\App\State; +use Magento\Framework\AppInterface; use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\MediaStorage\Model\File\Storage\Config; use Magento\MediaStorage\Model\File\Storage\ConfigFactory; use Magento\MediaStorage\Model\File\Storage\Response; -use Magento\Framework\App; -use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\Framework\AppInterface; +use Magento\MediaStorage\Model\File\Storage\Synchronization; use Magento\MediaStorage\Model\File\Storage\SynchronizationFactory; -use Magento\Framework\App\Area; -use Magento\MediaStorage\Model\File\Storage\Config; use Magento\MediaStorage\Service\ImageResize; /** + * Media Storage + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Media implements AppInterface @@ -28,7 +37,7 @@ class Media implements AppInterface /** * Authorization function * - * @var \Closure + * @var Closure */ private $isAllowed; @@ -59,7 +68,7 @@ class Media implements AppInterface private $response; /** - * @var \Magento\Framework\Filesystem\Directory\WriteInterface + * @var WriteInterface */ private $directory; @@ -92,7 +101,7 @@ class Media implements AppInterface * @param ConfigFactory $configFactory * @param SynchronizationFactory $syncFactory * @param Response $response - * @param \Closure $isAllowed + * @param Closure $isAllowed * @param string $mediaDirectory * @param string $configCacheFile * @param string $relativeFileName @@ -106,7 +115,7 @@ public function __construct( ConfigFactory $configFactory, SynchronizationFactory $syncFactory, Response $response, - \Closure $isAllowed, + Closure $isAllowed, $mediaDirectory, $configCacheFile, $relativeFileName, @@ -120,6 +129,7 @@ public function __construct( $this->directory = $filesystem->getDirectoryWrite(DirectoryList::PUB); $mediaDirectory = trim($mediaDirectory); if (!empty($mediaDirectory)) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction $this->mediaDirectoryPath = str_replace('\\', '/', realpath($mediaDirectory)); } $this->configCacheFile = $configCacheFile; @@ -135,9 +145,9 @@ public function __construct( * Run application * * @return Response - * @throws \LogicException + * @throws LogicException */ - public function launch() + public function launch(): ResponseInterface { $this->appState->setAreaCode(Area::AREA_GLOBAL); @@ -150,12 +160,12 @@ public function launch() $allowedResources = $config->getAllowedResources(); $isAllowed = $this->isAllowed; if (!$isAllowed($this->relativeFileName, $allowedResources)) { - throw new \LogicException('The specified path is not allowed.'); + throw new LogicException('The path is not allowed: ' . $this->relativeFileName); } } try { - /** @var \Magento\MediaStorage\Model\File\Storage\Synchronization $sync */ + /** @var Synchronization $sync */ $sync = $this->syncFactory->create(['directory' => $this->directory]); $sync->synchronize($this->relativeFileName); $this->imageResize->resizeFromImageName($this->getOriginalImage($this->relativeFileName)); @@ -164,14 +174,19 @@ public function launch() } else { $this->setPlaceholderImage(); } - } catch (\Exception $e) { + } catch (Exception $e) { $this->setPlaceholderImage(); } return $this->response; } - private function setPlaceholderImage() + /** + * Set Placeholder as a response + * + * @return void + */ + private function setPlaceholderImage(): void { $placeholder = $this->placeholderFactory->create(['type' => 'image']); $this->response->setFilePath($placeholder->getPath()); @@ -189,9 +204,9 @@ private function getOriginalImage(string $resizedImagePath): string } /** - * {@inheritdoc} + * @inheritdoc */ - public function catchException(App\Bootstrap $bootstrap, \Exception $exception) + public function catchException(App\Bootstrap $bootstrap, Exception $exception) { $this->response->setHttpResponseCode(404); if ($bootstrap->isDeveloperMode()) { diff --git a/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php b/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php index ba12d60cb0bc8..4ed84829c2ad0 100644 --- a/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php +++ b/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php @@ -8,26 +8,37 @@ namespace Magento\MediaStorage\Console\Command; use Magento\Framework\App\Area; -use Magento\Framework\App\ObjectManager; use Magento\Framework\App\State; -use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Console\Cli; use Magento\MediaStorage\Service\ImageResize; +use Magento\MediaStorage\Service\ImageResizeScheduler; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Helper\ProgressBarFactory; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Command\Command; +use Magento\Catalog\Model\ResourceModel\Product\Image as ProductImage; /** * Resizes product images according to theme view definitions. - * - * @package Magento\MediaStorage\Console\Command */ -class ImagesResizeCommand extends \Symfony\Component\Console\Command\Command +class ImagesResizeCommand extends Command { + /** + * Asynchronous image resize mode + */ + const ASYNC_RESIZE = 'async'; + + /** + * @var ImageResizeScheduler + */ + private $imageResizeScheduler; + /** * @var ImageResize */ - private $resize; + private $imageResize; /** * @var State @@ -39,24 +50,32 @@ class ImagesResizeCommand extends \Symfony\Component\Console\Command\Command */ private $progressBarFactory; + /** + * @var ProductImage + */ + private $productImage; + /** * @param State $appState - * @param ImageResize $resize - * @param ObjectManagerInterface $objectManager + * @param ImageResize $imageResize + * @param ImageResizeScheduler $imageResizeScheduler * @param ProgressBarFactory $progressBarFactory + * @param ProductImage $productImage * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( State $appState, - ImageResize $resize, - ObjectManagerInterface $objectManager, - ProgressBarFactory $progressBarFactory = null + ImageResize $imageResize, + ImageResizeScheduler $imageResizeScheduler, + ProgressBarFactory $progressBarFactory, + ProductImage $productImage ) { parent::__construct(); - $this->resize = $resize; $this->appState = $appState; - $this->progressBarFactory = $progressBarFactory - ?: ObjectManager::getInstance()->get(ProgressBarFactory::class); + $this->imageResize = $imageResize; + $this->imageResizeScheduler = $imageResizeScheduler; + $this->progressBarFactory = $progressBarFactory; + $this->productImage = $productImage; } /** @@ -65,7 +84,25 @@ public function __construct( protected function configure() { $this->setName('catalog:images:resize') - ->setDescription('Creates resized product images'); + ->setDescription('Creates resized product images') + ->setDefinition($this->getOptionsList()); + } + + /** + * Image resize command options list + * + * @return array + */ + private function getOptionsList() : array + { + return [ + new InputOption( + self::ASYNC_RESIZE, + 'a', + InputOption::VALUE_NONE, + 'Resize image in asynchronous mode' + ), + ]; } /** @@ -74,11 +111,25 @@ protected function configure() * @param OutputInterface $output */ protected function execute(InputInterface $input, OutputInterface $output) + { + $result = $input->getOption(self::ASYNC_RESIZE) ? + $this->executeAsync($output) : $this->executeSync($output); + + return $result; + } + + /** + * Run resize in synchronous mode + * + * @param OutputInterface $output + * @return int + */ + private function executeSync(OutputInterface $output): int { try { $errors = []; $this->appState->setAreaCode(Area::AREA_GLOBAL); - $generator = $this->resize->resizeFromThemes(); + $generator = $this->imageResize->resizeFromThemes(); /** @var ProgressBar $progress */ $progress = $this->progressBarFactory->create( @@ -111,7 +162,7 @@ protected function execute(InputInterface $input, OutputInterface $output) } catch (\Exception $e) { $output->writeln("<error>{$e->getMessage()}</error>"); // we must have an exit code higher than zero to indicate something was wrong - return \Magento\Framework\Console\Cli::RETURN_FAILURE; + return Cli::RETURN_FAILURE; } $output->write(PHP_EOL); @@ -124,6 +175,62 @@ protected function execute(InputInterface $input, OutputInterface $output) $output->writeln("<info>Product images resized successfully</info>"); } - return \Magento\Framework\Console\Cli::RETURN_SUCCESS; + return Cli::RETURN_SUCCESS; + } + + /** + * Schedule asynchronous image resizing + * + * @param OutputInterface $output + * @return int + */ + private function executeAsync(OutputInterface $output): int + { + try { + $errors = []; + $this->appState->setAreaCode(Area::AREA_GLOBAL); + + /** @var ProgressBar $progress */ + $progress = $this->progressBarFactory->create( + [ + 'output' => $output, + 'max' => $this->productImage->getCountUsedProductImages() + ] + ); + $progress->setFormat( + "%current%/%max% [%bar%] %percent:3s%% %elapsed% %memory:6s% \t| <info>%message%</info>" + ); + + if ($output->getVerbosity() !== OutputInterface::VERBOSITY_NORMAL) { + $progress->setOverwrite(false); + } + + $productImages = $this->productImage->getUsedProductImages(); + foreach ($productImages as $image) { + $result = $this->imageResizeScheduler->schedule($image['filepath']); + + if (!$result) { + $errors[$image['filepath']] = 'Error image scheduling: ' . $image['filepath']; + } + $progress->setMessage($image['filepath']); + $progress->advance(); + } + } catch (\Exception $e) { + $output->writeln("<error>{$e->getMessage()}</error>"); + // we must have an exit code higher than zero to indicate something was wrong + return Cli::RETURN_FAILURE; + } + + $output->write(PHP_EOL); + if (count($errors)) { + $output->writeln("<info>Product images resized with errors:</info>"); + foreach ($errors as $error) { + $output->writeln("<error>{$error}</error>"); + } + } else { + $output->writeln("<info>Product images scheduled successfully</info>"); + } + + return Cli::RETURN_SUCCESS; } } diff --git a/app/code/Magento/MediaStorage/Model/ConsumerImageResize.php b/app/code/Magento/MediaStorage/Model/ConsumerImageResize.php new file mode 100644 index 0000000000000..43f3e1d7767ce --- /dev/null +++ b/app/code/Magento/MediaStorage/Model/ConsumerImageResize.php @@ -0,0 +1,91 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaStorage\Model; + +use Magento\AsynchronousOperations\Api\Data\OperationInterface; +use Magento\Framework\Serialize\SerializerInterface; +use Psr\Log\LoggerInterface; +use Magento\MediaStorage\Service\ImageResize; +use Magento\Framework\EntityManager\EntityManager; +use Magento\Framework\Exception\NotFoundException; + +/** + * Consumer for image resize + */ +class ConsumerImageResize +{ + /** + * @var SerializerInterface + */ + private $serializer; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @var ImageResize + */ + private $resize; + + /** + * @var EntityManager + */ + private $entityManager; + + /** + * @param ImageResize $resize + * @param LoggerInterface $logger + * @param SerializerInterface $serializer + * @param EntityManager $entityManager + */ + public function __construct( + ImageResize $resize, + LoggerInterface $logger, + SerializerInterface $serializer, + EntityManager $entityManager + ) { + $this->resize = $resize; + $this->logger = $logger; + $this->serializer = $serializer; + $this->entityManager = $entityManager; + } + + /** + * Image resize + * + * @param OperationInterface $operation + * @return void + * @throws \Exception + */ + public function process(OperationInterface $operation): void + { + try { + $serializedData = $operation->getSerializedData(); + $data = $this->serializer->unserialize($serializedData); + $this->resize->resizeFromImageName($data['filename']); + } catch (NotFoundException $e) { + $this->logger->critical($e->getMessage()); + $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; + $errorCode = $e->getCode(); + $message = $e->getMessage(); + } catch (\Exception $e) { + $this->logger->critical($e->getMessage()); + $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; + $errorCode = $e->getCode(); + $message = __('Sorry, something went wrong during image resize. Please see log for details.'); + } + + $operation->setStatus($status ?? OperationInterface::STATUS_TYPE_COMPLETE) + ->setErrorCode($errorCode ?? null) + ->setResultMessage($message ?? null); + + $this->entityManager->save($operation); + } +} diff --git a/app/code/Magento/MediaStorage/Service/ImageResize.php b/app/code/Magento/MediaStorage/Service/ImageResize.php index 63353b2536a5a..d061ddbd3dc46 100644 --- a/app/code/Magento/MediaStorage/Service/ImageResize.php +++ b/app/code/Magento/MediaStorage/Service/ImageResize.php @@ -311,6 +311,10 @@ private function resize(array $imageParams, string $originalImagePath, string $o ] ); + if ($imageParams['image_width'] !== null && $imageParams['image_height'] !== null) { + $image->resize($imageParams['image_width'], $imageParams['image_height']); + } + if (isset($imageParams['watermark_file'])) { if ($imageParams['watermark_height'] !== null) { $image->setWatermarkHeight($imageParams['watermark_height']); @@ -331,9 +335,6 @@ private function resize(array $imageParams, string $originalImagePath, string $o $image->watermark($this->getWatermarkFilePath($imageParams['watermark_file'])); } - if ($imageParams['image_width'] !== null && $imageParams['image_height'] !== null) { - $image->resize($imageParams['image_width'], $imageParams['image_height']); - } $image->save($imageAsset->getPath()); if ($this->fileStorageDatabase->checkDbUsage()) { diff --git a/app/code/Magento/MediaStorage/Service/ImageResizeScheduler.php b/app/code/Magento/MediaStorage/Service/ImageResizeScheduler.php new file mode 100644 index 0000000000000..900bb026dc5b3 --- /dev/null +++ b/app/code/Magento/MediaStorage/Service/ImageResizeScheduler.php @@ -0,0 +1,97 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaStorage\Service; + +use Magento\Framework\Bulk\BulkManagementInterface; +use Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory; +use Magento\Framework\DataObject\IdentityGeneratorInterface; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\Bulk\OperationInterface; +use Magento\Authorization\Model\UserContextInterface; + +/** + * Scheduler for image resize queue + */ +class ImageResizeScheduler +{ + /** + * @var BulkManagementInterface + */ + private $bulkManagement; + + /** + * @var OperationInterfaceFactory + */ + private $operationFactory; + + /** + * @var IdentityGeneratorInterface + */ + private $identityService; + + /** + * @var SerializerInterface + */ + private $serializer; + + /** + * @var UserContextInterface + */ + private $userContext; + + /** + * @param BulkManagementInterface $bulkManagement + * @param OperationInterfaceFactory $operartionFactory + * @param IdentityGeneratorInterface $identityService + * @param SerializerInterface $serializer + * @param UserContextInterface $userContext + */ + public function __construct( + BulkManagementInterface $bulkManagement, + OperationInterfaceFactory $operartionFactory, + IdentityGeneratorInterface $identityService, + SerializerInterface $serializer, + UserContextInterface $userContext + ) { + $this->bulkManagement = $bulkManagement; + $this->operationFactory = $operartionFactory; + $this->identityService = $identityService; + $this->serializer = $serializer; + $this->userContext = $userContext; + } + + /** + * Schedule image resize based on original image. + * + * @param string $imageName + * @return boolean + */ + public function schedule(string $imageName): bool + { + $bulkUuid = $this->identityService->generateId(); + $bulkDescription = __('Image resize: %1', $imageName); + $dataToEncode = ['filename' => $imageName]; + + $data = [ + 'data' => [ + 'bulk_uuid' => $bulkUuid, + 'topic_name' => 'media.storage.catalog.image.resize', + 'serialized_data' => $this->serializer->serialize($dataToEncode), + 'status' => OperationInterface::STATUS_TYPE_OPEN, + ] + ]; + $operation = $this->operationFactory->create($data); + + return $this->bulkManagement->scheduleBulk( + $bulkUuid, + [$operation], + $bulkDescription, + $this->userContext->getUserId() + ); + } +} diff --git a/app/code/Magento/MediaStorage/Test/Unit/App/MediaTest.php b/app/code/Magento/MediaStorage/Test/Unit/App/MediaTest.php index 90075d11c6af3..8d3211684d377 100644 --- a/app/code/Magento/MediaStorage/Test/Unit/App/MediaTest.php +++ b/app/code/Magento/MediaStorage/Test/Unit/App/MediaTest.php @@ -3,145 +3,118 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\MediaStorage\Test\Unit\App; +use Exception; +use LogicException; use Magento\Catalog\Model\View\Asset\Placeholder; use Magento\Catalog\Model\View\Asset\PlaceholderFactory; +use Magento\Framework\App\Bootstrap; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\Read; +use Magento\Framework\Filesystem\Directory\WriteInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\MediaStorage\App\Media; +use Magento\MediaStorage\Model\File\Storage\Config; +use Magento\MediaStorage\Model\File\Storage\ConfigFactory; +use Magento\MediaStorage\Model\File\Storage\Response; +use Magento\MediaStorage\Model\File\Storage\Synchronization; +use Magento\MediaStorage\Model\File\Storage\SynchronizationFactory; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** - * Class MediaTest + * Verification for Media class + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class MediaTest extends \PHPUnit\Framework\TestCase +class MediaTest extends TestCase { const MEDIA_DIRECTORY = 'mediaDirectory'; const RELATIVE_FILE_PATH = 'test/file.png'; const CACHE_FILE_PATH = 'var'; /** - * @var \Magento\MediaStorage\App\Media + * @var Media */ - private $model; + private $mediaModel; /** - * @var \Magento\MediaStorage\Model\File\Storage\ConfigFactory|\PHPUnit_Framework_MockObject_MockObject + * @var ConfigFactory|MockObject */ private $configFactoryMock; /** - * @var \Magento\MediaStorage\Model\File\Storage\SynchronizationFactory|\PHPUnit_Framework_MockObject_MockObject + * @var SynchronizationFactory|MockObject */ private $syncFactoryMock; /** - * @var callable - */ - private $closure; - - /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Config|MockObject */ private $configMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var Synchronization|MockObject */ private $sync; /** - * @var \Magento\MediaStorage\Model\File\Storage\Response|\PHPUnit_Framework_MockObject_MockObject + * @var Response|MockObject */ private $responseMock; /** - * @var \Magento\Framework\Filesystem|\PHPUnit_Framework_MockObject_MockObject + * @var Filesystem|MockObject */ private $filesystemMock; /** - * @var \Magento\Framework\Filesystem\Directory\Read|\PHPUnit_Framework_MockObject_MockObject + * @var Read|MockObject */ private $directoryMock; protected function setUp() { - $this->closure = function () { - return true; - }; - $this->configMock = $this->createMock(\Magento\MediaStorage\Model\File\Storage\Config::class); - $this->sync = $this->createMock(\Magento\MediaStorage\Model\File\Storage\Synchronization::class); + $this->configMock = $this->createMock(Config::class); + $this->sync = $this->createMock(Synchronization::class); $this->configFactoryMock = $this->createPartialMock( - \Magento\MediaStorage\Model\File\Storage\ConfigFactory::class, + ConfigFactory::class, ['create'] ); $this->configFactoryMock->expects($this->any()) ->method('create') ->will($this->returnValue($this->configMock)); $this->syncFactoryMock = $this->createPartialMock( - \Magento\MediaStorage\Model\File\Storage\SynchronizationFactory::class, + SynchronizationFactory::class, ['create'] ); $this->syncFactoryMock->expects($this->any()) ->method('create') ->will($this->returnValue($this->sync)); - $this->filesystemMock = $this->createMock(\Magento\Framework\Filesystem::class); - $this->directoryMock = $this->getMockForAbstractClass( - \Magento\Framework\Filesystem\Directory\WriteInterface::class - ); + $this->filesystemMock = $this->createMock(Filesystem::class); + $this->directoryMock = $this->getMockForAbstractClass(WriteInterface::class); $this->filesystemMock->expects($this->any()) ->method('getDirectoryWrite') ->with(DirectoryList::PUB) ->will($this->returnValue($this->directoryMock)); - $this->responseMock = $this->createMock(\Magento\MediaStorage\Model\File\Storage\Response::class); - - $objectManager = new ObjectManager($this); - $this->model = $objectManager->getObject( - \Magento\MediaStorage\App\Media::class, - [ - 'configFactory' => $this->configFactoryMock, - 'syncFactory' => $this->syncFactoryMock, - 'response' => $this->responseMock, - 'isAllowed' => $this->closure, - 'mediaDirectory' => false, - 'configCacheFile' => self::CACHE_FILE_PATH, - 'relativeFileName' => self::RELATIVE_FILE_PATH, - 'filesystem' => $this->filesystemMock, - 'placeholderFactory' => $this->createConfiguredMock( - PlaceholderFactory::class, - [ - 'create' => $this->createMock(Placeholder::class) - ] - ), - ] - ); + $this->responseMock = $this->createMock(Response::class); } protected function tearDown() { - unset($this->model); + unset($this->mediaModel); } public function testProcessRequestCreatesConfigFileMediaDirectoryIsNotProvided() { - $objectManager = new ObjectManager($this); - $this->model = $objectManager->getObject( - \Magento\MediaStorage\App\Media::class, - [ - 'configFactory' => $this->configFactoryMock, - 'syncFactory' => $this->syncFactoryMock, - 'response' => $this->responseMock, - 'isAllowed' => $this->closure, - 'mediaDirectory' => false, - 'configCacheFile' => self::CACHE_FILE_PATH, - 'relativeFileName' => self::RELATIVE_FILE_PATH, - 'filesystem' => $this->filesystemMock - ] - ); + $this->mediaModel = $this->getMediaModel(); + $filePath = '/absolute/path/to/test/file.png'; $this->directoryMock->expects($this->any()) ->method('getAbsolutePath') @@ -158,11 +131,13 @@ public function testProcessRequestCreatesConfigFileMediaDirectoryIsNotProvided() ->with(self::RELATIVE_FILE_PATH) ->will($this->returnValue(true)); $this->responseMock->expects($this->once())->method('setFilePath')->with($filePath); - $this->model->launch(); + $this->mediaModel->launch(); } public function testProcessRequestReturnsFileIfItsProperlySynchronized() { + $this->mediaModel = $this->getMediaModel(); + $filePath = '/absolute/path/to/test/file.png'; $this->sync->expects($this->once())->method('synchronize')->with(self::RELATIVE_FILE_PATH); $this->directoryMock->expects($this->once()) @@ -178,11 +153,13 @@ public function testProcessRequestReturnsFileIfItsProperlySynchronized() ] )); $this->responseMock->expects($this->once())->method('setFilePath')->with($filePath); - $this->assertSame($this->responseMock, $this->model->launch()); + $this->assertSame($this->responseMock, $this->mediaModel->launch()); } public function testProcessRequestReturnsNotFoundIfFileIsNotSynchronized() { + $this->mediaModel = $this->getMediaModel(); + $this->sync->expects($this->once())->method('synchronize')->with(self::RELATIVE_FILE_PATH); $this->directoryMock->expects($this->once()) ->method('getAbsolutePath') @@ -192,7 +169,7 @@ public function testProcessRequestReturnsNotFoundIfFileIsNotSynchronized() ->method('isReadable') ->with(self::RELATIVE_FILE_PATH) ->will($this->returnValue(false)); - $this->assertSame($this->responseMock, $this->model->launch()); + $this->assertSame($this->responseMock, $this->mediaModel->launch()); } /** @@ -203,8 +180,12 @@ public function testProcessRequestReturnsNotFoundIfFileIsNotSynchronized() */ public function testCatchException($isDeveloper, $setBodyCalls) { - $bootstrap = $this->createMock(\Magento\Framework\App\Bootstrap::class); - $exception = $this->createMock(\Exception::class); + /** @var Bootstrap|MockObject $bootstrap */ + $bootstrap = $this->createMock(Bootstrap::class); + + /** @var Exception|MockObject $exception */ + $exception = $this->createMock(Exception::class); + $this->responseMock->expects($this->once()) ->method('setHttpResponseCode') ->with(404); @@ -215,7 +196,31 @@ public function testCatchException($isDeveloper, $setBodyCalls) ->method('setBody'); $this->responseMock->expects($this->once()) ->method('sendResponse'); - $this->model->catchException($bootstrap, $exception); + + $this->mediaModel = $this->getMediaModel(); + + $this->mediaModel->catchException($bootstrap, $exception); + } + + public function testExceptionWhenIsAllowedReturnsFalse() + { + $this->mediaModel = $this->getMediaModel(false); + + $filePath = '/absolute/path/to/test/file.png'; + $this->directoryMock->expects($this->any()) + ->method('getAbsolutePath') + ->will($this->returnValueMap( + [ + [null, self::MEDIA_DIRECTORY], + [self::RELATIVE_FILE_PATH, $filePath], + ] + )); + $this->configMock->expects($this->once())->method('save'); + + $this->expectException(LogicException::class); + $this->expectExceptionMessage('The path is not allowed: ' . self::RELATIVE_FILE_PATH); + + $this->mediaModel->launch(); } /** @@ -228,4 +233,42 @@ public function catchExceptionDataProvider() 'developer mode' => [true, 1], ]; } + + /** + * Generates Media class instance for test + * + * @param bool $isAllowed + * @return Media + */ + protected function getMediaModel(bool $isAllowed = true): Media + { + $objectManager = new ObjectManager($this); + + $isAllowedCallback = function () use ($isAllowed) { + return $isAllowed; + }; + + /** @var Media $mediaClass */ + $mediaClass = $objectManager->getObject( + Media::class, + [ + 'configFactory' => $this->configFactoryMock, + 'syncFactory' => $this->syncFactoryMock, + 'response' => $this->responseMock, + 'isAllowed' => $isAllowedCallback, + 'mediaDirectory' => false, + 'configCacheFile' => self::CACHE_FILE_PATH, + 'relativeFileName' => self::RELATIVE_FILE_PATH, + 'filesystem' => $this->filesystemMock, + 'placeholderFactory' => $this->createConfiguredMock( + PlaceholderFactory::class, + [ + 'create' => $this->createMock(Placeholder::class) + ] + ), + ] + ); + + return $mediaClass; + } } diff --git a/app/code/Magento/MediaStorage/composer.json b/app/code/Magento/MediaStorage/composer.json index 95c48f3fdc581..ce97eec97f7c3 100644 --- a/app/code/Magento/MediaStorage/composer.json +++ b/app/code/Magento/MediaStorage/composer.json @@ -11,7 +11,9 @@ "magento/module-config": "*", "magento/module-store": "*", "magento/module-catalog": "*", - "magento/module-theme": "*" + "magento/module-theme": "*", + "magento/module-asynchronous-operations": "*", + "magento/module-authorization": "*" }, "type": "magento2-module", "license": [ diff --git a/app/code/Magento/MediaStorage/etc/communication.xml b/app/code/Magento/MediaStorage/etc/communication.xml new file mode 100644 index 0000000000000..c9630b24ba336 --- /dev/null +++ b/app/code/Magento/MediaStorage/etc/communication.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Communication/etc/communication.xsd"> + <topic name="media.storage.catalog.image.resize" request="Magento\AsynchronousOperations\Api\Data\OperationInterface"> + <handler name="media.storage.catalog.image.resize" type="Magento\MediaStorage\Model\ConsumerImageResize" method="process" /> + </topic> +</config> diff --git a/app/code/Magento/MediaStorage/etc/di.xml b/app/code/Magento/MediaStorage/etc/di.xml index 2b9317787463d..5cdcbb3b2b9a9 100644 --- a/app/code/Magento/MediaStorage/etc/di.xml +++ b/app/code/Magento/MediaStorage/etc/di.xml @@ -26,4 +26,9 @@ </argument> </arguments> </type> + <type name="Magento\MediaStorage\Console\Command\ImagesResizeCommand"> + <arguments> + <argument name="imageResizeScheduler" xsi:type="object">Magento\MediaStorage\Service\ImageResizeScheduler\Proxy</argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/MediaStorage/etc/queue.xml b/app/code/Magento/MediaStorage/etc/queue.xml new file mode 100644 index 0000000000000..3e4ffbf8ec9e2 --- /dev/null +++ b/app/code/Magento/MediaStorage/etc/queue.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/queue.xsd"> + <broker topic="media.storage.catalog.image.resize" exchange="magento-db" type="db"> + <queue name="media.storage.catalog.image.resize" consumer="media.storage.catalog.image.resize" consumerInstance="Magento\Framework\MessageQueue\Consumer" handler="Magento\MediaStorage\Model\ConsumerImageResize::process" /> + </broker> +</config> diff --git a/app/code/Magento/MediaStorage/etc/queue_consumer.xml b/app/code/Magento/MediaStorage/etc/queue_consumer.xml new file mode 100644 index 0000000000000..e4c06a47f314e --- /dev/null +++ b/app/code/Magento/MediaStorage/etc/queue_consumer.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-message-queue:etc/consumer.xsd"> + <consumer name="media.storage.catalog.image.resize" queue="media.storage.catalog.image.resize" connection="db" maxMessages="100" consumerInstance="Magento\Framework\MessageQueue\Consumer" handler="Magento\MediaStorage\Model\ConsumerImageResize::process" /> +</config> diff --git a/app/code/Magento/MediaStorage/etc/queue_publisher.xml b/app/code/Magento/MediaStorage/etc/queue_publisher.xml new file mode 100644 index 0000000000000..dab99e2c307f8 --- /dev/null +++ b/app/code/Magento/MediaStorage/etc/queue_publisher.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/publisher.xsd"> + <publisher topic="media.storage.catalog.image.resize"> + <connection name="db" exchange="magento-db" /> + </publisher> +</config> diff --git a/app/code/Magento/MediaStorage/etc/queue_topology.xml b/app/code/Magento/MediaStorage/etc/queue_topology.xml new file mode 100644 index 0000000000000..9bb1ca5cef90e --- /dev/null +++ b/app/code/Magento/MediaStorage/etc/queue_topology.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/topology.xsd"> + <exchange name="magento-db" type="topic" connection="db"> + <binding id="imageResizeBinding" topic="media.storage.catalog.image.resize" destinationType="queue" destination="media.storage.catalog.image.resize"/> + </exchange> +</config> diff --git a/app/code/Magento/MediaStorage/registration.php b/app/code/Magento/MediaStorage/registration.php index 8bfa83028eeeb..552f668426125 100644 --- a/app/code/Magento/MediaStorage/registration.php +++ b/app/code/Magento/MediaStorage/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_MediaStorage', __DIR__); diff --git a/app/code/Magento/MessageQueue/Test/Unit/Model/ConsumerRunnerTest.php b/app/code/Magento/MessageQueue/Test/Unit/Model/ConsumerRunnerTest.php index 46a142bf488be..5f4bd9178a890 100644 --- a/app/code/Magento/MessageQueue/Test/Unit/Model/ConsumerRunnerTest.php +++ b/app/code/Magento/MessageQueue/Test/Unit/Model/ConsumerRunnerTest.php @@ -3,20 +3,27 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\MessageQueue\Test\Unit\Model; -use Magento\Framework\MessageQueue\ConsumerInterface; -use Magento\MessageQueue\Model\ConsumerRunner; +use Magento\Framework\App\MaintenanceMode; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\MessageQueue\ConsumerFactory; +use Magento\Framework\MessageQueue\ConsumerInterface; use Magento\Framework\Phrase; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\MessageQueue\Model\ConsumerRunner; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -/** - * Unit tests for consumer runner - */ -class ConsumerRunnerTest extends \PHPUnit\Framework\TestCase +class ConsumerRunnerTest extends TestCase { - /** @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */ + const STUB_SLEEP_INTERVAL = 0; + + /** + * @var ObjectManager + */ private $objectManager; /** @@ -25,12 +32,12 @@ class ConsumerRunnerTest extends \PHPUnit\Framework\TestCase private $consumerRunner; /** - * @var \Magento\Framework\MessageQueue\ConsumerFactory|\PHPUnit_Framework_MockObject_MockObject + * @var ConsumerFactory|MockObject */ private $consumerFactoryMock; /** - * @var \Magento\Framework\App\MaintenanceMode|\PHPUnit_Framework_MockObject_MockObject + * @var MaintenanceMode|MockObject */ private $maintenanceModeMock; @@ -39,21 +46,23 @@ class ConsumerRunnerTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->consumerFactoryMock = $this->getMockBuilder(\Magento\Framework\MessageQueue\ConsumerFactory::class) + $this->objectManager = new ObjectManager($this); + + $this->consumerFactoryMock = $this->getMockBuilder(ConsumerFactory::class) ->disableOriginalConstructor() ->getMock(); - $this->maintenanceModeMock = $this->getMockBuilder(\Magento\Framework\App\MaintenanceMode::class) + $this->maintenanceModeMock = $this->getMockBuilder(MaintenanceMode::class) ->disableOriginalConstructor() ->getMock(); + $this->consumerRunner = $this->objectManager->getObject( - \Magento\MessageQueue\Model\ConsumerRunner::class, + ConsumerRunner::class, [ 'consumerFactory' => $this->consumerFactoryMock, - 'maintenanceMode' => $this->maintenanceModeMock + 'maintenanceMode' => $this->maintenanceModeMock, + 'maintenanceSleepInterval' => self::STUB_SLEEP_INTERVAL ] ); - parent::setUp(); } /** @@ -64,8 +73,8 @@ protected function setUp() public function testMagicMethod() { $isMaintenanceModeOn = false; - /** @var ConsumerInterface|\PHPUnit_Framework_MockObject_MockObject $consumerMock */ - $consumerMock = $this->getMockBuilder(\Magento\Framework\MessageQueue\ConsumerInterface::class)->getMock(); + /** @var ConsumerInterface|MockObject $consumerMock */ + $consumerMock = $this->getMockBuilder(ConsumerInterface::class)->getMock(); $consumerMock->expects($this->once())->method('process'); $consumerName = 'someConsumerName'; $this->consumerFactoryMock @@ -81,12 +90,13 @@ public function testMagicMethod() /** * Ensure that exception will be thrown if requested magic method does not correspond to any declared consumer. * - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage "nonDeclaredConsumer" callback method specified in crontab.xml must * @return void */ public function testMagicMethodNoRelatedConsumer() { + $this->expectException(LocalizedException::class); + $this->expectExceptionMessage('"nonDeclaredConsumer" callback method specified in crontab.xml must'); + $consumerName = 'nonDeclaredConsumer'; $this->consumerFactoryMock ->expects($this->once()) @@ -105,8 +115,9 @@ public function testMagicMethodNoRelatedConsumer() public function testMagicMethodMaintenanceModeIsOn() { $isMaintenanceModeOn = true; - /** @var ConsumerInterface|\PHPUnit_Framework_MockObject_MockObject $consumerMock */ - $consumerMock = $this->getMockBuilder(\Magento\Framework\MessageQueue\ConsumerInterface::class)->getMock(); + + /** @var ConsumerInterface|MockObject $consumerMock */ + $consumerMock = $this->getMockBuilder(ConsumerInterface::class)->getMock(); $consumerMock->expects($this->never())->method('process'); $consumerName = 'someConsumerName'; $this->consumerFactoryMock diff --git a/app/code/Magento/MessageQueue/registration.php b/app/code/Magento/MessageQueue/registration.php index e27fa71517427..5608dbce482a9 100644 --- a/app/code/Magento/MessageQueue/registration.php +++ b/app/code/Magento/MessageQueue/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_MessageQueue', __DIR__); diff --git a/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml b/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml index d1eb1339fe856..ccb724e8bf199 100644 --- a/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml +++ b/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml @@ -114,7 +114,7 @@ <waitForElement selector="{{AdminProductFormAdvancedPricingSection.msrp}}" stepKey="waitForMsrp"/> <fillField selector="{{AdminProductFormAdvancedPricingSection.msrp}}" userInput="55" stepKey="setMsrpForFirstChildProduct"/> <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct1"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1"/> <amOnPage url="{{AdminProductEditPage.url($$createConfigChildProduct2.id$$)}}" stepKey="goToSecondChildProductEditPage"/> <waitForPageLoad stepKey="waitForProductPageLoad1"/> @@ -122,7 +122,7 @@ <waitForElement selector="{{AdminProductFormAdvancedPricingSection.msrp}}" stepKey="waitForMsrp1"/> <fillField selector="{{AdminProductFormAdvancedPricingSection.msrp}}" userInput="66" stepKey="setMsrpForSecondChildProduct"/> <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton1"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!--Clear cache--> <magentoCLI command="cache:flush" stepKey="flushCache"/> diff --git a/app/code/Magento/Msrp/Test/Unit/Pricing/MsrpPriceCalculatorTest.php b/app/code/Magento/Msrp/Test/Unit/Pricing/MsrpPriceCalculatorTest.php new file mode 100644 index 0000000000000..24e10207ff14c --- /dev/null +++ b/app/code/Magento/Msrp/Test/Unit/Pricing/MsrpPriceCalculatorTest.php @@ -0,0 +1,103 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Msrp\Test\Unit\Pricing; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Type as ProductType; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\GroupedProduct\Model\Product\Type\Grouped as GroupedType; +use Magento\Msrp\Pricing\MsrpPriceCalculator; +use Magento\MsrpGroupedProduct\Pricing\MsrpPriceCalculator as MsrpGroupedCalculator; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class MsrpPriceCalculatorTest extends TestCase +{ + /** + * @var MsrpPriceCalculator + */ + private $pricing; + + /** + * @var MsrpGroupedCalculator|MockObject + */ + private $msrpGroupedCalculatorMock; + + /** + * Prepare environment to test + */ + protected function setUp() + { + $objectManager = new ObjectManager($this); + $this->msrpGroupedCalculatorMock = $this->createMock(MsrpGroupedCalculator::class); + $this->pricing = $objectManager->getObject( + MsrpPriceCalculator::class, + [ + 'msrpPriceCalculators' => [ + [ + 'productType' => GroupedType::TYPE_CODE, + 'priceCalculator' => $this->msrpGroupedCalculatorMock + ] + ] + ] + ); + } + + /** + * Test getMrspPriceValue() with the data provider below + * + * @param array $msrpPriceCalculators + * @param Product $productMock + * @param float $expected + * @dataProvider getMsrpPriceValueDataProvider + */ + public function testGetMsrpPriceValue($msrpPriceCalculatorPrice, $productMock, $expected) + { + $this->msrpGroupedCalculatorMock->expects($this->any()) + ->method('getMsrpPriceValue')->willReturn($msrpPriceCalculatorPrice); + + $this->assertEquals($expected, $this->pricing->getMsrpPriceValue($productMock)); + } + + /** + * Data Provider for test getMrspPriceValue() + * + * @return array + */ + public function getMsrpPriceValueDataProvider() + { + return [ + 'Get Mrsp Price with product and msrp calculator and the same product type' => [ + 23.50, + $this->createProductMock(GroupedType::TYPE_CODE, 0), + 23.50 + ], + 'Get Mrsp Price with product and msrp calculator and the different product type' => [ + 24.88, + $this->createProductMock(ProductType::TYPE_SIMPLE, 24.88), + 24.88 + ] + ]; + } + + /** + * Create Product Mock + * + * @param string $typeId + * @param float $msrp + * @return MockObject + */ + private function createProductMock($typeId, $msrp) + { + $productMock = $this->createPartialMock(Product::class, ['getTypeId', 'getMsrp']); + $productMock->expects($this->any())->method('getTypeId')->willReturn($typeId); + $productMock->expects($this->any())->method('getMsrp')->willReturn($msrp); + return $productMock; + } +} diff --git a/app/code/Magento/Msrp/etc/di.xml b/app/code/Magento/Msrp/etc/di.xml index b8392b0bb0fe4..e617e153fd951 100644 --- a/app/code/Magento/Msrp/etc/di.xml +++ b/app/code/Magento/Msrp/etc/di.xml @@ -53,4 +53,14 @@ </argument> </arguments> </type> + <type name="Magento\Eav\Model\Config"> + <arguments> + <argument name="attributesForPreload" xsi:type="array"> + <item name="catalog_product" xsi:type="array"> + <item name="msrp" xsi:type="string">catalog_product</item> + <item name="msrp_display_actual_price_type" xsi:type="string">catalog_product</item> + </item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Msrp/registration.php b/app/code/Magento/Msrp/registration.php index 0c0269b39eeac..147d789975c3e 100644 --- a/app/code/Magento/Msrp/registration.php +++ b/app/code/Magento/Msrp/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Msrp', __DIR__); diff --git a/app/code/Magento/Msrp/view/base/web/js/msrp.js b/app/code/Magento/Msrp/view/base/web/js/msrp.js index 2789491137bc1..65af87d85de51 100644 --- a/app/code/Magento/Msrp/view/base/web/js/msrp.js +++ b/app/code/Magento/Msrp/view/base/web/js/msrp.js @@ -352,8 +352,11 @@ define([ $(this.options.mapInfoLinks).show(); if (useDefaultPrice || !this.wasOpened) { - this.$popup.find(this.options.msrpLabelId).html(options.msrpPrice); - this.$popup.find(this.options.priceLabelId).html(options.realPrice); + if (this.$popup) { + this.$popup.find(this.options.msrpLabelId).html(options.msrpPrice); + this.$popup.find(this.options.priceLabelId).html(options.realPrice); + } + $(this.options.displayPriceElement).html(msrpPrice); this.wasOpened = true; } diff --git a/app/code/Magento/MsrpConfigurableProduct/registration.php b/app/code/Magento/MsrpConfigurableProduct/registration.php index d4d58ec3c013b..e0772ae9c78fd 100644 --- a/app/code/Magento/MsrpConfigurableProduct/registration.php +++ b/app/code/Magento/MsrpConfigurableProduct/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_MsrpConfigurableProduct', __DIR__); diff --git a/app/code/Magento/MsrpGroupedProduct/registration.php b/app/code/Magento/MsrpGroupedProduct/registration.php index c5a261e66c640..a429ec00cd437 100644 --- a/app/code/Magento/MsrpGroupedProduct/registration.php +++ b/app/code/Magento/MsrpGroupedProduct/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_MsrpGroupedProduct', __DIR__); diff --git a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php index d1103abfbb94e..7fa674505461e 100644 --- a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php +++ b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php @@ -625,6 +625,7 @@ public function setShippingMethods($methods) $addressId = $address->getId(); if (isset($methods[$addressId])) { $address->setShippingMethod($methods[$addressId]); + $address->setCollectShippingRates(true); } elseif (!$address->getShippingMethod()) { throw new \Magento\Framework\Exception\LocalizedException( __('Set shipping methods for all addresses. Verify the shipping methods and try again.') @@ -662,7 +663,9 @@ public function setPaymentMethod($payment) $quote->getPayment()->importData($payment); // shipping totals may be affected by payment method if (!$quote->isVirtual() && $quote->getShippingAddress()) { - $quote->getShippingAddress()->setCollectShippingRates(true); + foreach ($quote->getAllShippingAddresses() as $shippingAddress) { + $shippingAddress->setCollectShippingRates(true); + } $quote->setTotalsCollectedFlag(false)->collectTotals(); } $this->quoteRepository->save($quote); diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AdminSalesOrderActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AdminSalesOrderActionGroup.xml index 67ba256f50ea7..dcd8bfd8d141b 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AdminSalesOrderActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AdminSalesOrderActionGroup.xml @@ -8,22 +8,21 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <actionGroup name="AdminSalesOrderActionGroup"> - <waitForPageLoad stepKey="waitForAdminSalesPageToLoad"/> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRowLink"/> - <waitForPageLoad stepKey="waitForOrderPageToLoad"/> - <waitForPageLoad stepKey="waitForCheckTotalActionGroup"/> <scrollTo selector="{{AdminOrderTotalSection.subTotal}}" stepKey="scrollToOrderTotalSection"/> - <grabTextFrom selector="{{AdminOrderTotalSection.subTotal}}" stepKey="grabvalueForSubtotal"/> - <grabTextFrom selector="{{AdminOrderTotalSection.shippingAndHandling}}" stepKey="grabvalueForShippingHandling"/> - <grabTextFrom selector="{{AdminOrderTotalSection.grandTotal}}" stepKey="grabvalueForGrandTotal"/> - <executeJS stepKey="sum_TotalValue" function=" - var subtotal = '{$grabvalueForSubtotal}'.substr(1); - var handling = '{$grabvalueForShippingHandling}'.substr(1); - var subtotal_handling = (parseFloat(subtotal) + parseFloat(handling)).toFixed(2); - return ('$' + subtotal_handling);"/> + <grabTextFrom selector="{{AdminOrderTotalSection.subTotal}}" stepKey="grabValueForSubtotal"/> + <grabTextFrom selector="{{AdminOrderTotalSection.shippingAndHandling}}" stepKey="grabValueForShippingHandling"/> + <grabTextFrom selector="{{AdminOrderTotalSection.grandTotal}}" stepKey="grabValueForGrandTotal"/> + <executeJS function=" + var grandTotal = '{$grabValueForGrandTotal}'.substr(1); + return (grandTotal);" stepKey="grandTotalValue"/> + <executeJS function=" + var subtotal = '{$grabValueForSubtotal}'.substr(1); + var handling = '{$grabValueForShippingHandling}'.substr(1); + var subtotalHandling = (parseFloat(subtotal) + parseFloat(handling)).toFixed(2); + return (subtotalHandling);" stepKey="sumTotalValue"/> <assertEquals stepKey="assertSubTotalPrice"> - <expectedResult type="string">$sum_TotalValue</expectedResult> - <actualResult type="string">$grabvalueForGrandTotal</actualResult> + <expectedResult type="variable">$sumTotalValue</expectedResult> + <actualResult type="variable">$grandTotalValue</actualResult> </assertEquals> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AssertStorefrontSalesOrderMatchesGrandTotalActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AssertStorefrontSalesOrderMatchesGrandTotalActionGroup.xml new file mode 100644 index 0000000000000..559d759e0468d --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/AssertStorefrontSalesOrderMatchesGrandTotalActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <actionGroup name="AssertStorefrontSalesOrderMatchesGrandTotalActionGroup"> + <arguments> + <argument name="dataHref" type="string"/> + </arguments> + <!--Click on View Order Link--> + <click selector="{{StorefrontSalesOrderSection.viewOrderLink(dataHref)}}" stepKey="viewOrderAction"/> + <waitForPageLoad stepKey="waitForViewOrderPageToLoad"/> + <grabTextFrom selector="{{StorefrontSalesOrderSection.salesOrderPrice('subtotal')}}" stepKey="grabValueForSubtotal"/> + <grabTextFrom selector="{{StorefrontSalesOrderSection.salesOrderPrice('shipping')}}" stepKey="grabValueForShippingHandling"/> + <grabTextFrom selector="{{StorefrontSalesOrderSection.salesOrderPrice('grand_total')}}" stepKey="grabValueForGrandTotal"/> + <executeJS function=" + var grandTotal = '{$grabValueForGrandTotal}'.substr(1); + return (grandTotal);" stepKey="grandTotalValue"/> + <executeJS function=" + var subtotal = '{$grabValueForSubtotal}'.substr(1); + var handling = '{$grabValueForShippingHandling}'.substr(1); + var subtotalHandling = (parseFloat(subtotal) + parseFloat(handling)).toFixed(2); + return (subtotalHandling);" stepKey="sumTotalValue"/> + <assertEquals stepKey="assertSubTotalPrice"> + <expectedResult type="variable">$sumTotalValue</expectedResult> + <actualResult type="variable">$grandTotalValue</actualResult> + </assertEquals> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithMinicartActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithMinicartActionGroup.xml index f648c1026b539..35c42225d458b 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithMinicartActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithMinicartActionGroup.xml @@ -9,9 +9,8 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <actionGroup name="CheckingWithMinicartActionGroup"> - <waitForPageLoad stepKey="waitForCheckoutCartPageLoad"/> - <click stepKey="clickOnCollapsibleDiv" selector="{{MinicartSection.clickOnCollapsibleDiv}}"/> - <click stepKey="clickOnShippingMethodRadioButton" selector="{{MinicartSection.shippingMethodRadioButton}}"/> + <click selector="{{MinicartSection.clickOnCollapsibleDiv}}" stepKey="clickOnCollapsibleDiv"/> + <click selector="{{MinicartSection.shippingMethodRadioButton}}" stepKey="clickOnShippingMethodRadioButton"/> <waitForPageLoad stepKey="waitForShippingPriceToBeChanged"/> <grabTextFrom selector="{{MinicartSection.shippingMethodRadioText}}" stepKey="shippingMethodRadioText"/> <grabTextFrom selector="{{MinicartSection.shippingMethodSubtotalPrice}}" stepKey="shippingMethodSubtotalPrice"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithMultipleAddressesActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithMultipleAddressesActionGroup.xml index 861b97427b44d..a34e0627bd150 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithMultipleAddressesActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithMultipleAddressesActionGroup.xml @@ -7,38 +7,18 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <actionGroup name="CheckingWithSingleAddressActionGroup"> - <click stepKey="clickOnCheckoutWithMultipleAddresses" selector="{{SingleShippingSection.checkoutWithMultipleAddresses}}"/> - <waitForPageLoad stepKey="waitForMultipleAddressPageLoad"/> - <click stepKey="goToShippingInformation" selector="{{SingleShippingSection.goToShippingInfo}}"/> - <waitForPageLoad stepKey="waitForShippingPageLoad"/> - </actionGroup> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="CheckingWithMultipleAddressesActionGroup" extends="CheckingWithSingleAddressActionGroup"> - <grabTextFrom stepKey="firstShippingAddressValue" selector="{{MultishippingSection.firstShippingAddressValue}}" after="waitForMultipleAddressPageLoad" /> - <selectOption selector="{{MultishippingSection.firstShippingAddressOption}}" userInput="{$firstShippingAddressValue}" stepKey="selectFirstShippingMethod" after="firstShippingAddressValue" /> - <waitForPageLoad stepKey="waitForSecondShippingAddresses" after="selectFirstShippingMethod" /> - <grabTextFrom stepKey="secondShippingAddressValue" selector="{{MultishippingSection.secondShippingAddressValue}}" after="waitForSecondShippingAddresses" /> - <selectOption selector="{{MultishippingSection.secondShippingAddressOption}}" userInput="{$secondShippingAddressValue}" stepKey="selectSecondShippingMethod" after="secondShippingAddressValue" /> - <click stepKey="clickOnUpdateAddress" selector="{{SingleShippingSection.updateAddress}}" after="selectSecondShippingMethod" /> - <waitForPageLoad stepKey="waitForShippingInformation" after="clickOnUpdateAddress" /> - </actionGroup> - <actionGroup name="StorefrontCheckoutWithMultipleAddressesActionGroup"> - <click selector="{{SingleShippingSection.checkoutWithMultipleAddresses}}" stepKey="clickOnCheckoutWithMultipleAddresses"/> - <waitForPageLoad stepKey="waitForMultipleAddressPageLoad"/> - </actionGroup> - <actionGroup name="StorefrontSelectAddressActionGroup"> <arguments> - <argument name="sequenceNumber" type="string" defaultValue="1"/> - <argument name="option" type="string" defaultValue="1"/> + <argument name="addressOption1" type="string" defaultValue="1"/> + <argument name="addressOption2" type="string" defaultValue="2"/> </arguments> - <selectOption selector="{{MultishippingSection.selectShippingAddress(sequenceNumber)}}" userInput="{{option}}" stepKey="selectShippingAddress"/> - </actionGroup> - <actionGroup name="StorefrontSaveAddressActionGroup"> - <click stepKey="clickOnUpdateAddress" selector="{{SingleShippingSection.updateAddress}}"/> - <waitForPageLoad stepKey="waitForShippingInformationAfterUpdated" time="90"/> - <click stepKey="goToShippingInformation" selector="{{SingleShippingSection.goToShippingInfo}}"/> - <waitForPageLoad stepKey="waitForShippingPageLoad"/> + <grabTextFrom selector="{{MultishippingSection.shippingAddressOptions(addressOption1,addressOption1)}}" after="waitForMultipleAddressPageLoad" stepKey="firstShippingAddressValue"/> + <selectOption selector="{{MultishippingSection.shippingAddressSelector(addressOption1)}}" userInput="{$firstShippingAddressValue}" after="firstShippingAddressValue" stepKey="selectFirstShippingMethod"/> + <waitForPageLoad after="selectFirstShippingMethod" stepKey="waitForSecondShippingAddresses"/> + <grabTextFrom selector="{{MultishippingSection.shippingAddressOptions(addressOption2,addressOption2)}}" after="waitForSecondShippingAddresses" stepKey="secondShippingAddressValue"/> + <selectOption selector="{{MultishippingSection.shippingAddressSelector(addressOption2)}}" userInput="{$secondShippingAddressValue}" after="secondShippingAddressValue" stepKey="selectSecondShippingMethod"/> + <click selector="{{SingleShippingSection.updateAddress}}" after="selectSecondShippingMethod" stepKey="clickOnUpdateAddress"/> + <waitForPageLoad after="clickOnUpdateAddress" stepKey="waitForShippingInformation"/> </actionGroup> </actionGroups> - diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithSingleAddressActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithSingleAddressActionGroup.xml new file mode 100644 index 0000000000000..d911488925427 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/CheckingWithSingleAddressActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <actionGroup name="CheckingWithSingleAddressActionGroup"> + <click selector="{{SingleShippingSection.checkoutWithMultipleAddresses}}" stepKey="clickOnCheckoutWithMultipleAddresses"/> + <waitForPageLoad stepKey="waitForMultipleAddressPageLoad"/> + <click selector="{{SingleShippingSection.goToShippingInfo}}" stepKey="goToShippingInformation"/> + <waitForPageLoad stepKey="waitForShippingPageLoad"/> + </actionGroup> +</actionGroups> + diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/PlaceOrderActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/PlaceOrderActionGroup.xml index 349d31ef1da5e..8cfbe2665d685 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/PlaceOrderActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/PlaceOrderActionGroup.xml @@ -9,11 +9,8 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <actionGroup name="PlaceOrderActionGroup"> - <waitForPageLoad stepKey="waitForPlaceOrderPageLoad"/> - <!-- place order and check the order number--> - <click stepKey="checkoutMultishipmentPlaceOrder" selector="{{SingleShippingSection.placeOrder}}" /> + <click selector="{{SingleShippingSection.placeOrder}}" stepKey="checkoutMultiShipmentPlaceOrder"/> <waitForPageLoad stepKey="waitForSuccessfullyPlacedOrder"/> <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage"/> - </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/ReviewOrderActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/ReviewOrderActionGroup.xml index bbd0e9ebad7aa..8a63c42006443 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/ReviewOrderActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/ReviewOrderActionGroup.xml @@ -9,30 +9,15 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <actionGroup name="ReviewOrderForSingleShipmentActionGroup"> - <waitForPageLoad stepKey="waitForReviewOrderPageLoad"/> - <grabTextFrom selector="{{ReviewOrderSection.shippingMethodBasePrice}}" stepKey="shippingMethodBasePrice"/> - <grabTextFrom selector="{{ReviewOrderSection.shippingMethodSubtotalPrice}}" stepKey="shippingMethodSubtotalPrice"/> + <arguments> + <argument name="totalName" type="string" defaultValue="Shipping & Handling"/> + <argument name="totalPosition" type="string" defaultValue="1"/> + </arguments> + <grabTextFrom selector="{{ReviewOrderSection.shippingMethodBasePrice(totalPosition)}}" stepKey="shippingMethodBasePrice"/> + <grabTextFrom selector="{{ReviewOrderSection.shippingMethodSubtotalPrice(totalPosition,totalName)}}" stepKey="shippingMethodSubtotalPrice"/> <assertEquals stepKey="assertShippingMethodPrice"> <expectedResult type="string">$shippingMethodSubtotalPrice</expectedResult> <actualResult type="string">$shippingMethodBasePrice</actualResult> </assertEquals> </actionGroup> - <actionGroup name="ReviewOrderForMultiShipmentActionGroup"> - <waitForPageLoad stepKey="waitForFirstShippingMethod" /> - <!--Check First Shipping Method Price--> - <grabTextFrom selector="{{ReviewOrderSection.firstShippingMethodBasePrice}}" stepKey="firstShippingMethodBasePrice"/> - <grabTextFrom selector="{{ReviewOrderSection.firstShippingMethodSubtotalPrice}}" stepKey="firstShippingMethodSubtotalPrice"/> - <assertEquals stepKey="assertShippingMethodPrice"> - <expectedResult type="string">$firstShippingMethodSubtotalPrice</expectedResult> - <actualResult type="string">$firstShippingMethodBasePrice</actualResult> - </assertEquals> - <!--Check Second Shipping Method Price--> - <grabTextFrom selector="{{ReviewOrderSection.secondShippingMethodBasePrice}}" stepKey="secondShippingMethodBasePrice" /> - <grabTextFrom selector="{{ReviewOrderSection.secondShippingMethodSubtotalPrice}}" stepKey="secondShippingMethodSubtotalPrice" /> - <assertEquals stepKey="assertSecondShippingMethodPrice" > - <expectedResult type="string">$secondShippingMethodSubtotalPrice</expectedResult> - <actualResult type="string">$secondShippingMethodBasePrice</actualResult> - </assertEquals> - - </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/ReviewOrderForMultiShipmentActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/ReviewOrderForMultiShipmentActionGroup.xml new file mode 100644 index 0000000000000..3a95c7779d596 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/ReviewOrderForMultiShipmentActionGroup.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ReviewOrderForMultiShipmentActionGroup"> + <arguments> + <argument name="totalNameForFirstOrder" type="string" defaultValue="Shipping & Handling"/> + <argument name="totalPositionForFirstOrder" type="string" defaultValue="1"/> + <argument name="totalNameForSecondOrder" type="string" defaultValue="Shipping & Handling"/> + <argument name="totalPositionForSecondOrder" type="string" defaultValue="2"/> + </arguments> + <!--Check First Shipping Method Price--> + <grabTextFrom selector="{{ReviewOrderSection.shippingMethodBasePrice(totalPositionForFirstOrder)}}" stepKey="firstShippingMethodBasePrice"/> + <grabTextFrom selector="{{ReviewOrderSection.shippingMethodSubtotalPrice(totalPositionForFirstOrder,totalNameForFirstOrder)}}" stepKey="firstShippingMethodSubtotalPrice"/> + <assertEquals stepKey="assertShippingMethodPrice"> + <expectedResult type="string">$firstShippingMethodSubtotalPrice</expectedResult> + <actualResult type="string">$firstShippingMethodBasePrice</actualResult> + </assertEquals> + <!--Check Second Shipping Method Price--> + <grabTextFrom selector="{{ReviewOrderSection.shippingMethodBasePrice(totalPositionForSecondOrder)}}" stepKey="secondShippingMethodBasePrice"/> + <grabTextFrom selector="{{ReviewOrderSection.shippingMethodSubtotalPrice(totalPositionForSecondOrder,totalNameForSecondOrder)}}" stepKey="secondShippingMethodSubtotalPrice"/> + <assertEquals stepKey="assertSecondShippingMethodPrice" > + <expectedResult type="string">$secondShippingMethodSubtotalPrice</expectedResult> + <actualResult type="string">$secondShippingMethodBasePrice</actualResult> + </assertEquals> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SalesOrderActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SalesOrderActionGroup.xml deleted file mode 100644 index 47cc3ffa455a0..0000000000000 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SalesOrderActionGroup.xml +++ /dev/null @@ -1,38 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <actionGroup name="SalesOrderForMultiShipmentActionGroup"> - <arguments> - <argument name="shippingPrice" defaultValue="$5.00" type="string" /> - <argument name="subtotalPrice" defaultValue="$123.00" type="string" /> - <argument name="totalPrice" defaultValue="$128.00" type="string" /> - </arguments> - <waitForPageLoad stepKey="waitForSalesOrderHistoryPageToLoad" /> - <!--Click on View Order Link--> - <click stepKey="viewOrderAction" selector="{{SalesOrderSection.viewOrderLink}}"/> - <waitForPageLoad stepKey="waitForViewOrderPageToLoad" /> - <!--Check Shipping Method, Subtotal and Total Price--> - <grabTextFrom selector="{{SalesOrderSection.salesOrderPrice('subtotal')}}" stepKey="salesOrderSubtotalPrice"/> - <grabTextFrom selector="{{SalesOrderSection.salesOrderPrice('shipping')}}" stepKey="salesOrderShippingPrice"/> - <grabTextFrom selector="{{SalesOrderSection.salesOrderPrice('grand_total')}}" stepKey="salesOrderGrandTotalPrice"/> - <assertEquals stepKey="assertSubtotalPrice"> - <expectedResult type="string">{{subtotalPrice}}</expectedResult> - <actualResult type="string">$salesOrderSubtotalPrice</actualResult> - </assertEquals> - <assertEquals stepKey="assertShippingMethodPrice"> - <expectedResult type="string">{{shippingPrice}}</expectedResult> - <actualResult type="string">$salesOrderShippingPrice</actualResult> - </assertEquals> - <assertEquals stepKey="assertTotalPrice"> - <expectedResult type="string">{{totalPrice}}</expectedResult> - <actualResult type="string">$salesOrderGrandTotalPrice</actualResult> - </assertEquals> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectBillingInfoActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectBillingInfoActionGroup.xml index c5dd97cadcc2d..63fbaea72cc50 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectBillingInfoActionGroup.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectBillingInfoActionGroup.xml @@ -10,6 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <actionGroup name="SelectBillingInfoActionGroup"> <waitForPageLoad stepKey="waitForBillingInfoPageLoad"/> - <click stepKey="goToReviewOrder" selector="{{PaymentMethodSection.goToReviewOrder}}"/> + <click selector="{{PaymentMethodSection.goToReviewOrder}}" stepKey="goToReviewOrder"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectMultiShippingInfoActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectMultiShippingInfoActionGroup.xml new file mode 100644 index 0000000000000..9da09a524184d --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectMultiShippingInfoActionGroup.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="SelectMultiShippingInfoActionGroup"> + <arguments> + <argument name="shippingMethodPosition1" type="string" defaultValue="1"/> + <argument name="shippingMethodPosition2" type="string" defaultValue="2"/> + <argument name="shippingMethodType1" type="string" defaultValue="Fixed"/> + <argument name="shippingMethodType2" type="string" defaultValue="Free"/> + </arguments> + <selectOption selector="{{ShippingMethodSection.selectShippingMethod(shippingMethodPosition1,shippingMethodPosition1)}}" userInput="{{shippingMethodType1}}" stepKey="selectShippingMethod1"/> + <waitForPageLoad stepKey="waitForSecondShippingMethod"/> + <selectOption selector="{{ShippingMethodSection.selectShippingMethod(shippingMethodPosition2,shippingMethodPosition2)}}" userInput="{{shippingMethodType2}}" stepKey="selectShippingMethod2"/> + <waitForPageLoad stepKey="waitForRadioOptions"/> + <click selector="{{ShippingMethodSection.goToBillingInfo}}" stepKey="goToBillingInformation"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectShippingInfoActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectShippingInfoActionGroup.xml deleted file mode 100644 index bcaeb8ba4800c..0000000000000 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectShippingInfoActionGroup.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <actionGroup name="SelectSingleShippingInfoActionGroup"> - <arguments> - <argument name="shippingMethodType" type="string" defaultValue="Fixed"/> - </arguments> - <waitForPageLoad stepKey="waitForShippingInfoPageLoad"/> - <selectOption selector="{{ShippingMethodSection.shippingMethodRadioButton}}" userInput="{{shippingMethodType}}" stepKey="selectShippingMethod"/> - <waitForPageLoad stepKey="waitForRadioOptions"/> - <click stepKey="goToBillingInformation" selector="{{ShippingMethodSection.goToBillingInfo}}"/> - </actionGroup> - <actionGroup name="SelectMultiShippingInfoActionGroup"> - <arguments> - <argument name="shippingMethodType1" type="string" defaultValue="Fixed"/> - <argument name="shippingMethodType2" type="string" defaultValue="Free"/> - </arguments> - <waitForPageLoad stepKey="waitForShippingInfoPageLoad"/> - <selectOption selector="{{ShippingMethodSection.firstShippingMethodRadioButton}}" userInput="{{shippingMethodType1}}" stepKey="selectShippingMethod1"/> - <waitForPageLoad stepKey="waitForSecondShippingMethod"/> - <selectOption selector="{{ShippingMethodSection.secondShippingMethodRadioButton}}" userInput="{{shippingMethodType2}}" stepKey="selectShippingMethod2"/> - <waitForPageLoad stepKey="waitForRadioOptions"/> - <click stepKey="goToBillingInformation" selector="{{ShippingMethodSection.goToBillingInfo}}"/> - </actionGroup> - <actionGroup name="StorefrontLeaveDefaultShippingMethodsAndGoToBillingInfoActionGroup"> - <waitForPageLoad stepKey="waitForShippingInfo"/> - <click stepKey="goToBillingInformation" selector="{{ShippingMethodSection.goToBillingInfo}}"/> - </actionGroup> -</actionGroups> - diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectSingleShippingInfoActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectSingleShippingInfoActionGroup.xml new file mode 100644 index 0000000000000..329f1451788cd --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/SelectSingleShippingInfoActionGroup.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/testSchema.xsd"> + <actionGroup name="SelectSingleShippingInfoActionGroup"> + <arguments> + <argument name="shippingMethodType" type="string" defaultValue="Fixed"/> + </arguments> + <selectOption selector="{{ShippingMethodSection.shippingMethodRadioButton}}" userInput="{{shippingMethodType}}" stepKey="selectShippingMethod"/> + <waitForPageLoad stepKey="waitForRadioOptions"/> + <click selector="{{ShippingMethodSection.goToBillingInfo}}" stepKey="goToBillingInformation"/> + </actionGroup> +</actionGroups> + diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontCheckoutShippingSelectMultipleAddressesActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontCheckoutShippingSelectMultipleAddressesActionGroup.xml new file mode 100644 index 0000000000000..b698f00078bd6 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontCheckoutShippingSelectMultipleAddressesActionGroup.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/testSchema.xsd"> + <actionGroup name="StorefrontCheckoutShippingSelectMultipleAddressesActionGroup"> + <arguments> + <argument name="firstAddress" type="string" defaultValue="{{CustomerAddressSimple.street[0]}}"/> + <argument name="secondAddress" type="string" defaultValue="{{CustomerAddressSimple.street[1]}}"/> + </arguments> + <selectOption selector="{{StorefrontCheckoutShippingMultipleAddressesSection.selectedMultipleShippingAddress('1')}}" userInput="{{firstAddress}}" stepKey="selectShippingAddressForTheFirstItem"/> + <selectOption selector="{{StorefrontCheckoutShippingMultipleAddressesSection.selectedMultipleShippingAddress('2')}}" userInput="{{secondAddress}}" stepKey="selectShippingAddressForTheSecondItem"/> + <click selector="{{CheckoutSuccessMainSection.continueShoppingButton}}" stepKey="clickToGoToInformationButton"/> + </actionGroup> +</actionGroups> + diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontCheckoutWithMultipleAddressesActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontCheckoutWithMultipleAddressesActionGroup.xml new file mode 100644 index 0000000000000..f7d6ea6711f89 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontCheckoutWithMultipleAddressesActionGroup.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="StorefrontCheckoutWithMultipleAddressesActionGroup"> + <click selector="{{SingleShippingSection.checkoutWithMultipleAddresses}}" stepKey="clickOnCheckoutWithMultipleAddresses"/> + <waitForPageLoad stepKey="waitForMultipleAddressPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontGoCheckoutWithMultipleAddressesActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontGoCheckoutWithMultipleAddressesActionGroup.xml new file mode 100644 index 0000000000000..fd57a3b095a3d --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontGoCheckoutWithMultipleAddressesActionGroup.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="StorefrontGoCheckoutWithMultipleAddressesActionGroup"> + <click selector="{{MultishippingSection.shippingMultipleCheckout}}" stepKey="clickToMultipleAddressShippingButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontGoToBillingInformationActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontGoToBillingInformationActionGroup.xml new file mode 100644 index 0000000000000..baca62007d3cb --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontGoToBillingInformationActionGroup.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="StorefrontGoToBillingInformationActionGroup"> + <click selector="{{StorefrontMultipleShippingMethodSection.continueToBillingInformationButton}}" stepKey="clickToContinueToBillingInformationButton"/> + <waitForPageLoad stepKey="waitForBillingPage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontLeaveDefaultShippingMethodsAndGoToBillingInfoActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontLeaveDefaultShippingMethodsAndGoToBillingInfoActionGroup.xml new file mode 100644 index 0000000000000..1054d9b7d9160 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontLeaveDefaultShippingMethodsAndGoToBillingInfoActionGroup.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="StorefrontLeaveDefaultShippingMethodsAndGoToBillingInfoActionGroup"> + <waitForPageLoad stepKey="waitForShippingInfo"/> + <click selector="{{ShippingMethodSection.goToBillingInfo}}" stepKey="goToBillingInformation"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontMultishippingCheckoutActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontMultishippingCheckoutActionGroup.xml deleted file mode 100644 index c5dee010239d7..0000000000000 --- a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontMultishippingCheckoutActionGroup.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <actionGroup name="StorefrontCheckoutShippingSelectMultipleAddressesActionGroup"> - <arguments> - <argument name="firstAddress" type="string" defaultValue="{{CustomerAddressSimple.street[0]}}"/> - <argument name="secondAddress" type="string" defaultValue="{{CustomerAddressSimple.street[1]}}"/> - </arguments> - <selectOption selector="{{StorefrontCheckoutShippingMultipleAddressesSection.selectedMultipleShippingAddress('1')}}" userInput="{{firstAddress}}" stepKey="selectShippingAddressForTheFirstItem"/> - <selectOption selector="{{StorefrontCheckoutShippingMultipleAddressesSection.selectedMultipleShippingAddress('2')}}" userInput="{{secondAddress}}" stepKey="selectShippingAddressForTheSecondItem"/> - <click selector="{{CheckoutSuccessMainSection.continueShoppingButton}}" stepKey="clickToGoToInformationButton"/> - </actionGroup> - <actionGroup name="StorefrontGoCheckoutWithMultipleAddresses"> - <click selector="{{MultishippingSection.shippingMultipleCheckout}}" stepKey="clickToMultipleAddressShippingButton"/> - </actionGroup> - <actionGroup name="StorefrontGoToBillingInformationActionGroup"> - <click selector="{{StorefrontMultipleShippingMethodSection.continueToBillingInformationButton}}" stepKey="clickToContinueToBillingInformationButton"/> - <waitForPageLoad stepKey="waitForBillingPage"/> - </actionGroup> -</actionGroups> - diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontPlaceOrderForMultipleAddressesActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontPlaceOrderForMultipleAddressesActionGroup.xml new file mode 100644 index 0000000000000..8391a16f81b6d --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontPlaceOrderForMultipleAddressesActionGroup.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="StorefrontPlaceOrderForMultipleAddressesActionGroup" extends="PlaceOrderActionGroup"> + <arguments> + <argument name="firstOrderPosition" type="string" defaultValue="1"/> + <argument name="secondOrderPosition" type="string" defaultValue="2"/> + </arguments> + <grabTextFrom selector="{{StorefrontSalesOrderSection.orderLinkByPosition(firstOrderPosition)}}" after="waitForLoadSuccessPage" stepKey="getFirstOrderId"/> + <grabAttributeFrom selector="{{StorefrontSalesOrderSection.orderLinkByPosition(firstOrderPosition)}}" userInput="href" after="getFirstOrderId" stepKey="dataHrefForFirstOrder"/> + <grabTextFrom selector="{{StorefrontSalesOrderSection.orderLinkByPosition(secondOrderPosition)}}" after="dataHrefForFirstOrder" stepKey="getSecondOrderId"/> + <grabAttributeFrom selector="{{StorefrontSalesOrderSection.orderLinkByPosition(secondOrderPosition)}}" userInput="href" after="getSecondOrderId" stepKey="dataHrefForSecondOrder"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontSaveAddressActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontSaveAddressActionGroup.xml new file mode 100644 index 0000000000000..c977c94b4e590 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontSaveAddressActionGroup.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="StorefrontSaveAddressActionGroup"> + <click selector="{{SingleShippingSection.updateAddress}}" stepKey="clickOnUpdateAddress"/> + <waitForPageLoad time="90" stepKey="waitForShippingInformationAfterUpdated"/> + <click selector="{{SingleShippingSection.goToShippingInfo}}" stepKey="goToShippingInformation"/> + <waitForPageLoad stepKey="waitForShippingPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontSelectAddressActionGroup.xml b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontSelectAddressActionGroup.xml new file mode 100644 index 0000000000000..9a20c325df7a6 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/ActionGroup/StorefrontSelectAddressActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontSelectAddressActionGroup"> + <arguments> + <argument name="sequenceNumber" type="string" defaultValue="1"/> + <argument name="option" type="string" defaultValue="1"/> + </arguments> + <selectOption selector="{{MultishippingSection.selectShippingAddress(sequenceNumber)}}" userInput="{{option}}" stepKey="selectShippingAddress"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Data/MultishippingSalesRuleData.xml b/app/code/Magento/Multishipping/Test/Mftf/Data/MultishippingSalesRuleData.xml new file mode 100644 index 0000000000000..7c79081245fd6 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/Data/MultishippingSalesRuleData.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="CartPriceRuleConditionForSubtotalForMultiShipping" extends="CartPriceRuleConditionAppliedForSubtotal"> + <data key="apply">Percent of product price discount</data> + <data key="customerGroups">'NOT LOGGED IN', 'General', 'Wholesale', 'Retailer'</data> + <data key="subtotal">50</data> + <data key="apply_to_shipping">1</data> + <data key="simple_free_shipping">For matching items only</data> + <data key="condition1">Subtotal</data> + <data key="condition2">Shipping Method</data> + <data key="rule1">equals or greater than</data> + <data key="shippingMethod">[flatrate] Fixed</data> + <data key="ruleToChange1">is</data> + </entity> +</entities> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection.xml index e6f3282493718..cd408f5600e3d 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/MultishippingSection.xml @@ -17,10 +17,8 @@ <section name="MultishippingSection"> <element name="checkoutWithMultipleAddresses" type="button" selector="//span[text()='Check Out with Multiple Addresses']"/> <element name="shippingMultipleCheckout" type="button" selector=".action.multicheckout"/> - <element name="firstShippingAddressValue" type="select" selector="//table//tbody//tr[position()=1]//td[position()=3]//div//select//option[2]"/> - <element name="firstShippingAddressOption" type="select" selector="//table//tbody//tr[position()=1]//td[position()=3]//div//select"/> - <element name="secondShippingAddressValue" type="select" selector="//table//tbody//tr[position()=2]//td[position()=3]//div//select//option[1]"/> - <element name="secondShippingAddressOption" type="select" selector="//table//tbody//tr[position()=2]//td[position()=3]//div//select"/> + <element name="shippingAddressSelector" type="select" selector="//tr[position()={{addressPosition}}]//td[@data-th='Send To']//select" parameterized="true"/> + <element name="shippingAddressOptions" type="select" selector="#multiship-addresses-table tbody tr:nth-of-type({{addressPosition}}) .col.address select option:nth-of-type({{optionIndex}})" parameterized="true"/> <element name="selectShippingAddress" type="select" selector="(//table[@id='multiship-addresses-table'] //div[@class='field address'] //select)[{{sequenceNumber}}]" parameterized="true"/> </section> <section name="StorefrontMultipleShippingMethodSection"> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/PaymentMethodSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/PaymentMethodSection.xml index 8113ed3aa0c07..2d47b54d84b9c 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Section/PaymentMethodSection.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/PaymentMethodSection.xml @@ -9,6 +9,6 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <section name="PaymentMethodSection"> - <element name="goToReviewOrder" type="button" selector="//span[text()='Go to Review Your Order']"/> + <element name="goToReviewOrder" type="button" selector="#payment-continue"/> </section> -</sections> \ No newline at end of file +</sections> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/ReviewOrderSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/ReviewOrderSection.xml index 7961a0f811f64..de33c28bfb1f2 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Section/ReviewOrderSection.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/ReviewOrderSection.xml @@ -9,18 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <section name="ReviewOrderSection"> - <element name="shippingMethodBasePrice" type="text" selector="//div[@class='block block-shipping'][position()=1]//div[@class='block-content'][position()=1]//div[@class='box box-shipping-method']//div[@class='box-content']//span[@class='price']"/> - <element name="shippingMethodSubtotalPrice" type="text" selector="//div[@class='block-content'][position()=1]//table[position()=1]//tr[position()=2]//td[@class='amount']//span[@class='price']"/> - <element name="firstShippingMethodBasePrice" type="text" selector="//div[@class='block block-shipping'][position()=1]//div[@class='block-content'][position()=1]//div[@class='box box-shipping-method']//div[@class='box-content']//span[@class='price']"/> - <element name="secondShippingMethodBasePrice" type="text" selector="//div[@class='block block-shipping'][position()=1]//div[@class='block-content'][position()=2]//div[@class='box box-shipping-method']//div[@class='box-content']//span[@class='price']"/> - <element name="firstShippingMethodSubtotalPrice" type="text" selector="//div[@class='block-content'][position()=1]//table[position()=1]//tr[position()=2]//td//span[@class='price']"/> - <element name="secondShippingMethodSubtotalPrice" type="text" selector="//div[@class='block-content'][position()=2]//table[position()=1]//tr[position()=2]//td//span[@class='price']"/> - <element name="firstOrderSubtotalPrice" type="text" selector="//div[@class='block-content'][position()=1]//table[position()=1]//tr[@class='totals sub'][position()=1]//td[@data-th='Subtotal']//span[@class='price']"/> - <element name="secondOrderSubtotalPrice" type="text" selector="//div[@class='block-content'][position()=2]//table[position()=1]//tr[@class='totals sub'][position()=1]//td[@data-th='Subtotal']//span[@class='price']"/> - <element name="firstOrderTaxPrice" type="text" selector="//div[@class='block-content'][position()=1]//table[position()=1]//tr[@class='totals-tax'][position()=1]//td[@data-th='Tax']//span[@class='price']"/> - <element name="secondOrderTaxPrice" type="text" selector="//div[@class='block-content'][position()=2]//table[position()=1]//tr[@class='totals-tax'][position()=1]//td[@data-th='Tax']//span[@class='price']"/> - <element name="firstOrderTotalPrice" type="text" selector="//div[@class='block-content'][position()=1]//table[position()=1]//tr[@class='grand totals'][position()=1]//td//span[@class='price']"/> - <element name="secondOrderTotalPrice" type="text" selector="//div[@class='block-content'][position()=2]//table[position()=1]//tr[@class='grand totals'][position()=1]//td//span[@class='price']"/> - <element name="grandTotalPrice" type="text" selector="//div[@class='checkout-review']//div[@class='grand totals']//span[@class='price']"/> + <element name="shippingMethodBasePrice" type="text" selector="//div[@class='block-content'][position()={{shippingMethodPosition}}]//div[@class='box box-shipping-method'][position()=1]//span[@class='price']" parameterized="true"/> + <element name="shippingMethodSubtotalPrice" type="text" selector="//div[@class='block-content'][position()={{shippingMethodPosition}}]//td[@class='amount'][contains(@data-th,'{{priceType}}')]//span[@class='price']" parameterized="true"/> </section> -</sections> \ No newline at end of file +</sections> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/SalesOrderSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/SalesOrderSection.xml deleted file mode 100644 index c788ef5978ad5..0000000000000 --- a/app/code/Magento/Multishipping/Test/Mftf/Section/SalesOrderSection.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <section name="SalesOrderSection"> - <element name="viewOrderLink" type="text" selector="//span[text()='View Order']"/> - <element name="salesOrderPrice" type="text" selector="//div[@class='order-details-items ordered']//tr[@class='{{price_type}}']//td[@class='amount']//span[@class='price']" parameterized="true"/> - </section> -</sections> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/ShippingMethodSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/ShippingMethodSection.xml index 311b3ae959069..c4dd2494f7fe8 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Section/ShippingMethodSection.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/ShippingMethodSection.xml @@ -10,8 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <section name="ShippingMethodSection"> <element name="shippingMethodRadioButton" type="select" selector="//input[@class='radio']"/> - <element name="firstShippingMethodRadioButton" type="select" selector="//div[@class='block block-shipping'][position()=1]//div[@class='block-content']//div[@class='box box-shipping-method']//div[@class='box-content']//dl//dd[position()=1]//fieldset//div//div//input[@class='radio']"/> - <element name="secondShippingMethodRadioButton" type="select" selector="//div[@class='block block-shipping'][position()=2]//div[@class='block-content']//div[@class='box box-shipping-method']//div[@class='box-content']//dl//dd[position()=2]//fieldset//div//div//input[@class='radio']"/> - <element name="goToBillingInfo" type="button" selector="//span[text()='Continue to Billing Information']"/> + <element name="selectShippingMethod" type="radio" selector="//div[@class='block block-shipping'][position()={{shippingBlockPosition}}]//dd[position()={{shippingMethodPosition}}]//input[@class='radio']" parameterized="true" timeout="5"/> + <element name="goToBillingInfo" type="button" selector=".action.primary.continue"/> </section> -</sections> \ No newline at end of file +</sections> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontSalesOrderSection.xml b/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontSalesOrderSection.xml new file mode 100644 index 0000000000000..94546dcfef9a0 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/Section/StorefrontSalesOrderSection.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:Test/etc/testSchema.xsd"> + <section name="StorefrontSalesOrderSection"> + <element name="orderLinkByPosition" type="text" selector="//li[@class='shipping-list'][position()={{orderLinkPosition}}]//a" parameterized="true"/> + <element name="viewOrderLink" type="text" selector="//td[@data-th='Actions']//a[contains(@href,'{{orderLink}}')]//span[text()='View Order']" parameterized="true" timeout="5"/> + <element name="salesOrderPrice" type="text" selector="//div[@class='order-details-items ordered']//tr[@class='{{priceType}}']//td[@class='amount']//span[@class='price']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithMultishipmentTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithMultishipmentTest.xml index 3a58ead3b6dfa..cfec857329b3d 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithMultishipmentTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithMultishipmentTest.xml @@ -38,11 +38,11 @@ </before> <amOnPage url="$$product1.name$$.html" stepKey="goToProduct1"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProduct1"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProduct1"> <argument name="productName" value="$$product1.name$$"/> </actionGroup> <amOnPage url="$$product2.name$$.html" stepKey="goToProduct2"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProduct2"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProduct2"> <argument name="productName" value="$$product2.name$$"/> </actionGroup> <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openCart"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithSingleShipmentTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithSingleShipmentTest.xml index c9f1856249762..dcf0770e5421e 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithSingleShipmentTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontCheckingWithSingleShipmentTest.xml @@ -38,11 +38,11 @@ </before> <amOnPage url="$$product1.name$$.html" stepKey="goToProduct1"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProduct1"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProduct1"> <argument name="productName" value="$$product1.name$$"/> </actionGroup> <amOnPage url="$$product2.name$$.html" stepKey="goToProduct2"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProduct2"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProduct2"> <argument name="productName" value="$$product2.name$$"/> </actionGroup> <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openCart"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMinicartWithMultishipmentTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMinicartWithMultishipmentTest.xml index d52ddb11212aa..e826d8e03ffbc 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMinicartWithMultishipmentTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMinicartWithMultishipmentTest.xml @@ -38,21 +38,21 @@ </before> <after> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> <deleteData stepKey="deleteCategory" createDataKey="category"/> <deleteData stepKey="deleteProduct1" createDataKey="product1"/> <deleteData stepKey="deleteProduct2" createDataKey="product2"/> <deleteData stepKey="deleteCustomer" createDataKey="customer"/> <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShipping"/> - <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> <actionGroup ref="logout" stepKey="logoutAdmin"/> </after> <amOnPage url="$$product1.name$$.html" stepKey="goToProduct1"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProduct1"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProduct1"> <argument name="productName" value="$$product1.name$$"/> </actionGroup> <amOnPage url="$$product2.name$$.html" stepKey="goToProduct2"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProduct2"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProduct2"> <argument name="productName" value="$$product2.name$$"/> </actionGroup> <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openCart"/> @@ -62,6 +62,7 @@ <actionGroup ref="ReviewOrderForMultiShipmentActionGroup" stepKey="reviewOrderForMultiShipment"/> <amOnPage url="/checkout/cart/index/" stepKey="amOnCheckoutCartIndexPage"/> <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openCartAgain"/> + <waitForPageLoad stepKey="waitForMinicartPageLoad"/> <actionGroup ref="CheckingWithMinicartActionGroup" stepKey="checkoutWithMinicart"/> </test> </tests> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml index a81d24e99563a..ad68b709c5729 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StoreFrontMyAccountWithMultishipmentTest.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="StoreFrontMyAccountWithMultishipmentTest"> + <test name="StorefrontMyAccountWithMultishipmentTest"> <annotations> <features value="Multishipping"/> <stories value="Shipping price shows 0 on Order view page after multiple address checkout"/> @@ -17,53 +17,101 @@ <severity value="CRITICAL"/> <testCaseId value="MC-19303"/> <group value="multishipping"/> + <skip> + <issueId value="MC-22683"/> + </skip> </annotations> <before> - <createData stepKey="category" entity="SimpleSubCategory"/> - <createData stepKey="product1" entity="SimpleProduct"> + <createData entity="SimpleSubCategory" stepKey="category"/> + <createData entity="SimpleProduct" stepKey="product1"> <requiredEntity createDataKey="category"/> </createData> - <createData stepKey="product2" entity="SimpleProduct"> + <createData entity="SimpleProduct" stepKey="product2"> <requiredEntity createDataKey="category"/> </createData> <createData entity="Simple_US_Customer_Two_Addresses" stepKey="customer"/> - <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShipping"/> - <createData entity="FlatRateShippingMethodDefault" stepKey="enableFlatRateShipping"/> - <magentoCLI command="config:set payment/checkmo/active 1" stepKey="enableCheckMoneyOrderPaymentMethod"/> + <!-- Set configurations --> + <magentoCLI command="config:set {{EnableMultiShippingCheckoutMultiple.path}} {{EnableMultiShippingCheckoutMultiple.value}}" stepKey="allowShippingToMultipleAddresses"/> + <magentoCLI command="config:set {{EnableFreeShippingMethod.path}} {{EnableFreeShippingMethod.value}}" stepKey="enableFreeShipping"/> + <magentoCLI command="config:set {{EnableFlatRateShippingMethod.path}} {{EnableFlatRateShippingMethod.value}}" stepKey="enableFlatRateShipping"/> + <magentoCLI command="config:set {{EnableCheckMoneyOrderPaymentMethod.path}} {{EnableCheckMoneyOrderPaymentMethod.value}}" stepKey="enableCheckMoneyOrderPaymentMethod"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> <argument name="Customer" value="$$customer$$"/> </actionGroup> </before> - - <amOnPage url="$$product1.name$$.html" stepKey="goToProduct1"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProduct1"> - <argument name="productName" value="$$product1.name$$"/> + <after> + <actionGroup ref="StorefrontSignOutActionGroup" stepKey="customerLogout"/> + <magentoCLI command="config:set {{DisableMultiShippingCheckoutMultiple.path}} {{DisableMultiShippingCheckoutMultiple.value}}" stepKey="withdrawShippingToMultipleAddresses"/> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + <deleteData createDataKey="product1" stepKey="deleteProduct1"/> + <deleteData createDataKey="product2" stepKey="deleteProduct2"/> + <deleteData createDataKey="customer" stepKey="deleteCustomer"/> + <magentoCLI command="config:set {{DisableFreeShippingMethod.path}} {{DisableFreeShippingMethod.value}}" stepKey="disableFreeShipping"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearAllFilters"/> + <actionGroup ref="logout" stepKey="logoutAdmin"/> + </after> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addSimpleProduct1ToCart"> + <argument name="product" value="$$product1$$"/> </actionGroup> - <amOnPage url="$$product2.name$$.html" stepKey="goToProduct2"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addToCartFromStorefrontProduct2"> - <argument name="productName" value="$$product2.name$$"/> + <waitForPageLoad stepKey="waitForSecondProductPageLoad"/> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addSimpleProduct2ToCart"> + <argument name="product" value="$$product2$$"/> </actionGroup> <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openCart"/> <actionGroup ref="CheckingWithMultipleAddressesActionGroup" stepKey="checkoutWithMultipleAddresses"/> + <waitForPageLoad stepKey="waitForShippingInfoPageLoad"/> <actionGroup ref="SelectMultiShippingInfoActionGroup" stepKey="checkoutWithMultipleShipping"/> + <!--Select Check / Money order Payment method--> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> <actionGroup ref="SelectBillingInfoActionGroup" stepKey="checkoutWithPaymentMethod"/> - <actionGroup ref="ReviewOrderForMultiShipmentActionGroup" stepKey="reviewOrderForMultiShipment"/> - <actionGroup ref="PlaceOrderActionGroup" stepKey="placeOrder"/> + <waitForPageLoad stepKey="waitForReviewOrderPageLoad"/> + <actionGroup ref="ReviewOrderForMultiShipmentActionGroup" stepKey="reviewOrderForMultiShipment"> + <argument name="totalNameForFirstOrder" value="Shipping & Handling"/> + <argument name="totalPositionForFirstOrder" value="1"/> + <argument name="totalNameForSecondOrder" value="Shipping & Handling"/> + <argument name="totalPositionForSecondOrder" value="2"/> + </actionGroup> + <waitForPageLoad stepKey="waitForPlaceOrderPageLoad"/> + <actionGroup ref="StorefrontPlaceOrderForMultipleAddressesActionGroup" stepKey="placeOrder"> + <argument name="firstOrderPosition" value="1"/> + <argument name="secondOrderPosition" value="2"/> + </actionGroup> + <waitForPageLoad stepKey="waitForOrderPageLoad"/> <amOnPage url="{{StorefrontCustomerOrdersHistoryPage.url}}" stepKey="goToSalesOrder"/> - <actionGroup ref="SalesOrderForMultiShipmentActionGroup" stepKey="salesOrderForMultiShipment"/> + <actionGroup ref="AssertStorefrontSalesOrderMatchesGrandTotalActionGroup" stepKey="checkSalesOrderForFirstOrder"> + <argument name="dataHref" value="$dataHrefForFirstOrderPlaceOrder"/> + </actionGroup> + <amOnPage url="{{StorefrontCustomerOrdersHistoryPage.url}}" stepKey="goToSalesOrder2"/> + <waitForPageLoad stepKey="waitForOrderPageLoad2"/> + <actionGroup ref="AssertStorefrontSalesOrderMatchesGrandTotalActionGroup" stepKey="checkSalesOrderForSecondOrder"> + <argument name="dataHref" value="$dataHrefForSecondOrderPlaceOrder"/> + </actionGroup> <waitForPageLoad stepKey="waitForAdminPageToLoad"/> <!-- Go to Stores > Configuration > Sales > Orders --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onAdminOrdersPage"/> - <actionGroup ref="AdminSalesOrderActionGroup" stepKey="ValidateOrderTotals"/> - <after> - <deleteData stepKey="deleteCategory" createDataKey="category"/> - <deleteData stepKey="deleteProduct1" createDataKey="product1"/> - <deleteData stepKey="deleteProduct2" createDataKey="product2"/> - <deleteData stepKey="deleteCustomer" createDataKey="customer"/> - <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShipping"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> + <waitForPageLoad stepKey="waitForOrderPageLoad3"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFilters"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> + <!--Assert order in orders grid --> + <!-- Go to order page --> + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openFirstOrderPage"> + <argument name="orderId" value="{$getFirstOrderIdPlaceOrder}"/> + </actionGroup> + <!-- Check status --> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeFirstOrderPendingStatus"/> + <actionGroup ref="AdminSalesOrderActionGroup" stepKey="validateOrderTotalsForFirstOrder"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onAdminOrdersPage2"/> + <waitForPageLoad stepKey="waitForOrderPageLoad4"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFilters2"/> + <!-- Go to order page --> + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openSecondOrderPage"> + <argument name="orderId" value="{$getSecondOrderIdPlaceOrder}"/> + </actionGroup> + <!-- Check status --> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeSecondOrderPendingStatus"/> + <actionGroup ref="AdminSalesOrderActionGroup" stepKey="validateOrderTotalsForSecondOrder"/> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="gotToHomePage"/> </test> </tests> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckingWithCartPriceRuleMatchingSubtotalForMultiShipmentTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckingWithCartPriceRuleMatchingSubtotalForMultiShipmentTest.xml new file mode 100644 index 0000000000000..02187658a8781 --- /dev/null +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckingWithCartPriceRuleMatchingSubtotalForMultiShipmentTest.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="StorefrontCheckingWithCartPriceRuleMatchingSubtotalForMultiShipmentTest" extends="StoreFrontCheckingWithMultishipmentTest"> + <annotations> + <features value="Multi shipment and Cart Price Rule"/> + <stories value="Checking cart price rule for multi shipment with multiple shipment addresses on front end order page"/> + <title value="Checking sub total amount and free shipping is applied with multiple shipment addresses on front end order page"/> + <description value="Cart Price Rules not working and free shipping not applied for Multi shipping "/> + <severity value="MAJOR"/> + <testCaseId value="MC-21738"/> + <group value="Multishipment"/> + <group value="SalesRule"/> + </annotations> + <before> + <magentoCLI command="config:set multishipping/options/checkout_multiple 1" stepKey="allowShippingToMultipleAddresses"/> + </before> + <after> + <magentoCLI command="config:set multishipping/options/checkout_multiple 0" stepKey="disableShippingToMultipleAddresses"/> + </after> + <actionGroup ref="AdminCreateCartPriceRuleActionsWithSubtotalActionGroup" before="goToProduct1" stepKey="createSubtotalCartPriceRuleActionsSection"> + <argument name="ruleName" value="CartPriceRuleConditionForSubtotalForMultiShipping"/> + </actionGroup> + <actionGroup ref="DeleteCartPriceRuleByName" after="placeOrder" stepKey="deleteCreatedCartPriceRule"> + <argument name="ruleName" value="{$getSubtotalRuleCreateSubtotalCartPriceRuleActionsSection}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckoutWithMultipleAddressesTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckoutWithMultipleAddressesTest.xml index 138ab5df26ab0..dc786f9cbc5db 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckoutWithMultipleAddressesTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckoutWithMultipleAddressesTest.xml @@ -51,7 +51,7 @@ <argument name="productUrl" value="$$firstProduct.custom_attributes[url_key]$$"/> </actionGroup> <!-- Add the first product to the Shopping Cart --> - <actionGroup ref="AddProductWithQtyToCartFromStorefrontProductPage" stepKey="addFirstProductToCart"> + <actionGroup ref="AddProductWithQtyToCartFromStorefrontProductPageActionGroup" stepKey="addFirstProductToCart"> <argument name="productName" value="$$firstProduct.name$$"/> <argument name="productQty" value="1"/> </actionGroup> @@ -60,7 +60,7 @@ <argument name="productUrl" value="$$secondProduct.custom_attributes[url_key]$$"/> </actionGroup> <!-- Add the second product to the Shopping Cart --> - <actionGroup ref="AddProductWithQtyToCartFromStorefrontProductPage" stepKey="addSecondProductToCart"> + <actionGroup ref="AddProductWithQtyToCartFromStorefrontProductPageActionGroup" stepKey="addSecondProductToCart"> <argument name="productName" value="$$secondProduct.name$$"/> <argument name="productQty" value="1"/> </actionGroup> @@ -89,7 +89,7 @@ <argument name="productUrl" value="$$firstProduct.custom_attributes[url_key]$$"/> </actionGroup> <!-- Add three identical products to the Shopping Cart --> - <actionGroup ref="AddProductWithQtyToCartFromStorefrontProductPage" stepKey="addIdenticalProductsToCart"> + <actionGroup ref="AddProductWithQtyToCartFromStorefrontProductPageActionGroup" stepKey="addIdenticalProductsToCart"> <argument name="productName" value="$$firstProduct.name$$"/> <argument name="productQty" value="3"/> </actionGroup> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontProcessMultishippingCheckoutWhenCartPageIsOpenedInAnotherTabTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontProcessMultishippingCheckoutWhenCartPageIsOpenedInAnotherTabTest.xml index fd79d4d954cd4..f25ac203c3fa5 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontProcessMultishippingCheckoutWhenCartPageIsOpenedInAnotherTabTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontProcessMultishippingCheckoutWhenCartPageIsOpenedInAnotherTabTest.xml @@ -62,7 +62,7 @@ <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnShoppingCartPage"/> <!-- Click 'Check Out with Multiple Addresses' --> <waitForPageLoad stepKey="waitForSecondPageLoad"/> - <actionGroup ref="StorefrontGoCheckoutWithMultipleAddresses" stepKey="goCheckoutWithMultipleAddresses"/> + <actionGroup ref="StorefrontGoCheckoutWithMultipleAddressesActionGroup" stepKey="goCheckoutWithMultipleAddresses"/> <!-- Select different addresses and click 'Go to Shipping Information' --> <actionGroup ref="StorefrontCheckoutShippingSelectMultipleAddressesActionGroup" stepKey="selectMultipleAddresses"> <argument name="firstAddress" value="{{UK_Not_Default_Address.street[0]}}"/> @@ -105,11 +105,11 @@ <waitForPageLoad stepKey="waitForOrderPageLoad"/> <!-- Go to Admin > Sales > Orders --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> - <actionGroup ref="searchAdminDataGridByKeyword" stepKey="searchFirstOrder"> + <actionGroup ref="SearchAdminDataGridByKeywordActionGroup" stepKey="searchFirstOrder"> <argument name="keyword" value="$grabFirstOrderId"/> </actionGroup> <seeElement selector="{{AdminOrdersGridSection.orderId({$grabFirstOrderId})}}" stepKey="seeAdminFirstOrder"/> - <actionGroup ref="searchAdminDataGridByKeyword" stepKey="searchSecondOrder"> + <actionGroup ref="SearchAdminDataGridByKeywordActionGroup" stepKey="searchSecondOrder"> <argument name="keyword" value="$grabSecondOrderId"/> </actionGroup> <seeElement selector="{{AdminOrdersGridSection.orderId({$grabSecondOrderId})}}" stepKey="seeAdminSecondOrder"/> diff --git a/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php b/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php index 731365974c235..fba3245bec68d 100644 --- a/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php +++ b/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php @@ -420,13 +420,16 @@ public function testSetShippingMethods() $methodsArray = [1 => 'flatrate_flatrate', 2 => 'tablerate_bestway']; $addressId = 1; $addressMock = $this->getMockBuilder(QuoteAddress::class) - ->setMethods(['getId', 'setShippingMethod']) + ->setMethods(['getId', 'setShippingMethod', 'setCollectShippingRates']) ->disableOriginalConstructor() ->getMock(); $addressMock->expects($this->once())->method('getId')->willReturn($addressId); $this->quoteMock->expects($this->once())->method('getAllShippingAddresses')->willReturn([$addressMock]); $addressMock->expects($this->once())->method('setShippingMethod')->with($methodsArray[$addressId]); + $addressMock->expects($this->once()) + ->method('setCollectShippingRates') + ->with(true); $this->quoteMock->expects($this->once()) ->method('__call') ->with('setTotalsCollectedFlag', [false]) diff --git a/app/code/Magento/Multishipping/registration.php b/app/code/Magento/Multishipping/registration.php index 6598a8e431ca8..f19d2c4d740d8 100644 --- a/app/code/Magento/Multishipping/registration.php +++ b/app/code/Magento/Multishipping/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Multishipping', __DIR__); diff --git a/app/code/Magento/MysqlMq/Model/Driver/Bulk/Exchange.php b/app/code/Magento/MysqlMq/Model/Driver/Bulk/Exchange.php index 247a44667be06..718fba0a1a1ad 100644 --- a/app/code/Magento/MysqlMq/Model/Driver/Bulk/Exchange.php +++ b/app/code/Magento/MysqlMq/Model/Driver/Bulk/Exchange.php @@ -3,10 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\MysqlMq\Model\Driver\Bulk; use Magento\Framework\MessageQueue\Bulk\ExchangeInterface; -use Magento\Framework\MessageQueue\ConfigInterface as MessageQueueConfig; +use Magento\Framework\MessageQueue\Topology\ConfigInterface as MessageQueueConfig; +use Magento\MysqlMq\Model\ConnectionTypeResolver; use Magento\MysqlMq\Model\QueueManagement; /** @@ -14,6 +16,11 @@ */ class Exchange implements ExchangeInterface { + /** + * @var ConnectionTypeResolver + */ + private $connectionTypeResolver; + /** * @var MessageQueueConfig */ @@ -27,13 +34,18 @@ class Exchange implements ExchangeInterface /** * Initialize dependencies. * + * @param ConnectionTypeResolver $connectionTypeResolver * @param MessageQueueConfig $messageQueueConfig * @param QueueManagement $queueManagement */ - public function __construct(MessageQueueConfig $messageQueueConfig, QueueManagement $queueManagement) - { + public function __construct( + ConnectionTypeResolver $connectionTypeResolver, + MessageQueueConfig $messageQueueConfig, + QueueManagement $queueManagement + ) { $this->messageQueueConfig = $messageQueueConfig; $this->queueManagement = $queueManagement; + $this->connectionTypeResolver = $connectionTypeResolver; } /** @@ -41,7 +53,20 @@ public function __construct(MessageQueueConfig $messageQueueConfig, QueueManagem */ public function enqueue($topic, array $envelopes) { - $queueNames = $this->messageQueueConfig->getQueuesByTopic($topic); + $queueNames = []; + $exchanges = $this->messageQueueConfig->getExchanges(); + foreach ($exchanges as $exchange) { + $connection = $exchange->getConnection(); + if ($this->connectionTypeResolver->getConnectionType($connection)) { + foreach ($exchange->getBindings() as $binding) { + // This only supports exact matching of topics. + if ($binding->getTopic() === $topic) { + $queueNames[] = $binding->getDestination(); + } + } + } + } + $messages = array_map( function ($envelope) { return $envelope->getBody(); diff --git a/app/code/Magento/MysqlMq/Model/Driver/Exchange.php b/app/code/Magento/MysqlMq/Model/Driver/Exchange.php index b6050c6b3d0b6..3e9b131fa8d1c 100644 --- a/app/code/Magento/MysqlMq/Model/Driver/Exchange.php +++ b/app/code/Magento/MysqlMq/Model/Driver/Exchange.php @@ -3,15 +3,25 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\MysqlMq\Model\Driver; use Magento\Framework\MessageQueue\EnvelopeInterface; use Magento\Framework\MessageQueue\ExchangeInterface; -use Magento\Framework\MessageQueue\ConfigInterface as MessageQueueConfig; +use Magento\Framework\MessageQueue\Topology\ConfigInterface as MessageQueueConfig; +use Magento\MysqlMq\Model\ConnectionTypeResolver; use Magento\MysqlMq\Model\QueueManagement; +/** + * Class Exchange + */ class Exchange implements ExchangeInterface { + /** + * @var ConnectionTypeResolver + */ + private $connectionTypeResolver; + /** * @var MessageQueueConfig */ @@ -25,13 +35,18 @@ class Exchange implements ExchangeInterface /** * Initialize dependencies. * + * @param ConnectionTypeResolver $connectionTypeResolver * @param MessageQueueConfig $messageQueueConfig * @param QueueManagement $queueManagement */ - public function __construct(MessageQueueConfig $messageQueueConfig, QueueManagement $queueManagement) - { + public function __construct( + ConnectionTypeResolver $connectionTypeResolver, + MessageQueueConfig $messageQueueConfig, + QueueManagement $queueManagement + ) { $this->messageQueueConfig = $messageQueueConfig; $this->queueManagement = $queueManagement; + $this->connectionTypeResolver = $connectionTypeResolver; } /** @@ -43,7 +58,18 @@ public function __construct(MessageQueueConfig $messageQueueConfig, QueueManagem */ public function enqueue($topic, EnvelopeInterface $envelope) { - $queueNames = $this->messageQueueConfig->getQueuesByTopic($topic); + $queueNames = []; + $exchanges = $this->messageQueueConfig->getExchanges(); + foreach ($exchanges as $exchange) { + $connection = $exchange->getConnection(); + if ($this->connectionTypeResolver->getConnectionType($connection)) { + foreach ($exchange->getBindings() as $binding) { + if ($binding->getTopic() == $topic) { + $queueNames[] = $binding->getDestination(); + } + } + } + } $this->queueManagement->addMessageToQueues($topic, $envelope->getBody(), $queueNames); return null; } diff --git a/app/code/Magento/MysqlMq/Setup/Recurring.php b/app/code/Magento/MysqlMq/Setup/Recurring.php index db3a39bf5fbd0..57f3931cee8d8 100644 --- a/app/code/Magento/MysqlMq/Setup/Recurring.php +++ b/app/code/Magento/MysqlMq/Setup/Recurring.php @@ -3,12 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\MysqlMq\Setup; use Magento\Framework\Setup\InstallSchemaInterface; use Magento\Framework\Setup\ModuleContextInterface; use Magento\Framework\Setup\SchemaSetupInterface; -use Magento\Framework\MessageQueue\ConfigInterface as MessageQueueConfig; +use Magento\Framework\MessageQueue\Topology\ConfigInterface as MessageQueueConfig; /** * Class Recurring @@ -29,17 +30,17 @@ public function __construct(MessageQueueConfig $messageQueueConfig) } /** - * {@inheritdoc} + * @inheritdoc */ public function install(SchemaSetupInterface $setup, ModuleContextInterface $context) { $setup->startSetup(); - $binds = $this->messageQueueConfig->getBinds(); $queues = []; - foreach ($binds as $bind) { - $queues[] = $bind[MessageQueueConfig::BIND_QUEUE]; + foreach ($this->messageQueueConfig->getQueues() as $queue) { + $queues[] = $queue->getName(); } + $connection = $setup->getConnection(); $existingQueues = $connection->fetchCol($connection->select()->from($setup->getTable('queue'), 'name')); $queues = array_unique(array_diff($queues, $existingQueues)); diff --git a/app/code/Magento/MysqlMq/Test/Unit/Model/Driver/Bulk/ExchangeTest.php b/app/code/Magento/MysqlMq/Test/Unit/Model/Driver/Bulk/ExchangeTest.php index 452825058c9d8..2f4b1350568d1 100644 --- a/app/code/Magento/MysqlMq/Test/Unit/Model/Driver/Bulk/ExchangeTest.php +++ b/app/code/Magento/MysqlMq/Test/Unit/Model/Driver/Bulk/ExchangeTest.php @@ -25,6 +25,10 @@ class ExchangeTest extends \PHPUnit\Framework\TestCase * @var \Magento\MysqlMq\Model\Driver\Bulk\Exchange */ private $exchange; + /** + * @var \Magento\MysqlMq\Model\ConnectionTypeResolver|\PHPUnit_Framework_MockObject_MockObject + */ + private $connnectionTypeResolver; /** * Set up. @@ -33,15 +37,20 @@ class ExchangeTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $this->messageQueueConfig = $this->getMockBuilder(\Magento\Framework\MessageQueue\ConfigInterface::class) + $this->messageQueueConfig = $this->getMockBuilder( + \Magento\Framework\MessageQueue\Topology\ConfigInterface::class + ) ->disableOriginalConstructor()->getMock(); $this->queueManagement = $this->getMockBuilder(\Magento\MysqlMq\Model\QueueManagement::class) ->disableOriginalConstructor()->getMock(); + $this->connnectionTypeResolver = $this->getMockBuilder(\Magento\MysqlMq\Model\ConnectionTypeResolver::class) + ->disableOriginalConstructor()->getMock(); $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->exchange = $objectManager->getObject( \Magento\MysqlMq\Model\Driver\Bulk\Exchange::class, [ + 'connectionTypeResolver' => $this->connnectionTypeResolver, 'messageQueueConfig' => $this->messageQueueConfig, 'queueManagement' => $this->queueManagement, ] @@ -56,10 +65,46 @@ protected function setUp() public function testEnqueue() { $topicName = 'topic.name'; - $queueNames = ['queue0', 'queue1']; + $queueNames = ['queue0']; + $binding1 = $this->createMock( + \Magento\Framework\MessageQueue\Topology\Config\ExchangeConfigItem\BindingInterface::class + ); + $binding1->expects($this->once()) + ->method('getTopic') + ->willReturn($topicName); + $binding1->expects($this->once()) + ->method('getDestination') + ->willReturn($queueNames[0]); + $binding2 = $this->createMock( + \Magento\Framework\MessageQueue\Topology\Config\ExchangeConfigItem\BindingInterface::class + ); + $binding2->expects($this->once()) + ->method('getTopic') + ->willReturn('different.topic'); + $binding2->expects($this->never()) + ->method('getDestination'); + $exchange1 = $this->createMock( + \Magento\Framework\MessageQueue\Topology\Config\ExchangeConfigItemInterface::class + ); + $exchange1->expects($this->once()) + ->method('getConnection') + ->willReturn('db'); + $exchange1->expects($this->once()) + ->method('getBindings') + ->willReturn([$binding1, $binding2]); + $exchange2 = $this->createMock( + \Magento\Framework\MessageQueue\Topology\Config\ExchangeConfigItemInterface::class + ); + $exchange2->expects($this->once()) + ->method('getConnection') + ->willReturn('amqp'); + $exchange2->expects($this->never()) + ->method('getBindings'); + + $this->connnectionTypeResolver->method('getConnectionType')->willReturnOnConsecutiveCalls(['db', null]); $envelopeBody = 'serializedMessage'; $this->messageQueueConfig->expects($this->once()) - ->method('getQueuesByTopic')->with($topicName)->willReturn($queueNames); + ->method('getExchanges')->willReturn([$exchange1, $exchange2]); $envelope = $this->getMockBuilder(\Magento\Framework\MessageQueue\EnvelopeInterface::class) ->disableOriginalConstructor()->getMock(); $envelope->expects($this->once())->method('getBody')->willReturn($envelopeBody); diff --git a/app/code/Magento/MysqlMq/Test/Unit/Setup/RecurringTest.php b/app/code/Magento/MysqlMq/Test/Unit/Setup/RecurringTest.php index e2e7ad3c4c92d..ccbe41a4bd705 100644 --- a/app/code/Magento/MysqlMq/Test/Unit/Setup/RecurringTest.php +++ b/app/code/Magento/MysqlMq/Test/Unit/Setup/RecurringTest.php @@ -34,7 +34,9 @@ class RecurringTest extends \PHPUnit\Framework\TestCase protected function setUp() { $this->objectManager = new ObjectManager($this); - $this->messageQueueConfig = $this->getMockBuilder(\Magento\Framework\MessageQueue\ConfigInterface::class) + $this->messageQueueConfig = $this->getMockBuilder( + \Magento\Framework\MessageQueue\Topology\ConfigInterface::class + ) ->getMockForAbstractClass(); $this->model = $this->objectManager->getObject( \Magento\MysqlMq\Setup\Recurring::class, @@ -49,23 +51,14 @@ protected function setUp() */ public function testInstall() { - $binds = [ - 'first_bind' => [ - 'queue' => 'queue_name_1', - 'exchange' => 'magento-db', - 'topic' => 'queue.topic.1' - ], - 'second_bind' => [ - 'queue' => 'queue_name_2', - 'exchange' => 'magento-db', - 'topic' => 'queue.topic.2' - ], - 'third_bind' => [ - 'queue' => 'queue_name_3', - 'exchange' => 'magento-db', - 'topic' => 'queue.topic.3' - ] - ]; + for ($i = 1; $i <= 3; $i++) { + $queue = $this->createMock(\Magento\Framework\MessageQueue\Topology\Config\QueueConfigItemInterface::class); + $queue->expects($this->once()) + ->method('getName') + ->willReturn('queue_name_' . $i); + $queues[] = $queue; + } + $dbQueues = [ 'queue_name_1', 'queue_name_2', @@ -81,7 +74,7 @@ public function testInstall() ->getMockForAbstractClass(); $setup->expects($this->once())->method('startSetup')->willReturnSelf(); - $this->messageQueueConfig->expects($this->once())->method('getBinds')->willReturn($binds); + $this->messageQueueConfig->expects($this->once())->method('getQueues')->willReturn($queues); $connection = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) ->getMockForAbstractClass(); $setup->expects($this->once())->method('getConnection')->willReturn($connection); diff --git a/app/code/Magento/MysqlMq/registration.php b/app/code/Magento/MysqlMq/registration.php index e13a38b468005..b0799f79b463f 100644 --- a/app/code/Magento/MysqlMq/registration.php +++ b/app/code/Magento/MysqlMq/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_MysqlMq', __DIR__); diff --git a/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php b/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php index bce42b4e90074..fa7f2f1090629 100644 --- a/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php +++ b/app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php @@ -5,6 +5,8 @@ */ namespace Magento\NewRelicReporting\Model; +use Exception; + /** * Wrapper for New Relic functions * @@ -31,10 +33,10 @@ public function addCustomParameter($param, $value) /** * Wrapper for 'newrelic_notice_error' function * - * @param \Exception $exception + * @param Exception $exception * @return void */ - public function reportError($exception) + public function reportError(Exception $exception) { if ($this->isExtensionInstalled()) { newrelic_notice_error($exception->getMessage(), $exception); @@ -67,6 +69,19 @@ public function setTransactionName(string $transactionName): void } } + /** + * Wrapper for 'newrelic_end_transaction' + * + * @param bool $ignore + * @return void + */ + public function endTransaction($ignore = false) + { + if ($this->isExtensionInstalled()) { + newrelic_end_transaction($ignore); + } + } + /** * Checks whether newrelic-php5 agent is installed * diff --git a/app/code/Magento/NewRelicReporting/Plugin/CommandPlugin.php b/app/code/Magento/NewRelicReporting/Plugin/CommandPlugin.php index 04ad3d0504d34..d21f972da57c6 100644 --- a/app/code/Magento/NewRelicReporting/Plugin/CommandPlugin.php +++ b/app/code/Magento/NewRelicReporting/Plugin/CommandPlugin.php @@ -25,16 +25,24 @@ class CommandPlugin */ private $newRelicWrapper; + /** + * @var string[] + */ + private $skipCommands; + /** * @param Config $config * @param NewRelicWrapper $newRelicWrapper + * @param array $skipCommands */ public function __construct( Config $config, - NewRelicWrapper $newRelicWrapper + NewRelicWrapper $newRelicWrapper, + array $skipCommands = [] ) { $this->config = $config; $this->newRelicWrapper = $newRelicWrapper; + $this->skipCommands = $skipCommands; } /** @@ -46,10 +54,24 @@ public function __construct( */ public function beforeRun(Command $command, ...$args) { - $this->newRelicWrapper->setTransactionName( - sprintf('CLI %s', $command->getName()) - ); + if (!$this->isCommandSkipped($command)) { + $this->newRelicWrapper->setTransactionName( + sprintf('CLI %s', $command->getName()) + ); + } return $args; } + + /** + * Determines whether the Command is declared to be skipped + * + * @param Command $command + * @return bool + */ + private function isCommandSkipped(Command $command): bool + { + $commandName = $command->getName(); + return isset($this->skipCommands[$commandName]) && $this->skipCommands[$commandName] === true; + } } diff --git a/app/code/Magento/NewRelicReporting/Plugin/StatPlugin.php b/app/code/Magento/NewRelicReporting/Plugin/StatPlugin.php new file mode 100644 index 0000000000000..30dddfe11910a --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Plugin/StatPlugin.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\NewRelicReporting\Plugin; + +use Magento\Framework\Profiler\Driver\Standard\Stat; +use Magento\NewRelicReporting\Model\Config; +use Magento\NewRelicReporting\Model\NewRelicWrapper; +use Psr\Log\LoggerInterface; + +/** + * Class StatPlugin handles single Cron Jobs transaction names + */ +class StatPlugin +{ + public const TIMER_NAME_CRON_PREFIX = 'job'; + + /** + * @var Config + */ + private $config; + + /** + * @var NewRelicWrapper + */ + private $newRelicWrapper; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @param Config $config + * @param NewRelicWrapper $newRelicWrapper + * @param LoggerInterface $logger + */ + public function __construct( + Config $config, + NewRelicWrapper $newRelicWrapper, + LoggerInterface $logger + ) { + $this->config = $config; + $this->newRelicWrapper = $newRelicWrapper; + $this->logger = $logger; + } + + /** + * Before running original profiler, register NewRelic transaction + * + * @param Stat $schedule + * @param array $args + * @return array + * @see \Magento\Cron\Observer\ProcessCronQueueObserver::startProfiling + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeStart(Stat $schedule, ...$args): array + { + $timerName = current($args); + + if ($this->isCronJob($timerName)) { + $this->newRelicWrapper->setTransactionName( + sprintf('Cron %s', $timerName) + ); + } + + return $args; + } + + /** + * Before stopping original profiler, close NewRelic transaction + * + * @param Stat $schedule + * @param array $args + * @return array + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeStop(Stat $schedule, ...$args): array + { + $timerName = current($args); + + if ($this->isCronJob($timerName)) { + $this->newRelicWrapper->endTransaction(); + } + + return $args; + } + + /** + * Determines whether provided name is Cron Job + * + * @param string $timerName + * @return bool + */ + private function isCronJob(string $timerName): bool + { + return 0 === strpos($timerName, static::TIMER_NAME_CRON_PREFIX); + } +} diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminNavigateToNewRelicConfigurationActionGroup.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminNavigateToNewRelicConfigurationActionGroup.xml new file mode 100644 index 0000000000000..9e8314792b0bd --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminNavigateToNewRelicConfigurationActionGroup.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="AdminNavigateToNewRelicConfigurationActionGroup"> + <amOnPage url="{{AdminNewRelicConfigPage.url}}" stepKey="navigateToNewRelicConfigurationPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminToggleNewRelicReportingEnabledActionGroup.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminToggleNewRelicReportingEnabledActionGroup.xml new file mode 100644 index 0000000000000..602484189dda4 --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminToggleNewRelicReportingEnabledActionGroup.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="AdminToggleNewRelicReportingEnabledActionGroup"> + <arguments> + <argument name="state" type="string"/> + </arguments> + <selectOption selector="{{AdminNewRelicConfigSystemSection.status}}" userInput="{{state}}" stepKey="switchActiveState"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminUncheckNewRelicUseSystemValueActionGroup.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminUncheckNewRelicUseSystemValueActionGroup.xml new file mode 100644 index 0000000000000..41f18f0f90d8d --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AdminUncheckNewRelicUseSystemValueActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminUncheckUseSystemValueActionGroup"> + <arguments> + <argument name="rowId" type="string"/> + </arguments> + + <uncheckOption selector="{{AdminConfigSection.useSystemValue(rowId)}}" stepKey="uncheckCheckbox"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminNewRelicConfigFieldIsNotVisibleActionGroup.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminNewRelicConfigFieldIsNotVisibleActionGroup.xml new file mode 100644 index 0000000000000..1c347512c1737 --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminNewRelicConfigFieldIsNotVisibleActionGroup.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="AssertAdminNewRelicConfigFieldIsNotVisibleActionGroup"> + <arguments> + <argument name="config" type="string"/> + </arguments> + <dontSeeElement selector="{{config}}" stepKey="dontSeeConfigField"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminNewRelicConfigFieldIsVisibleActionGroup.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminNewRelicConfigFieldIsVisibleActionGroup.xml new file mode 100644 index 0000000000000..fd3b3e47719c0 --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/ActionGroup/AssertAdminNewRelicConfigFieldIsVisibleActionGroup.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="AssertAdminNewRelicConfigFieldIsVisibleActionGroup"> + <arguments> + <argument name="config" type="string"/> + </arguments> + <seeElement selector="{{config}}" stepKey="seeConfigField"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/Page/AdminNewRelicConfigPage.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/Page/AdminNewRelicConfigPage.xml new file mode 100644 index 0000000000000..fda7d0ef336b1 --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/Page/AdminNewRelicConfigPage.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminNewRelicConfigPage" url="admin/system_config/edit/section/newrelicreporting/" area="admin" module="Magento_Config"> + <section name="AdminNewRelicConfigSystemSection"/> + </page> +</pages> \ No newline at end of file diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/Section/AdminNewRelicConfigSystemSection.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/Section/AdminNewRelicConfigSystemSection.xml new file mode 100644 index 0000000000000..5bf849cd0134e --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/Section/AdminNewRelicConfigSystemSection.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="AdminNewRelicConfigSystemSection"> + <element name="status" type="select" selector="#row_newrelicreporting_general_enable [data-ui-id='select-groups-general-fields-enable-value']"/> + <element name="apiUrl" type="input" selector="input#newrelicreporting_general_api_url"/> + </section> +</sections> diff --git a/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminCheckNewRelicSystemConfigDependencyTest.xml b/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminCheckNewRelicSystemConfigDependencyTest.xml new file mode 100644 index 0000000000000..c343f93de2c58 --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Mftf/Test/AdminCheckNewRelicSystemConfigDependencyTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckNewRelicSystemConfigDependencyTest"> + <annotations> + <features value="NewRelicReporting"/> + <stories value="Admin is able to see the configuration fields only after enabling the feature"/> + <title value="Admin can see the configuration fields only after enabling the feature"/> + <description value="The system configs should be available only after enabling the New Relic feature."/> + <severity value="MINOR"/> + <group value="NewRelicReporting"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminNavigateToNewRelicConfigurationActionGroup" stepKey="goToConfigPage"/> + <actionGroup ref="AdminExpandConfigSectionActionGroup" stepKey="expandingGeneralSection"> + <argument name="sectionName" value="General"/> + </actionGroup> + </before> + <after> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + </after> + <actionGroup ref="AssertAdminNewRelicConfigFieldIsNotVisibleActionGroup" stepKey="checkingIfApiUrlIsNotVisible"> + <argument name="config" value="{{AdminNewRelicConfigSystemSection.apiUrl}}"/> + </actionGroup> + <actionGroup ref="AdminUncheckUseSystemValueActionGroup" stepKey="uncheckUseSystemValue"> + <argument name="rowId" value="row_newrelicreporting_general_enable"/> + </actionGroup> + <actionGroup ref="AdminToggleNewRelicReportingEnabledActionGroup" stepKey="enablingNewRelicReporting"> + <argument name="state" value="Yes"/> + </actionGroup> + <actionGroup ref="AssertAdminNewRelicConfigFieldIsVisibleActionGroup" stepKey="checkingIfApiUrlIsVisible"> + <argument name="config" value="{{AdminNewRelicConfigSystemSection.apiUrl}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/NewRelicReporting/Test/Unit/Plugin/CommandPluginTest.php b/app/code/Magento/NewRelicReporting/Test/Unit/Plugin/CommandPluginTest.php new file mode 100644 index 0000000000000..f75997a6302bb --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Unit/Plugin/CommandPluginTest.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\NewRelicReporting\Test\Unit\Plugin; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\NewRelicReporting\Model\NewRelicWrapper; +use Magento\NewRelicReporting\Plugin\CommandPlugin; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Command\Command; + +class CommandPluginTest extends TestCase +{ + private const STUB_SKIPPED_COMMAND_NAME = 'skippedCommand'; + private const STUB_NON_SKIPPED_COMMAND_NAME = 'nonSkippedCommand'; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var MockObject|NewRelicWrapper + */ + private $newRelicWrapperMock; + + /** + * ObjectManager and mocks necessary to run the tests + */ + protected function setUp() + { + $this->newRelicWrapperMock = $this->getMockBuilder(NewRelicWrapper::class) + ->disableOriginalConstructor() + ->setMethods(['setTransactionName']) + ->getMock(); + + $this->objectManager = new ObjectManager($this); + } + + /** + * When Command name is not in the list of skipped, handle New Relic transaction + */ + public function testNewRelicTransactionSetForNonSkippedCommand() + { + $nonSkippedCommand = $this->getCommandMock(self::STUB_NON_SKIPPED_COMMAND_NAME); + + $this->newRelicWrapperMock->expects($this->once()) + ->method('setTransactionName') + ->with(sprintf('CLI %s', self::STUB_NON_SKIPPED_COMMAND_NAME)); + + $commandPlugin = $this->getCommandPlugin([self::STUB_SKIPPED_COMMAND_NAME => true]); + $commandPlugin->beforeRun($nonSkippedCommand); + } + + /** + * When Command name is set to be skipped, do not let run New Relic transaction + */ + public function testNewRelicTransactionOmmitForSkippedCommand() + { + $skippedCommand = $this->getCommandMock(self::STUB_SKIPPED_COMMAND_NAME); + + $this->newRelicWrapperMock->expects($this->never()) + ->method('setTransactionName'); + + $commandPlugin = $this->getCommandPlugin([self::STUB_SKIPPED_COMMAND_NAME => true]); + $commandPlugin->beforeRun($skippedCommand); + } + + /** + * @param string $commandName + * @return Command|MockObject + */ + private function getCommandMock(string $commandName): Command + { + $commandMock = $this->getMockBuilder(Command::class) + ->disableOriginalConstructor() + ->setMethods(['getName']) + ->getMock(); + + $commandMock->method('getName') + ->willReturn($commandName); + + return $commandMock; + } + + /** + * @param string[] $skippedCommands + * @return CommandPlugin + */ + private function getCommandPlugin(array $skippedCommands): CommandPlugin + { + /** @var CommandPlugin $commandPlugin */ + $commandPlugin = $this->objectManager->getObject(CommandPlugin::class, [ + 'skipCommands' => $skippedCommands, + 'newRelicWrapper' => $this->newRelicWrapperMock + ]); + + return $commandPlugin; + } +} diff --git a/app/code/Magento/NewRelicReporting/Test/Unit/Plugin/StatPluginTest.php b/app/code/Magento/NewRelicReporting/Test/Unit/Plugin/StatPluginTest.php new file mode 100644 index 0000000000000..163f9a69992ed --- /dev/null +++ b/app/code/Magento/NewRelicReporting/Test/Unit/Plugin/StatPluginTest.php @@ -0,0 +1,97 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\NewRelicReporting\Test\Unit\Plugin; + +use Magento\Framework\Profiler\Driver\Standard\Stat; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\NewRelicReporting\Model\NewRelicWrapper; +use Magento\NewRelicReporting\Plugin\StatPlugin; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +class StatPluginTest extends TestCase +{ + private const STAT_NAME_NOT_CRON_JOB = 'NotCronJob'; + private const STAT_NAME_CRON_JOB = StatPlugin::TIMER_NAME_CRON_PREFIX . 'Name'; + + /** + * @var StatPlugin + */ + private $statPlugin; + + /** + * @var MockObject|NewRelicWrapper + */ + private $newRelicWrapperMock; + + /** + * @var MockObject|Stat + */ + private $statMock; + + /** + * Build class for testing + */ + public function setUp() + { + $objectManager = new ObjectManager($this); + + $this->statPlugin = $objectManager->getObject(StatPlugin::class, [ + 'newRelicWrapper' => $this->getNewRelicWrapperMock() + ]); + + $this->statMock = $this->getMockBuilder(Stat::class)->disableOriginalConstructor()->getMock(); + } + + /** + * Expects that NewRelic wrapper will never be called + */ + public function testNewRelicTransactionNameIsNotSetIfNotCronjobPattern() + { + $this->newRelicWrapperMock + ->expects($this->never()) + ->method('setTransactionName'); + $this->newRelicWrapperMock + ->expects($this->never()) + ->method('endTransaction'); + + $this->statPlugin->beforeStart($this->statMock, self::STAT_NAME_NOT_CRON_JOB); + $this->statPlugin->beforeStop($this->statMock, self::STAT_NAME_NOT_CRON_JOB); + } + + /** + * NewRelic Wrapper is called when Task name fits Cron Job pattern + */ + public function testNewRelicTransactionNameIsSetForCronjobNamePattern() + { + $this->newRelicWrapperMock + ->expects($this->once()) + ->method('setTransactionName'); + $this->newRelicWrapperMock + ->expects($this->once()) + ->method('endTransaction'); + + $this->statPlugin->beforeStart($this->statMock, self::STAT_NAME_CRON_JOB); + $this->statPlugin->beforeStop($this->statMock, self::STAT_NAME_CRON_JOB); + } + + /** + * @return NewRelicWrapper + */ + private function getNewRelicWrapperMock(): NewRelicWrapper + { + if (null === $this->newRelicWrapperMock) { + $this->newRelicWrapperMock = $this->getMockBuilder(NewRelicWrapper::class) + ->disableOriginalConstructor() + ->setMethods(['setTransactionName', 'endTransaction']) + ->getMock(); + } + + return $this->newRelicWrapperMock; + } +} diff --git a/app/code/Magento/NewRelicReporting/etc/adminhtml/system.xml b/app/code/Magento/NewRelicReporting/etc/adminhtml/system.xml index 98f9c55adbdf0..60c52164021d9 100644 --- a/app/code/Magento/NewRelicReporting/etc/adminhtml/system.xml +++ b/app/code/Magento/NewRelicReporting/etc/adminhtml/system.xml @@ -19,37 +19,61 @@ </field> <field id="api_url" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>New Relic API URL</label> + <depends> + <field id="enable">1</field> + </depends> </field> <field id="insights_api_url" translate="label comment" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Insights API URL</label> <comment>Use %s to replace the account ID in the URL</comment> + <depends> + <field id="enable">1</field> + </depends> </field> <field id="account_id" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>New Relic Account ID</label> <comment><![CDATA["Need a New Relic account? <a href="http://www.newrelic.com/magento" target="_blank">Click here to get one]]></comment> + <depends> + <field id="enable">1</field> + </depends> </field> <field id="app_id" translate="label comment" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1"> <label>New Relic Application ID</label> <comment>This can commonly be found at the end of the URL when viewing the APM after "/applications/"</comment> + <depends> + <field id="enable">1</field> + </depends> </field> <field id="api" translate="label comment" type="obscure" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="1"> <label>New Relic API Key</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> <comment>This is located by navigating to Events -> Deployments from the New Relic APM website</comment> + <depends> + <field id="enable">1</field> + </depends> </field> <field id="insights_insert_key" translate="label comment" type="obscure" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Insights API Key</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> <comment>Generated under Insights in Manage data -> API Keys -> Insert Keys</comment> + <depends> + <field id="enable">1</field> + </depends> </field> <field id="app_name" translate="label comment" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="1"> <label>New Relic Application Name</label> <comment>This is located by navigating to Settings from the New Relic APM website</comment> + <depends> + <field id="enable">1</field> + </depends> </field> <field id="separate_apps" translate="label comment" type="select" sortOrder="9" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Adminhtml and Frontend as Separate Apps</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>In addition to the main app (which includes all PHP execution), separate apps for adminhtml and frontend will be created. Requires New Relic Application Name to be set.</comment> + <depends> + <field id="enable">1</field> + </depends> </field> </group> <group id="cron" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1"> diff --git a/app/code/Magento/NewRelicReporting/etc/di.xml b/app/code/Magento/NewRelicReporting/etc/di.xml index 15516f6df89be..cd8b0f46087a4 100644 --- a/app/code/Magento/NewRelicReporting/etc/di.xml +++ b/app/code/Magento/NewRelicReporting/etc/di.xml @@ -41,6 +41,16 @@ </arguments> </type> <type name="Symfony\Component\Console\Command\Command"> - <plugin name="newrelic-describe-commands" type="Magento\NewRelicReporting\Plugin\CommandPlugin"/> + <plugin name="newrelic-describe-commands" type="Magento\NewRelicReporting\Plugin\CommandPlugin"/> + </type> + <type name="Magento\Framework\Profiler\Driver\Standard\Stat"> + <plugin name="newrelic-describe-cronjobs" type="Magento\NewRelicReporting\Plugin\StatPlugin"/> + </type> + <type name="Magento\NewRelicReporting\Plugin\CommandPlugin"> + <arguments> + <argument name="skipCommands" xsi:type="array"> + <item xsi:type="boolean" name="cron:run">true</item> + </argument> + </arguments> </type> </config> diff --git a/app/code/Magento/NewRelicReporting/registration.php b/app/code/Magento/NewRelicReporting/registration.php index 39984a11e26a3..8d43f8d1de563 100644 --- a/app/code/Magento/NewRelicReporting/registration.php +++ b/app/code/Magento/NewRelicReporting/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_NewRelicReporting', __DIR__); diff --git a/app/code/Magento/Newsletter/Controller/Adminhtml/Template/Preview.php b/app/code/Magento/Newsletter/Controller/Adminhtml/Template/Preview.php index 9fd9f4335b5c5..c5ed6fb55c48b 100644 --- a/app/code/Magento/Newsletter/Controller/Adminhtml/Template/Preview.php +++ b/app/code/Magento/Newsletter/Controller/Adminhtml/Template/Preview.php @@ -6,12 +6,14 @@ */ namespace Magento\Newsletter\Controller\Adminhtml\Template; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Newsletter\Controller\Adminhtml\Template; /** * View a rendered template. */ -class Preview extends \Magento\Newsletter\Controller\Adminhtml\Template implements HttpGetActionInterface +class Preview extends Template implements HttpPostActionInterface, HttpGetActionInterface { /** * Preview Newsletter template @@ -25,7 +27,7 @@ public function execute() $data = $this->getRequest()->getParams(); $isEmptyRequestData = empty($data) || !isset($data['id']); $isEmptyPreviewData = !$this->_getSession()->hasPreviewData() || empty($this->_getSession()->getPreviewData()); - + if ($isEmptyRequestData && $isEmptyPreviewData) { $this->_forward('noroute'); return $this; diff --git a/app/code/Magento/Newsletter/Controller/Manage/Save.php b/app/code/Magento/Newsletter/Controller/Manage/Save.php index d7d511e2d1906..01012e39a992a 100644 --- a/app/code/Magento/Newsletter/Controller/Manage/Save.php +++ b/app/code/Magento/Newsletter/Controller/Manage/Save.php @@ -12,6 +12,7 @@ use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\SubscriptionManagerInterface; /** * Customers newsletter subscription save controller @@ -34,9 +35,9 @@ class Save extends \Magento\Newsletter\Controller\Manage implements HttpPostActi protected $customerRepository; /** - * @var \Magento\Newsletter\Model\SubscriberFactory + * @var SubscriptionManagerInterface */ - protected $subscriberFactory; + private $subscriptionManager; /** * Initialize dependencies. @@ -46,7 +47,7 @@ class Save extends \Magento\Newsletter\Controller\Manage implements HttpPostActi * @param \Magento\Framework\Data\Form\FormKey\Validator $formKeyValidator * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param CustomerRepository $customerRepository - * @param \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory + * @param SubscriptionManagerInterface $subscriptionManager */ public function __construct( \Magento\Framework\App\Action\Context $context, @@ -54,13 +55,13 @@ public function __construct( \Magento\Framework\Data\Form\FormKey\Validator $formKeyValidator, \Magento\Store\Model\StoreManagerInterface $storeManager, CustomerRepository $customerRepository, - \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory + SubscriptionManagerInterface $subscriptionManager ) { $this->storeManager = $storeManager; $this->formKeyValidator = $formKeyValidator; $this->customerRepository = $customerRepository; - $this->subscriberFactory = $subscriberFactory; parent::__construct($context, $customerSession); + $this->subscriptionManager = $subscriptionManager; } /** @@ -80,28 +81,24 @@ public function execute() } else { try { $customer = $this->customerRepository->getById($customerId); - $storeId = $this->storeManager->getStore()->getId(); + $storeId = (int)$this->storeManager->getStore()->getId(); $customer->setStoreId($storeId); - $isSubscribedState = $customer->getExtensionAttributes() - ->getIsSubscribed(); - $isSubscribedParam = (boolean)$this->getRequest() - ->getParam('is_subscribed', false); + $isSubscribedState = $customer->getExtensionAttributes()->getIsSubscribed(); + $isSubscribedParam = (boolean)$this->getRequest()->getParam('is_subscribed', false); if ($isSubscribedParam !== $isSubscribedState) { // No need to validate customer and customer address while saving subscription preferences $this->setIgnoreValidationFlag($customer); $this->customerRepository->save($customer); if ($isSubscribedParam) { - $subscribeModel = $this->subscriberFactory->create() - ->subscribeCustomerById($customerId); - $subscribeStatus = $subscribeModel->getStatus(); - if ($subscribeStatus == Subscriber::STATUS_SUBSCRIBED) { + $subscribeModel = $this->subscriptionManager->subscribeCustomer((int)$customerId, $storeId); + $subscribeStatus = (int)$subscribeModel->getStatus(); + if ($subscribeStatus === Subscriber::STATUS_SUBSCRIBED) { $this->messageManager->addSuccess(__('We have saved your subscription.')); } else { $this->messageManager->addSuccess(__('A confirmation request has been sent.')); } } else { - $this->subscriberFactory->create() - ->unsubscribeCustomerById($customerId); + $this->subscriptionManager->unsubscribeCustomer((int)$customerId, $storeId); $this->messageManager->addSuccess(__('We have removed your newsletter subscription.')); } } else { diff --git a/app/code/Magento/Newsletter/Controller/Subscriber/NewAction.php b/app/code/Magento/Newsletter/Controller/Subscriber/NewAction.php index 7557f1610b4f4..ea52ae8aaa864 100644 --- a/app/code/Magento/Newsletter/Controller/Subscriber/NewAction.php +++ b/app/code/Magento/Newsletter/Controller/Subscriber/NewAction.php @@ -4,7 +4,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Newsletter\Controller\Subscriber; use Magento\Customer\Api\AccountManagementInterface as CustomerAccountManagement; @@ -14,11 +13,14 @@ use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Controller\Result\Redirect; +use Magento\Framework\Controller\ResultFactory; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Phrase; use Magento\Framework\Validator\EmailAddress as EmailValidator; use Magento\Newsletter\Controller\Subscriber as SubscriberController; use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\SubscriptionManagerInterface; use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\Newsletter\Model\SubscriberFactory; @@ -40,6 +42,11 @@ class NewAction extends SubscriberController implements HttpPostActionInterface */ private $emailValidator; + /** + * @var SubscriptionManagerInterface + */ + private $subscriptionManager; + /** * Initialize dependencies. * @@ -49,6 +56,7 @@ class NewAction extends SubscriberController implements HttpPostActionInterface * @param StoreManagerInterface $storeManager * @param CustomerUrl $customerUrl * @param CustomerAccountManagement $customerAccountManagement + * @param SubscriptionManagerInterface $subscriptionManager * @param EmailValidator $emailValidator */ public function __construct( @@ -58,9 +66,11 @@ public function __construct( StoreManagerInterface $storeManager, CustomerUrl $customerUrl, CustomerAccountManagement $customerAccountManagement, + SubscriptionManagerInterface $subscriptionManager, EmailValidator $emailValidator = null ) { $this->customerAccountManagement = $customerAccountManagement; + $this->subscriptionManager = $subscriptionManager; $this->emailValidator = $emailValidator ?: ObjectManager::getInstance()->get(EmailValidator::class); parent::__construct( $context, @@ -132,7 +142,7 @@ protected function validateEmailFormat($email) /** * New subscription action * - * @return \Magento\Framework\Controller\Result\Redirect + * @return Redirect */ public function execute() { @@ -144,29 +154,55 @@ public function execute() $this->validateGuestSubscription(); $this->validateEmailAvailable($email); - $subscriber = $this->_subscriberFactory->create()->loadByEmail($email); + $websiteId = (int)$this->_storeManager->getStore()->getWebsiteId(); + /** @var Subscriber $subscriber */ + $subscriber = $this->_subscriberFactory->create()->loadBySubscriberEmail($email, $websiteId); if ($subscriber->getId() - && (int) $subscriber->getSubscriberStatus() === Subscriber::STATUS_SUBSCRIBED - ) { + && (int)$subscriber->getSubscriberStatus() === Subscriber::STATUS_SUBSCRIBED) { throw new LocalizedException( __('This email address is already subscribed.') ); } - $status = (int) $this->_subscriberFactory->create()->subscribe($email); - $this->messageManager->addSuccessMessage($this->getSuccessMessage($status)); + $storeId = (int)$this->_storeManager->getStore()->getId(); + $currentCustomerId = $this->getSessionCustomerId($email); + $subscriber = $currentCustomerId + ? $this->subscriptionManager->subscribeCustomer($currentCustomerId, $storeId) + : $this->subscriptionManager->subscribe($email, $storeId); + $message = $this->getSuccessMessage((int)$subscriber->getSubscriberStatus()); + $this->messageManager->addSuccessMessage($message); } catch (LocalizedException $e) { $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { $this->messageManager->addExceptionMessage($e, __('Something went wrong with the subscription.')); } } - /** @var \Magento\Framework\Controller\Result\Redirect $redirect */ - $redirect = $this->resultFactory->create(\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT); + /** @var Redirect $redirect */ + $redirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); $redirectUrl = $this->_redirect->getRedirectUrl(); return $redirect->setUrl($redirectUrl); } + /** + * Get customer id from session if he is owner of the email + * + * @param string $email + * @return int|null + */ + private function getSessionCustomerId(string $email): ?int + { + if (!$this->_customerSession->isLoggedIn()) { + return null; + } + + $customer = $this->_customerSession->getCustomerDataObject(); + if ($customer->getEmail() !== $email) { + return null; + } + + return (int)$this->_customerSession->getId(); + } + /** * Get success message * diff --git a/app/code/Magento/Newsletter/Model/Observer.php b/app/code/Magento/Newsletter/Model/Observer.php index d77371569601f..a6f8ffd461d08 100644 --- a/app/code/Magento/Newsletter/Model/Observer.php +++ b/app/code/Magento/Newsletter/Model/Observer.php @@ -3,8 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Newsletter\Model; +use Magento\Newsletter\Model\ResourceModel\Queue\Collection; +use Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory; + /** * Newsletter module observer * @@ -12,20 +18,35 @@ */ class Observer { + /** + * Number of queue + */ + private const COUNT_OF_QUEUE = 3; + + /** + * Number of subscriptions + */ + private const COUNT_OF_SUBSCRIPTIONS = 20; + + /** + * First page in collection + */ + private const FIRST_PAGE = 1; + /** * Queue collection factory * - * @var \Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory + * @var CollectionFactory */ protected $_queueCollectionFactory; /** * Construct * - * @param \Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory $queueCollectionFactory + * @param CollectionFactory $queueCollectionFactory */ public function __construct( - \Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory $queueCollectionFactory + CollectionFactory $queueCollectionFactory ) { $this->_queueCollectionFactory = $queueCollectionFactory; } @@ -37,13 +58,11 @@ public function __construct( */ public function scheduledSend() { - $countOfQueue = 3; - $countOfSubscriptions = 20; - - /** @var \Magento\Newsletter\Model\ResourceModel\Queue\Collection $collection */ + /** @var Collection $collection */ $collection = $this->_queueCollectionFactory->create(); - $collection->setPageSize($countOfQueue)->setCurPage(1)->addOnlyForSendingFilter()->load(); + $collection->setPageSize(self::COUNT_OF_QUEUE) + ->setCurPage(self::FIRST_PAGE)->addOnlyForSendingFilter()->load(); - $collection->walk('sendPerSubscriber', [$countOfSubscriptions]); + $collection->walk('sendPerSubscriber', [self::COUNT_OF_SUBSCRIPTIONS]); } } diff --git a/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php b/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php index 309bfadab41b3..60b279b659ca6 100644 --- a/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php +++ b/app/code/Magento/Newsletter/Model/Plugin/CustomerPlugin.php @@ -5,55 +5,92 @@ */ namespace Magento\Newsletter\Model\Plugin; -use Magento\Customer\Api\CustomerRepositoryInterface as CustomerRepository; +use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\CustomerInterface; -use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Customer\Model\Config\Share; use Magento\Framework\Api\ExtensionAttributesFactory; -use Magento\Newsletter\Model\ResourceModel\Subscriber; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\ResourceModel\Subscriber\CollectionFactory; use Magento\Customer\Api\Data\CustomerExtensionInterface; +use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Newsletter\Model\SubscriptionManagerInterface; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Psr\Log\LoggerInterface; /** * Newsletter Plugin for customer + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CustomerPlugin { /** - * Factory used for manipulating newsletter subscriptions - * - * @var SubscriberFactory + * @var ExtensionAttributesFactory */ - private $subscriberFactory; + private $extensionFactory; /** - * @var ExtensionAttributesFactory + * @var CollectionFactory */ - private $extensionFactory; + private $collectionFactory; + + /** + * @var SubscriptionManagerInterface + */ + private $subscriptionManager; /** - * @var Subscriber + * @var Share */ - private $subscriberResource; + private $shareConfig; + + /** + * @var StoreManagerInterface + */ + private $storeManager; /** * @var array */ - private $customerSubscriptionStatus = []; + private $customerSubscriber = []; + + /** + * @var SubscriberFactory + */ + private $subscriberFactory; + + /** + * @var LoggerInterface + */ + private $logger; /** - * Initialize dependencies. - * * @param SubscriberFactory $subscriberFactory * @param ExtensionAttributesFactory $extensionFactory - * @param Subscriber $subscriberResource + * @param CollectionFactory $collectionFactory + * @param SubscriptionManagerInterface $subscriptionManager + * @param Share $shareConfig + * @param StoreManagerInterface $storeManager + * @param LoggerInterface $logger */ public function __construct( SubscriberFactory $subscriberFactory, ExtensionAttributesFactory $extensionFactory, - Subscriber $subscriberResource + CollectionFactory $collectionFactory, + SubscriptionManagerInterface $subscriptionManager, + Share $shareConfig, + StoreManagerInterface $storeManager, + LoggerInterface $logger ) { $this->subscriberFactory = $subscriberFactory; $this->extensionFactory = $extensionFactory; - $this->subscriberResource = $subscriberResource; + $this->collectionFactory = $collectionFactory; + $this->subscriptionManager = $subscriptionManager; + $this->shareConfig = $shareConfig; + $this->storeManager = $storeManager; + $this->logger = $logger; } /** @@ -61,128 +98,231 @@ public function __construct( * * If we have extension attribute (is_subscribed) we need to subscribe that customer * - * @param CustomerRepository $subject + * @param CustomerRepositoryInterface $subject * @param CustomerInterface $result * @param CustomerInterface $customer * @return CustomerInterface * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterSave(CustomerRepository $subject, CustomerInterface $result, CustomerInterface $customer) - { - $resultId = $result->getId(); - /** @var \Magento\Newsletter\Model\Subscriber $subscriber */ - $subscriber = $this->subscriberFactory->create(); + public function afterSave( + CustomerRepositoryInterface $subject, + CustomerInterface $result, + CustomerInterface $customer + ) { + $subscriber = $this->getSubscriber($result); + $subscribeStatus = $this->getIsSubscribedFromExtensionAttr($customer) ?? $subscriber->isSubscribed(); + $needToUpdate = $this->isSubscriptionChanged($result, $subscriber, $subscribeStatus); - $subscriber->updateSubscription($resultId); - // update the result only if the original customer instance had different value. - $initialExtensionAttributes = $result->getExtensionAttributes(); - if ($initialExtensionAttributes === null) { - /** @var CustomerExtensionInterface $initialExtensionAttributes */ - $initialExtensionAttributes = $this->extensionFactory->create(CustomerInterface::class); - $result->setExtensionAttributes($initialExtensionAttributes); + /** + * If subscriber is waiting to confirm customer registration + * and customer is already confirmed registration + * than need to subscribe customer + */ + if ((int)$subscriber->getStatus() === Subscriber::STATUS_UNCONFIRMED && empty($result->getConfirmation())) { + $needToUpdate = true; + $subscribeStatus = true; } + if ($needToUpdate) { + $storeId = $this->getCurrentStoreId($result); + $subscriber = $subscribeStatus + ? $this->subscriptionManager->subscribeCustomer((int)$result->getId(), $storeId) + : $this->subscriptionManager->unsubscribeCustomer((int)$result->getId(), $storeId); + $this->customerSubscriber[(int)$result->getId()] = $subscriber; + } + $this->addIsSubscribedExtensionAttr($result, $subscriber->isSubscribed()); + + return $result; + } + /** + * Get subscription status from extension customer attribute + * + * @param CustomerInterface $customer + * @return bool|null + */ + private function getIsSubscribedFromExtensionAttr(CustomerInterface $customer): ?bool + { $newExtensionAttributes = $customer->getExtensionAttributes(); - if ($newExtensionAttributes - && $initialExtensionAttributes->getIsSubscribed() !== $newExtensionAttributes->getIsSubscribed() - ) { - if ($newExtensionAttributes->getIsSubscribed()) { - $subscriber->subscribeCustomerById($resultId); - } else { - $subscriber->unsubscribeCustomerById($resultId); - } + if ($newExtensionAttributes === null || $newExtensionAttributes->getIsSubscribed() === null) { + return null; } - $isSubscribed = $subscriber->isSubscribed(); - $this->customerSubscriptionStatus[$resultId] = $isSubscribed; - $initialExtensionAttributes->setIsSubscribed($isSubscribed); + return (bool)$newExtensionAttributes->getIsSubscribed(); + } - return $result; + /** + * Get is customer subscription changed + * + * @param CustomerInterface $customer + * @param Subscriber $subscriber + * @param bool $newStatus + * @return bool + */ + private function isSubscriptionChanged(CustomerInterface $customer, Subscriber $subscriber, bool $newStatus): bool + { + if ($subscriber->isSubscribed() !== $newStatus) { + return true; + } + + if (!$subscriber->getId()) { + return false; + } + + /** + * If customer has changed email or subscriber was loaded by email + * than need to update customer subscription + */ + return $customer->getEmail() !== $subscriber->getEmail() || (int)$subscriber->getCustomerId() === 0; } /** * Plugin around delete customer that updates any newsletter subscription that may have existed. * - * @param CustomerRepository $subject + * @param CustomerRepositoryInterface $subject * @param callable $deleteCustomerById Function we are wrapping around * @param int $customerId Input to the function * @return bool */ public function aroundDeleteById( - CustomerRepository $subject, + CustomerRepositoryInterface $subject, callable $deleteCustomerById, $customerId ) { $customer = $subject->getById($customerId); $result = $deleteCustomerById($customerId); - /** @var \Magento\Newsletter\Model\Subscriber $subscriber */ - $subscriber = $this->subscriberFactory->create(); - $subscriber->loadByEmail($customer->getEmail()); - if ($subscriber->getId()) { - $subscriber->delete(); - } + $this->deleteSubscriptionsAfterCustomerDelete($customer); + return $result; } /** * Plugin after delete customer that updates any newsletter subscription that may have existed. * - * @param CustomerRepository $subject + * @param CustomerRepositoryInterface $subject * @param bool $result * @param CustomerInterface $customer * @return bool * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterDelete(CustomerRepository $subject, $result, CustomerInterface $customer) + public function afterDelete(CustomerRepositoryInterface $subject, $result, CustomerInterface $customer) { - $subscriber = $this->subscriberFactory->create(); - $subscriber->loadByEmail($customer->getEmail()); - if ($subscriber->getId()) { - $subscriber->delete(); - } + $this->deleteSubscriptionsAfterCustomerDelete($customer); return $result; } /** * Plugin after getById customer that obtains newsletter subscription status for given customer. * - * @param CustomerRepository $subject + * @param CustomerRepositoryInterface $subject * @param CustomerInterface $customer * @return CustomerInterface * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterGetById(CustomerRepository $subject, CustomerInterface $customer) + public function afterGetById(CustomerRepositoryInterface $subject, CustomerInterface $customer) { $extensionAttributes = $customer->getExtensionAttributes(); + if ($extensionAttributes === null || $extensionAttributes->getIsSubscribed() === null) { + $isSubscribed = $this->getSubscriber($customer)->isSubscribed(); + $this->addIsSubscribedExtensionAttr($customer, $isSubscribed); + } + + return $customer; + } + /** + * Set Is Subscribed extension attribute + * + * @param CustomerInterface $customer + * @param bool $isSubscribed + */ + private function addIsSubscribedExtensionAttr(CustomerInterface $customer, bool $isSubscribed): void + { + $extensionAttributes = $customer->getExtensionAttributes(); if ($extensionAttributes === null) { /** @var CustomerExtensionInterface $extensionAttributes */ $extensionAttributes = $this->extensionFactory->create(CustomerInterface::class); $customer->setExtensionAttributes($extensionAttributes); } - if ($extensionAttributes->getIsSubscribed() === null) { - $isSubscribed = $this->isSubscribed($customer); - $extensionAttributes->setIsSubscribed($isSubscribed); + $extensionAttributes->setIsSubscribed($isSubscribed); + } + + /** + * Delete customer subscriptions + * + * @param CustomerInterface $customer + * @return void + */ + private function deleteSubscriptionsAfterCustomerDelete(CustomerInterface $customer): void + { + $collection = $this->collectionFactory->create(); + $collection->addFieldToFilter('subscriber_email', $customer->getEmail()); + if ($this->shareConfig->isWebsiteScope()) { + try { + $storeIds = $this->storeManager->getWebsite($customer->getWebsiteId())->getStoreIds(); + $collection->addFieldToFilter('store_id', ['in' => $storeIds]); + } catch (NoSuchEntityException $exception) { + $this->logger->error($exception); + } + } + /** @var Subscriber $subscriber */ + foreach ($collection as $subscriber) { + $subscriber->delete(); } + } - return $customer; + /** + * Get Subscriber model by customer + * + * @param CustomerInterface $customer + * @return Subscriber + */ + private function getSubscriber(CustomerInterface $customer): Subscriber + { + $customerId = (int)$customer->getId(); + if (isset($this->customerSubscriber[$customerId])) { + return $this->customerSubscriber[$customerId]; + } + + /** @var Subscriber $subscriber */ + $subscriber = $this->subscriberFactory->create(); + $websiteId = $this->getCurrentWebsiteId($customer); + $subscriber->loadByCustomer((int)$customer->getId(), $websiteId); + /** + * If subscriber was't found by customer id then try to find subscriber by customer email. + * It need when the customer is creating and he has already subscribed as guest by same email. + */ + if (!$subscriber->getId()) { + $subscriber->loadBySubscriberEmail((string)$customer->getEmail(), $websiteId); + } + $this->customerSubscriber[$customerId] = $subscriber; + + return $subscriber; } /** - * This method returns newsletters subscription status for given customer. + * Retrieve current website id * * @param CustomerInterface $customer - * @return bool + * @return int + */ + private function getCurrentWebsiteId(CustomerInterface $customer): int + { + return (int)$this->storeManager->getStore($this->getCurrentStoreId($customer))->getWebsiteId(); + } + + /** + * Retrieve current store id + * + * @param CustomerInterface $customer + * @return int */ - private function isSubscribed(CustomerInterface $customer) + private function getCurrentStoreId(CustomerInterface $customer): int { - $customerId = $customer->getId(); - if (!isset($this->customerSubscriptionStatus[$customerId])) { - $subscriber = $this->subscriberResource->loadByCustomerData($customer); - $this->customerSubscriptionStatus[$customerId] = isset($subscriber['subscriber_status']) - && $subscriber['subscriber_status'] == 1; + $storeId = (int)$this->storeManager->getStore()->getId(); + if ($storeId === Store::DEFAULT_STORE_ID) { + $storeId = (int)$customer->getStoreId(); } - return $this->customerSubscriptionStatus[$customerId]; + return $storeId; } } diff --git a/app/code/Magento/Newsletter/Model/ResourceModel/Queue/Collection.php b/app/code/Magento/Newsletter/Model/ResourceModel/Queue/Collection.php index 52009dad6614b..33c539fbba84f 100644 --- a/app/code/Magento/Newsletter/Model/ResourceModel/Queue/Collection.php +++ b/app/code/Magento/Newsletter/Model/ResourceModel/Queue/Collection.php @@ -208,6 +208,31 @@ public function addSubscriberFilter($subscriberId) return $this; } + /** + * Set filter for queue by customer + * + * @param int $customerId + * @return $this + */ + public function addCustomerFilter(int $customerId): Collection + { + $this->getSelect() + ->join( + ['link' => $this->getTable('newsletter_queue_link')], + 'main_table.queue_id=link.queue_id', + ['letter_sent_at'] + )->join( + ['subscriber' => $this->getTable('newsletter_subscriber')], + 'link.subscriber_id=subscriber.subscriber_id', + ['subscriber_store_id' => 'subscriber.store_id'] + )->where( + 'subscriber.customer_id = ?', + $customerId + ); + + return $this; + } + /** * Add filter by only ready for sending item * diff --git a/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php b/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php index 8ca489d89c1df..6391219e23c7e 100644 --- a/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php +++ b/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php @@ -5,23 +5,30 @@ */ namespace Magento\Newsletter\Model\ResourceModel; +use Magento\Customer\Api\Data\CustomerInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Math\Random; +use Magento\Framework\Model\ResourceModel\Db\AbstractDb; +use Magento\Framework\Model\ResourceModel\Db\Context; +use Magento\Framework\Stdlib\DateTime\DateTime; +use Magento\Newsletter\Model\Subscriber as SubscriberModel; use Magento\Store\Model\StoreManagerInterface; /** * Newsletter subscriber resource model * - * @author Magento Core Team <core@magentocommerce.com> - * + * @author Magento Core Team <core@magentocommerce.com> * @api * @since 100.0.2 */ -class Subscriber extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb +class Subscriber extends AbstractDb { /** * DB connection * - * @var \Magento\Framework\DB\Adapter\AdapterInterface + * @var AdapterInterface */ protected $connection; @@ -42,12 +49,12 @@ class Subscriber extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb /** * Date * - * @var \Magento\Framework\Stdlib\DateTime\DateTime + * @var DateTime */ protected $_date; /** - * @var \Magento\Framework\Math\Random + * @var Random */ protected $mathRandom; @@ -61,16 +68,16 @@ class Subscriber extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb /** * Construct * - * @param \Magento\Framework\Model\ResourceModel\Db\Context $context - * @param \Magento\Framework\Stdlib\DateTime\DateTime $date - * @param \Magento\Framework\Math\Random $mathRandom + * @param Context $context + * @param DateTime $date + * @param Random $mathRandom * @param string $connectionName * @param StoreManagerInterface $storeManager */ public function __construct( - \Magento\Framework\Model\ResourceModel\Db\Context $context, - \Magento\Framework\Stdlib\DateTime\DateTime $date, - \Magento\Framework\Math\Random $mathRandom, + Context $context, + DateTime $date, + Random $mathRandom, $connectionName = null, StoreManagerInterface $storeManager = null ) { @@ -104,65 +111,51 @@ public function setMessagesScope($scope) } /** - * Load subscriber from DB by email + * Load by subscriber email * - * @param string $subscriberEmail + * @param string $email + * @param int $websiteId * @return array */ - public function loadByEmail($subscriberEmail) + public function loadBySubscriberEmail(string $email, int $websiteId): array { - $select = $this->connection->select()->from($this->getMainTable())->where('subscriber_email=:subscriber_email'); - - $result = $this->connection->fetchRow($select, ['subscriber_email' => $subscriberEmail]); - - if (!$result) { + $storeIds = $this->storeManager->getWebsite($websiteId)->getStoreIds(); + $select = $this->connection->select() + ->from($this->getMainTable()) + ->where('subscriber_email = ?', $email) + ->where('store_id IN (?)', $storeIds) + ->limit(1); + + $data = $this->connection->fetchRow($select); + if (!$data) { return []; } - return $result; + return $data; } /** - * Load subscriber by customer + * Load by customer id * - * @param \Magento\Customer\Api\Data\CustomerInterface $customer + * @param int $customerId + * @param int $websiteId * @return array */ - public function loadByCustomerData(\Magento\Customer\Api\Data\CustomerInterface $customer) + public function loadByCustomerId(int $customerId, int $websiteId): array { - $storeIds = $this->storeManager->getWebsite()->getStoreIds(); - - if ($customer->getId()) { - $select = $this->connection - ->select() - ->from($this->getMainTable()) - ->where('customer_id = ?', $customer->getId()) - ->where('store_id IN (?)', $storeIds) - ->limit(1); - - $result = $this->connection->fetchRow($select); - - if ($result) { - return $result; - } - } - - if ($customer->getEmail()) { - $select = $this->connection - ->select() - ->from($this->getMainTable()) - ->where('subscriber_email = ?', $customer->getEmail()) - ->where('store_id IN (?)', $storeIds) - ->limit(1); - - $result = $this->connection->fetchRow($select); - - if ($result) { - return $result; - } + $storeIds = $this->storeManager->getWebsite($websiteId)->getStoreIds(); + $select = $this->connection->select() + ->from($this->getMainTable()) + ->where('customer_id = ?', $customerId) + ->where('store_id IN (?)', $storeIds) + ->limit(1); + + $data = $this->connection->fetchRow($select); + if (!$data) { + return []; } - return []; + return $data; } /** @@ -178,12 +171,12 @@ protected function _generateRandomCode() /** * Updates data when subscriber received * - * @param \Magento\Newsletter\Model\Subscriber $subscriber + * @param SubscriberModel $subscriber * @param \Magento\Newsletter\Model\Queue $queue * @return $this - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException */ - public function received(\Magento\Newsletter\Model\Subscriber $subscriber, \Magento\Newsletter\Model\Queue $queue) + public function received(SubscriberModel $subscriber, \Magento\Newsletter\Model\Queue $queue) { $this->connection->beginTransaction(); try { @@ -196,8 +189,41 @@ public function received(\Magento\Newsletter\Model\Subscriber $subscriber, \Mage $this->connection->commit(); } catch (\Exception $e) { $this->connection->rollBack(); - throw new \Magento\Framework\Exception\LocalizedException(__('We cannot mark as received subscriber.')); + throw new LocalizedException(__('We cannot mark as received subscriber.')); } return $this; } + + /** + * Load subscriber from DB by email + * + * @param string $subscriberEmail + * @return array + * @deprecated The subscription should be loaded by website id + * @see loadBySubscriberEmail + */ + public function loadByEmail($subscriberEmail) + { + $websiteId = (int)$this->storeManager->getWebsite()->getId(); + return $this->loadBySubscriberEmail((string)$subscriberEmail, $websiteId); + } + + /** + * Load subscriber by customer + * + * @param CustomerInterface $customer + * @return array + * @deprecated The subscription should be loaded by website id + * @see loadByCustomerId + */ + public function loadByCustomerData(CustomerInterface $customer) + { + $websiteId = (int)$this->storeManager->getWebsite()->getId(); + $data = $this->loadByCustomerId((int)$customer->getId(), $websiteId); + if (empty($data)) { + $data = $this->loadBySubscriberEmail((string)$customer->getEmail(), $websiteId); + } + + return $data; + } } diff --git a/app/code/Magento/Newsletter/Model/Subscriber.php b/app/code/Magento/Newsletter/Model/Subscriber.php index c5eee5e3cf771..5c573f47aa0bf 100644 --- a/app/code/Magento/Newsletter/Model/Subscriber.php +++ b/app/code/Magento/Newsletter/Model/Subscriber.php @@ -9,9 +9,23 @@ use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\CustomerInterfaceFactory; use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\App\Area; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ObjectManager; -use Magento\Framework\Exception\MailException; +use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Mail\Template\TransportBuilder; +use Magento\Framework\Math\Random; +use Magento\Framework\Model\AbstractModel; +use Magento\Framework\Model\Context; +use Magento\Framework\Model\ResourceModel\AbstractResource; +use Magento\Framework\Registry; +use Magento\Framework\Stdlib\DateTime\DateTime; +use Magento\Framework\Translate\Inline\StateInterface; +use Magento\Newsletter\Helper\Data; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; /** * Subscriber model @@ -33,12 +47,11 @@ * * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - * @SuppressWarnings(PHPMD.CyclomaticComplexity) * * @api * @since 100.0.2 */ -class Subscriber extends \Magento\Framework\Model\AbstractModel +class Subscriber extends AbstractModel { const STATUS_SUBSCRIBED = 1; const STATUS_NOT_ACTIVE = 2; @@ -80,14 +93,14 @@ class Subscriber extends \Magento\Framework\Model\AbstractModel /** * Newsletter data * - * @var \Magento\Newsletter\Helper\Data + * @var Data */ protected $_newsletterData = null; /** * Core store config * - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface */ protected $_scopeConfig; @@ -100,14 +113,14 @@ class Subscriber extends \Magento\Framework\Model\AbstractModel /** * Date - * @var \Magento\Framework\Stdlib\DateTime\DateTime + * @var DateTime */ private $dateTime; /** * Store manager * - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ protected $_storeManager; @@ -122,12 +135,12 @@ class Subscriber extends \Magento\Framework\Model\AbstractModel protected $customerAccountManagement; /** - * @var \Magento\Framework\Mail\Template\TransportBuilder + * @var TransportBuilder */ protected $_transportBuilder; /** - * @var \Magento\Framework\Translate\Inline\StateInterface + * @var StateInterface */ protected $inlineTranslation; @@ -142,51 +155,56 @@ class Subscriber extends \Magento\Framework\Model\AbstractModel private $dataObjectHelper; /** - * Initialize dependencies. - * - * @param \Magento\Framework\Model\Context $context - * @param \Magento\Framework\Registry $registry - * @param \Magento\Newsletter\Helper\Data $newsletterData - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder - * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @var SubscriptionManagerInterface + */ + private $subscriptionManager; + + /** + * @param Context $context + * @param Registry $registry + * @param Data $newsletterData + * @param ScopeConfigInterface $scopeConfig + * @param TransportBuilder $transportBuilder + * @param StoreManagerInterface $storeManager * @param \Magento\Customer\Model\Session $customerSession * @param CustomerRepositoryInterface $customerRepository * @param AccountManagementInterface $customerAccountManagement - * @param \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation - * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource - * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection + * @param StateInterface $inlineTranslation + * @param AbstractResource|null $resource + * @param AbstractDb|null $resourceCollection * @param array $data - * @param \Magento\Framework\Stdlib\DateTime\DateTime|null $dateTime + * @param DateTime|null $dateTime * @param CustomerInterfaceFactory|null $customerFactory * @param DataObjectHelper|null $dataObjectHelper + * @param SubscriptionManagerInterface|null $subscriptionManager * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Framework\Model\Context $context, - \Magento\Framework\Registry $registry, - \Magento\Newsletter\Helper\Data $newsletterData, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder, - \Magento\Store\Model\StoreManagerInterface $storeManager, + Context $context, + Registry $registry, + Data $newsletterData, + ScopeConfigInterface $scopeConfig, + TransportBuilder $transportBuilder, + StoreManagerInterface $storeManager, \Magento\Customer\Model\Session $customerSession, CustomerRepositoryInterface $customerRepository, AccountManagementInterface $customerAccountManagement, - \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation, - \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, - \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, + StateInterface $inlineTranslation, + AbstractResource $resource = null, + AbstractDb $resourceCollection = null, array $data = [], - \Magento\Framework\Stdlib\DateTime\DateTime $dateTime = null, + DateTime $dateTime = null, CustomerInterfaceFactory $customerFactory = null, - DataObjectHelper $dataObjectHelper = null + DataObjectHelper $dataObjectHelper = null, + SubscriptionManagerInterface $subscriptionManager = null ) { $this->_newsletterData = $newsletterData; $this->_scopeConfig = $scopeConfig; $this->_transportBuilder = $transportBuilder; $this->_storeManager = $storeManager; $this->_customerSession = $customerSession; - $this->dateTime = $dateTime ?: \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\Framework\Stdlib\DateTime\DateTime::class + $this->dateTime = $dateTime ?: ObjectManager::getInstance()->get( + DateTime::class ); $this->customerFactory = $customerFactory ?: ObjectManager::getInstance() ->get(CustomerInterfaceFactory::class); @@ -195,6 +213,8 @@ public function __construct( $this->customerRepository = $customerRepository; $this->customerAccountManagement = $customerAccountManagement; $this->inlineTranslation = $inlineTranslation; + $this->subscriptionManager = $subscriptionManager ?: ObjectManager::getInstance() + ->get(SubscriptionManagerInterface::class); parent::__construct($context, $registry, $resource, $resourceCollection, $data); } @@ -205,7 +225,7 @@ public function __construct( */ protected function _construct() { - $this->_init(\Magento\Newsletter\Model\ResourceModel\Subscriber::class); + $this->_init(ResourceModel\Subscriber::class); } /** @@ -357,51 +377,38 @@ public function isSubscribed() } /** - * Load subscriber data from resource model by email + * Load by subscriber email * - * @param string $subscriberEmail + * @param string $email + * @param int $websiteId * @return $this */ - public function loadByEmail($subscriberEmail) + public function loadBySubscriberEmail(string $email, int $websiteId): Subscriber { - $storeId = $this->_storeManager->getStore()->getId(); - $customerData = ['store_id' => $storeId, 'email'=> $subscriberEmail]; + /** @var ResourceModel\Subscriber $resource */ + $resource = $this->getResource(); + $data = $resource->loadBySubscriberEmail($email, $websiteId); + $this->addData($data); + $this->setOrigData(); - /** @var \Magento\Customer\Api\Data\CustomerInterface $customer */ - $customer = $this->customerFactory->create(); - $this->dataObjectHelper->populateWithArray( - $customer, - $customerData, - \Magento\Customer\Api\Data\CustomerInterface::class - ); - $this->addData($this->getResource()->loadByCustomerData($customer)); return $this; } /** - * Load subscriber info by customerId + * Load by customer id * * @param int $customerId + * @param int $websiteId * @return $this */ - public function loadByCustomerId($customerId) + public function loadByCustomer(int $customerId, int $websiteId): Subscriber { - try { - $customerData = $this->customerRepository->getById($customerId); - $customerData->setStoreId($this->_storeManager->getStore()->getId()); - if ($customerData->getWebsiteId() === null) { - $customerData->setWebsiteId($this->_storeManager->getStore()->getWebsiteId()); - } - $data = $this->getResource()->loadByCustomerData($customerData); - $this->addData($data); - if (!empty($data) && $customerData->getId() && !$this->getCustomerId()) { - $this->setCustomerId($customerData->getId()); - $this->setSubscriberConfirmCode($this->randomSequence()); - $this->save(); - } - // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock - } catch (NoSuchEntityException $e) { - } + /** @var ResourceModel\Subscriber $resource */ + $resource = $this->getResource(); + $data = $resource->loadByCustomerId($customerId, $websiteId); + $this->addData($data); + $this->setOrigData(); + return $this; } @@ -418,95 +425,23 @@ public function randomSequence($length = 32) $char = array_merge(range('a', 'z'), range(0, 9)); $charLen = count($char) - 1; for ($i = 0; $i < $length; $i++) { - $disc = \Magento\Framework\Math\Random::getRandomNumber(0, $charLen); + $disc = Random::getRandomNumber(0, $charLen); $par[$i] = $char[$disc]; $id = $id . $char[$disc]; } return $id; } - /** - * Subscribes by email - * - * @param string $email - * @throws \Exception - * @return int - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - public function subscribe($email) - { - $this->loadByEmail($email); - - if ($this->getId() && $this->getStatus() == self::STATUS_SUBSCRIBED) { - return $this->getStatus(); - } - - if (!$this->getId()) { - $this->setSubscriberConfirmCode($this->randomSequence()); - } - - $isConfirmNeed = $this->_scopeConfig->getValue( - self::XML_PATH_CONFIRMATION_FLAG, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) == 1 ? true : false; - - $isSubscribeOwnEmail = $this->_customerSession->isLoggedIn() - && $this->_customerSession->getCustomerDataObject()->getEmail() == $email; - - if (!$this->getId() || $this->getStatus() == self::STATUS_UNSUBSCRIBED - || $this->getStatus() == self::STATUS_NOT_ACTIVE - ) { - if ($isConfirmNeed === true) { - $this->setStatus(self::STATUS_NOT_ACTIVE); - } else { - $this->setStatus(self::STATUS_SUBSCRIBED); - } - $this->setSubscriberEmail($email); - } - - if ($isSubscribeOwnEmail) { - try { - $customer = $this->customerRepository->getById($this->_customerSession->getCustomerId()); - $this->setStoreId($customer->getStoreId()); - $this->setCustomerId($customer->getId()); - } catch (NoSuchEntityException $e) { - $this->setStoreId($this->_storeManager->getStore()->getId()); - $this->setCustomerId(0); - } - } else { - $this->setStoreId($this->_storeManager->getStore()->getId()); - $this->setCustomerId(0); - } - - $this->setStatusChanged(true); - - try { - /* Save model before sending out email */ - $this->save(); - if ($isConfirmNeed === true) { - $this->sendConfirmationRequestEmail(); - } else { - $this->sendConfirmationSuccessEmail(); - } - return $this->getStatus(); - } catch (\Exception $e) { - // phpcs:ignore Magento2.Exceptions.DirectThrow - throw new \Exception($e->getMessage()); - } - } - /** * Unsubscribes loaded subscription * - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException * @return $this */ public function unsubscribe() { if ($this->hasCheckCode() && $this->getCode() != $this->getCheckCode()) { - throw new \Magento\Framework\Exception\LocalizedException( + throw new LocalizedException( __('This is an invalid subscription confirmation code.') ); } @@ -518,149 +453,6 @@ public function unsubscribe() return $this; } - /** - * Subscribe the customer with the id provided - * - * @param int $customerId - * @return $this - */ - public function subscribeCustomerById($customerId) - { - return $this->_updateCustomerSubscription($customerId, true); - } - - /** - * Unsubscribe the customer with the id provided - * - * @param int $customerId - * @return $this - */ - public function unsubscribeCustomerById($customerId) - { - return $this->_updateCustomerSubscription($customerId, false); - } - - /** - * Update the subscription based on latest information of associated customer. - * - * @param int $customerId - * @return $this - */ - public function updateSubscription($customerId) - { - $this->loadByCustomerId($customerId); - $this->_updateCustomerSubscription($customerId, $this->isSubscribed()); - return $this; - } - - /** - * Saving customer subscription status - * - * @param int $customerId - * @param bool $subscribe indicates whether the customer should be subscribed or unsubscribed - * @return $this - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - protected function _updateCustomerSubscription($customerId, $subscribe) - { - try { - $customerData = $this->customerRepository->getById($customerId); - } catch (NoSuchEntityException $e) { - return $this; - } - - $this->loadByCustomerId($customerId); - if (!$subscribe && !$this->getId()) { - return $this; - } - - if (!$this->getId()) { - $this->setSubscriberConfirmCode($this->randomSequence()); - } - - $sendInformationEmail = false; - $status = self::STATUS_SUBSCRIBED; - $isConfirmNeed = $this->_scopeConfig->getValue( - self::XML_PATH_CONFIRMATION_FLAG, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) == 1 ? true : false; - if ($subscribe) { - if (AccountManagementInterface::ACCOUNT_CONFIRMATION_REQUIRED - == $this->customerAccountManagement->getConfirmationStatus($customerId) - ) { - if ($this->getId() && $this->getStatus() == self::STATUS_SUBSCRIBED) { - // if a customer was already subscribed then keep the subscribed - $status = self::STATUS_SUBSCRIBED; - } else { - $status = self::STATUS_UNCONFIRMED; - } - } elseif ($isConfirmNeed) { - if ($this->getStatus() != self::STATUS_SUBSCRIBED) { - $status = self::STATUS_NOT_ACTIVE; - } - } - } elseif (($this->getStatus() == self::STATUS_UNCONFIRMED) && ($customerData->getConfirmation() === null)) { - $status = self::STATUS_SUBSCRIBED; - $sendInformationEmail = true; - } elseif (($this->getStatus() == self::STATUS_NOT_ACTIVE) && ($customerData->getConfirmation() === null)) { - $status = self::STATUS_NOT_ACTIVE; - } else { - $status = self::STATUS_UNSUBSCRIBED; - } - /** - * If subscription status has been changed then send email to the customer - */ - if ($status != self::STATUS_UNCONFIRMED && $status != $this->getStatus()) { - $sendInformationEmail = true; - } - - if ($status != $this->getStatus()) { - $this->setStatusChanged(true); - } - - $this->setStatus($status); - - $storeId = $customerData->getStoreId(); - if ((int)$customerData->getStoreId() === 0) { - $storeId = $this->_storeManager->getWebsite($customerData->getWebsiteId())->getDefaultStore()->getId(); - } - - if (!$this->getId()) { - $this->setStoreId($storeId) - ->setCustomerId($customerData->getId()) - ->setEmail($customerData->getEmail()); - } else { - $this->setStoreId($storeId) - ->setEmail($customerData->getEmail()); - } - - $this->save(); - $sendSubscription = $sendInformationEmail; - if ($sendSubscription === null xor $sendSubscription && $this->isStatusChanged()) { - try { - switch ($status) { - case self::STATUS_UNSUBSCRIBED: - $this->sendUnsubscriptionEmail(); - break; - case self::STATUS_SUBSCRIBED: - $this->sendConfirmationSuccessEmail(); - break; - case self::STATUS_NOT_ACTIVE: - if ($isConfirmNeed) { - $this->sendConfirmationRequestEmail(); - } - break; - } - } catch (MailException $e) { - // If we are not able to send a new account email, this should be ignored - $this->_logger->critical($e); - } - } - return $this; - } - /** * Confirms subscriber newsletter * @@ -684,10 +476,10 @@ public function confirm($code) /** * Mark receiving subscriber of queue newsletter * - * @param \Magento\Newsletter\Model\Queue $queue + * @param Queue $queue * @return Subscriber */ - public function received(\Magento\Newsletter\Model\Queue $queue) + public function received(Queue $queue) { $this->getResource()->received($this, $queue); return $this; @@ -700,48 +492,13 @@ public function received(\Magento\Newsletter\Model\Queue $queue) */ public function sendConfirmationRequestEmail() { - if ($this->getImportMode()) { - return $this; - } - - if (!$this->_scopeConfig->getValue( - self::XML_PATH_CONFIRM_EMAIL_TEMPLATE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) || !$this->_scopeConfig->getValue( - self::XML_PATH_CONFIRM_EMAIL_IDENTITY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) - ) { - return $this; - } - - $this->inlineTranslation->suspend(); - - $this->_transportBuilder->setTemplateIdentifier( - $this->_scopeConfig->getValue( - self::XML_PATH_CONFIRM_EMAIL_TEMPLATE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) - )->setTemplateOptions( - [ - 'area' => \Magento\Framework\App\Area::AREA_FRONTEND, - 'store' => $this->_storeManager->getStore()->getId(), - ] - )->setTemplateVars( - ['subscriber' => $this, 'store' => $this->_storeManager->getStore()] - )->setFrom( - $this->_scopeConfig->getValue( - self::XML_PATH_CONFIRM_EMAIL_IDENTITY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) - )->addTo( - $this->getEmail(), - $this->getName() - ); - $transport = $this->_transportBuilder->getTransport(); - $transport->sendMessage(); - - $this->inlineTranslation->resume(); + $vars = [ + 'store' => $this->_storeManager->getStore($this->getStoreId()), + 'subscriber_data' => [ + 'confirmation_link' => $this->getConfirmationLink(), + ], + ]; + $this->sendEmail(self::XML_PATH_CONFIRM_EMAIL_TEMPLATE, self::XML_PATH_CONFIRM_EMAIL_IDENTITY, $vars); return $this; } @@ -753,48 +510,7 @@ public function sendConfirmationRequestEmail() */ public function sendConfirmationSuccessEmail() { - if ($this->getImportMode()) { - return $this; - } - - if (!$this->_scopeConfig->getValue( - self::XML_PATH_SUCCESS_EMAIL_TEMPLATE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) || !$this->_scopeConfig->getValue( - self::XML_PATH_SUCCESS_EMAIL_IDENTITY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) - ) { - return $this; - } - - $this->inlineTranslation->suspend(); - - $this->_transportBuilder->setTemplateIdentifier( - $this->_scopeConfig->getValue( - self::XML_PATH_SUCCESS_EMAIL_TEMPLATE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) - )->setTemplateOptions( - [ - 'area' => \Magento\Framework\App\Area::AREA_FRONTEND, - 'store' => $this->_storeManager->getStore()->getId(), - ] - )->setTemplateVars( - ['subscriber' => $this] - )->setFrom( - $this->_scopeConfig->getValue( - self::XML_PATH_SUCCESS_EMAIL_IDENTITY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) - )->addTo( - $this->getEmail(), - $this->getName() - ); - $transport = $this->_transportBuilder->getTransport(); - $transport->sendMessage(); - - $this->inlineTranslation->resume(); + $this->sendEmail(self::XML_PATH_SUCCESS_EMAIL_TEMPLATE, self::XML_PATH_SUCCESS_EMAIL_IDENTITY); return $this; } @@ -805,40 +521,45 @@ public function sendConfirmationSuccessEmail() * @return $this */ public function sendUnsubscriptionEmail() + { + $this->sendEmail(self::XML_PATH_UNSUBSCRIBE_EMAIL_TEMPLATE, self::XML_PATH_UNSUBSCRIBE_EMAIL_IDENTITY); + + return $this; + } + + /** + * Send email about change status + * + * @param string $emailTemplatePath + * @param string $emailIdentityPath + * @param array $templateVars + * @return void + */ + private function sendEmail(string $emailTemplatePath, string $emailIdentityPath, array $templateVars = []): void { if ($this->getImportMode()) { - return $this; + return; } - if (!$this->_scopeConfig->getValue( - self::XML_PATH_UNSUBSCRIBE_EMAIL_TEMPLATE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) || !$this->_scopeConfig->getValue( - self::XML_PATH_UNSUBSCRIBE_EMAIL_IDENTITY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) - ) { - return $this; + + $template = $this->_scopeConfig->getValue($emailTemplatePath, ScopeInterface::SCOPE_STORE, $this->getStoreId()); + $identity = $this->_scopeConfig->getValue($emailIdentityPath, ScopeInterface::SCOPE_STORE, $this->getStoreId()); + if (!$template || !$identity) { + return; } + $templateVars += ['subscriber' => $this]; $this->inlineTranslation->suspend(); - $this->_transportBuilder->setTemplateIdentifier( - $this->_scopeConfig->getValue( - self::XML_PATH_UNSUBSCRIBE_EMAIL_TEMPLATE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) + $template )->setTemplateOptions( [ - 'area' => \Magento\Framework\App\Area::AREA_FRONTEND, - 'store' => $this->_storeManager->getStore()->getId(), + 'area' => Area::AREA_FRONTEND, + 'store' => $this->getStoreId(), ] )->setTemplateVars( - ['subscriber' => $this] + $templateVars )->setFrom( - $this->_scopeConfig->getValue( - self::XML_PATH_UNSUBSCRIBE_EMAIL_IDENTITY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) + $identity )->addTo( $this->getEmail(), $this->getName() @@ -847,8 +568,6 @@ public function sendUnsubscriptionEmail() $transport->sendMessage(); $this->inlineTranslation->resume(); - - return $this; } /** @@ -878,4 +597,125 @@ public function beforeSave() } return $this; } + + /** + * Load subscriber data from resource model by email + * + * @param string $subscriberEmail + * @return $this + * @deprecated The subscription should be loaded by website id + * @see loadBySubscriberEmail + */ + public function loadByEmail($subscriberEmail) + { + $websiteId = (int)$this->_storeManager->getStore()->getWebsiteId(); + $this->loadBySubscriberEmail($subscriberEmail, $websiteId); + + return $this; + } + + /** + * Load subscriber info by customerId + * + * @param int $customerId + * @return $this + * @deprecated The subscription should be loaded by website id + * @see loadByCustomer + */ + public function loadByCustomerId($customerId) + { + try { + $customer = $this->customerRepository->getById($customerId); + $websiteId = (int)$this->_storeManager->getStore()->getWebsiteId(); + $this->loadByCustomer((int)$customerId, $websiteId); + if ($this->getId() && $customer->getId() && !$this->getCustomerId()) { + $this->setCustomerId($customer->getId()); + $this->setSubscriberConfirmCode($this->randomSequence()); + $this->save(); + } + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock + } catch (NoSuchEntityException $e) { + } + return $this; + } + + /** + * Subscribes by email + * + * @param string $email + * @return int + * @deprecated The subscription should be updated by store id + * @see \Magento\Newsletter\Model\SubscriptionManager::subscribe + */ + public function subscribe($email) + { + $storeId = (int)$this->_storeManager->getStore()->getId(); + $subscriber = $this->subscriptionManager->subscribe($email, $storeId); + $this->addData($subscriber->getData()); + + return $this->getStatus(); + } + + /** + * Subscribe the customer with the id provided + * + * @param int $customerId + * @return $this + * @deprecated The subscription should be updated by store id + * @see \Magento\Newsletter\Model\SubscriptionManager::subscribeCustomer + */ + public function subscribeCustomerById($customerId) + { + return $this->_updateCustomerSubscription($customerId, true); + } + + /** + * Unsubscribe the customer with the id provided + * + * @param int $customerId + * @return $this + * @deprecated The subscription should be updated by store id + * @see \Magento\Newsletter\Model\SubscriptionManager::unsubscribeCustomer + */ + public function unsubscribeCustomerById($customerId) + { + return $this->_updateCustomerSubscription($customerId, false); + } + + /** + * Update the subscription based on latest information of associated customer. + * + * @param int $customerId + * @return $this + * @deprecated The subscription should be updated by store id + * @see \Magento\Newsletter\Model\SubscriptionManager::subscribeCustomer + */ + public function updateSubscription($customerId) + { + $this->loadByCustomerId($customerId); + $this->_updateCustomerSubscription($customerId, $this->isSubscribed()); + return $this; + } + + /** + * Saving customer subscription status + * + * @param int $customerId + * @param bool $subscribe indicates whether the customer should be subscribed or unsubscribed + * @return $this + * @deprecated The subscription should be updated by store id + * @see \Magento\Newsletter\Model\SubscriptionManager::subscribeCustomer + */ + protected function _updateCustomerSubscription($customerId, $subscribe) + { + $storeId = (int)$this->_storeManager->getStore()->getId(); + if ($subscribe) { + $subscriber = $this->subscriptionManager->subscribeCustomer((int)$customerId, $storeId); + } else { + $subscriber = $this->subscriptionManager->unsubscribeCustomer((int)$customerId, $storeId); + } + $this->addData($subscriber->getData()); + + return $this; + } } diff --git a/app/code/Magento/Newsletter/Model/SubscriptionManager.php b/app/code/Magento/Newsletter/Model/SubscriptionManager.php new file mode 100644 index 0000000000000..846d095625e0c --- /dev/null +++ b/app/code/Magento/Newsletter/Model/SubscriptionManager.php @@ -0,0 +1,314 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Newsletter\Model; + +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Exception\MailException; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; +use Psr\Log\LoggerInterface; + +/** + * Class to update newsletter subscription status + */ +class SubscriptionManager implements SubscriptionManagerInterface +{ + /** + * @var SubscriberFactory + */ + private $subscriberFactory; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var AccountManagementInterface + */ + private $customerAccountManagement; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @param SubscriberFactory $subscriberFactory + * @param LoggerInterface $logger + * @param StoreManagerInterface $storeManager + * @param ScopeConfigInterface $scopeConfig + * @param AccountManagementInterface $customerAccountManagement + * @param CustomerRepositoryInterface $customerRepository + */ + public function __construct( + SubscriberFactory $subscriberFactory, + LoggerInterface $logger, + StoreManagerInterface $storeManager, + ScopeConfigInterface $scopeConfig, + AccountManagementInterface $customerAccountManagement, + CustomerRepositoryInterface $customerRepository + ) { + $this->subscriberFactory = $subscriberFactory; + $this->logger = $logger; + $this->storeManager = $storeManager; + $this->scopeConfig = $scopeConfig; + $this->customerAccountManagement = $customerAccountManagement; + $this->customerRepository = $customerRepository; + } + + /** + * @inheritdoc + */ + public function subscribe(string $email, int $storeId): Subscriber + { + $websiteId = (int)$this->storeManager->getStore($storeId)->getWebsiteId(); + $subscriber = $this->subscriberFactory->create()->loadBySubscriberEmail($email, $websiteId); + $currentStatus = (int)$subscriber->getStatus(); + if ($currentStatus === Subscriber::STATUS_SUBSCRIBED) { + return $subscriber; + } + + $status = $this->isConfirmNeed($storeId) ? Subscriber::STATUS_NOT_ACTIVE : Subscriber::STATUS_SUBSCRIBED; + if (!$subscriber->getId()) { + $subscriber->setSubscriberConfirmCode($subscriber->randomSequence()); + $subscriber->setSubscriberEmail($email); + } + $subscriber->setStatus($status) + ->setStoreId($storeId) + ->save(); + + $this->sendEmailAfterChangeStatus($subscriber); + + return $subscriber; + } + + /** + * @inheritdoc + */ + public function unsubscribe(string $email, int $storeId, string $confirmCode): Subscriber + { + $websiteId = (int)$this->storeManager->getStore($storeId)->getWebsiteId(); + /** @var Subscriber $subscriber */ + $subscriber = $this->subscriberFactory->create()->loadBySubscriberEmail($email, $websiteId); + if (!$subscriber->getId()) { + return $subscriber; + } + $subscriber->setCheckCode($confirmCode); + $subscriber->unsubscribe(); + + return $subscriber; + } + + /** + * @inheritdoc + */ + public function subscribeCustomer(int $customerId, int $storeId): Subscriber + { + return $this->updateCustomerSubscription($customerId, $storeId, true); + } + + /** + * @inheritdoc + */ + public function unsubscribeCustomer(int $customerId, int $storeId): Subscriber + { + return $this->updateCustomerSubscription($customerId, $storeId, false); + } + + /** + * Update customer newsletter subscription + * + * @param int $customerId + * @param int $storeId + * @param bool $status + * @return Subscriber + */ + private function updateCustomerSubscription(int $customerId, int $storeId, bool $status): Subscriber + { + $customer = $this->customerRepository->getById($customerId); + $websiteId = (int)$this->storeManager->getStore($storeId)->getWebsiteId(); + $subscriber = $this->loadSubscriberByCustomer($customer, $websiteId); + if (!$status && !$subscriber->getId()) { + return $subscriber; + } + + $newStatus = $this->getNewSubscriptionStatus($subscriber, $customer, $storeId, $status); + $needToSendLetter = $this->saveSubscriber($subscriber, $customer, $storeId, $newStatus); + if ($needToSendLetter) { + $this->sendEmailAfterChangeStatus($subscriber); + } + + return $subscriber; + } + + /** + * Load subscriber model by customer + * + * @param CustomerInterface $customer + * @param int $websiteId + * @return Subscriber + */ + private function loadSubscriberByCustomer(CustomerInterface $customer, int $websiteId): Subscriber + { + $subscriber = $this->subscriberFactory->create(); + $subscriber->loadByCustomer((int)$customer->getId(), $websiteId); + if (!$subscriber->getId()) { + $subscriber->loadBySubscriberEmail((string)$customer->getEmail(), $websiteId); + } + + return $subscriber; + } + + /** + * Save Subscriber model + * + * @param Subscriber $subscriber + * @param CustomerInterface $customer + * @param int $storeId + * @param int $status + * @return bool Need to send email + */ + private function saveSubscriber( + Subscriber $subscriber, + CustomerInterface $customer, + int $storeId, + int $status + ): bool { + $statusChanged = (int)$subscriber->getStatus() !== $status; + $emailChanged = $subscriber->getEmail() !== $customer->getEmail(); + if ($subscriber->getId() + && !$statusChanged + && (int)$subscriber->getCustomerId() === (int)$customer->getId() + && (int)$subscriber->getStoreId() === $storeId + && !$emailChanged + ) { + return false; + } + + if (!$subscriber->getId()) { + $subscriber->setSubscriberConfirmCode($subscriber->randomSequence()); + } + $subscriber->setStatus($status) + ->setStatusChanged($statusChanged) + ->setCustomerId($customer->getId()) + ->setStoreId($storeId) + ->setEmail($customer->getEmail()) + ->save(); + + if ($statusChanged) { + return true; + } + + /** + * If the subscriber is waiting to confirm from the customer + * and customer changed the email + * than need to send confirmation letter to the new email + */ + return $status === Subscriber::STATUS_NOT_ACTIVE && $emailChanged; + } + + /** + * Get new subscription status + * + * @param Subscriber $subscriber + * @param CustomerInterface $customer + * @param int $storeId + * @param bool $subscribe + * @return int + */ + private function getNewSubscriptionStatus( + Subscriber $subscriber, + CustomerInterface $customer, + int $storeId, + bool $subscribe + ): int { + $currentStatus = (int)$subscriber->getStatus(); + // If the current status is already as needed then return them + if (($subscribe && $currentStatus === Subscriber::STATUS_SUBSCRIBED) + || (!$subscribe && $currentStatus === Subscriber::STATUS_UNSUBSCRIBED) + ) { + return $currentStatus; + } + + $status = $currentStatus; + if ($subscribe) { + $customerConfirmStatus = $this->customerAccountManagement->getConfirmationStatus($customer->getId()); + if ($customerConfirmStatus === AccountManagementInterface::ACCOUNT_CONFIRMATION_REQUIRED) { + $status = Subscriber::STATUS_UNCONFIRMED; + } elseif ($this->isConfirmNeed($storeId)) { + $status = Subscriber::STATUS_NOT_ACTIVE; + } else { + $status = Subscriber::STATUS_SUBSCRIBED; + } + } elseif ($currentStatus === Subscriber::STATUS_SUBSCRIBED) { + $status = Subscriber::STATUS_UNSUBSCRIBED; + } + + return $status; + } + + /** + * Sends out email to customer after change subscription status + * + * @param Subscriber $subscriber + * @return void + */ + private function sendEmailAfterChangeStatus(Subscriber $subscriber): void + { + $status = (int)$subscriber->getStatus(); + if ($status === Subscriber::STATUS_UNCONFIRMED) { + return; + } + + try { + switch ($status) { + case Subscriber::STATUS_UNSUBSCRIBED: + $subscriber->sendUnsubscriptionEmail(); + break; + case Subscriber::STATUS_SUBSCRIBED: + $subscriber->sendConfirmationSuccessEmail(); + break; + case Subscriber::STATUS_NOT_ACTIVE: + $subscriber->sendConfirmationRequestEmail(); + break; + } + } catch (MailException $e) { + // If we are not able to send a new account email, this should be ignored + $this->logger->critical($e); + } + } + + /** + * Is need to confirm subscription + * + * @param int $storeId + * @return bool + */ + private function isConfirmNeed(int $storeId): bool + { + return (bool)$this->scopeConfig->isSetFlag( + Subscriber::XML_PATH_CONFIRMATION_FLAG, + ScopeInterface::SCOPE_STORE, + $storeId + ); + } +} diff --git a/app/code/Magento/Newsletter/Model/SubscriptionManagerInterface.php b/app/code/Magento/Newsletter/Model/SubscriptionManagerInterface.php new file mode 100644 index 0000000000000..8b92d825bdbcd --- /dev/null +++ b/app/code/Magento/Newsletter/Model/SubscriptionManagerInterface.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Newsletter\Model; + +/** + * Interface to update newsletter subscription status + */ +interface SubscriptionManagerInterface +{ + /** + * Subscribe to newsletters by email + * + * @param string $email + * @param int $storeId + * @return Subscriber + */ + public function subscribe(string $email, int $storeId): Subscriber; + + /** + * Unsubscribe from newsletters by email + * + * @param string $email + * @param int $storeId + * @param string $confirmCode + * @return Subscriber + */ + public function unsubscribe(string $email, int $storeId, string $confirmCode): Subscriber; + + /** + * Subscribe customer to newsletter + * + * @param int $customerId + * @param int $storeId + * @return Subscriber + */ + public function subscribeCustomer(int $customerId, int $storeId): Subscriber; + + /** + * Unsubscribe customer from newsletter + * + * @param int $customerId + * @param int $storeId + * @return Subscriber + */ + public function unsubscribeCustomer(int $customerId, int $storeId): Subscriber; +} diff --git a/app/code/Magento/Newsletter/Model/Template.php b/app/code/Magento/Newsletter/Model/Template.php index 4c71014826cf5..88fbfb152d14f 100644 --- a/app/code/Magento/Newsletter/Model/Template.php +++ b/app/code/Magento/Newsletter/Model/Template.php @@ -200,9 +200,16 @@ public function getProcessedTemplateSubject(array $variables) { $variables['this'] = $this; - return $this->getTemplateFilter() - ->setVariables($variables) - ->filter($this->getTemplateSubject()); + $filter = $this->getTemplateFilter(); + $filter->setVariables($variables); + + $previousStrictMode = $filter->setStrictMode( + !$this->getData('is_legacy') && is_numeric($this->getTemplateId()) + ); + $result = $filter->filter($this->getTemplateSubject()); + $filter->setStrictMode($previousStrictMode); + + return $result; } /** @@ -227,6 +234,8 @@ public function getTemplateText() } /** + * Return the filter factory + * * @return \Magento\Newsletter\Model\Template\FilterFactory */ protected function getFilterFactory() diff --git a/app/code/Magento/Newsletter/Setup/Patch/Data/FlagLegacyTemplates.php b/app/code/Magento/Newsletter/Setup/Patch/Data/FlagLegacyTemplates.php new file mode 100644 index 0000000000000..13baf7f1dd5d4 --- /dev/null +++ b/app/code/Magento/Newsletter/Setup/Patch/Data/FlagLegacyTemplates.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Newsletter\Setup\Patch\Data; + +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; + +/** + * Flag all existing email templates overrides as legacy + */ +class FlagLegacyTemplates implements DataPatchInterface +{ + /** + * @var ModuleDataSetupInterface + */ + private $moduleDataSetup; + + /** + * @param ModuleDataSetupInterface $moduleDataSetup + */ + public function __construct(ModuleDataSetupInterface $moduleDataSetup) + { + $this->moduleDataSetup = $moduleDataSetup; + } + + /** + * @inheritDoc + */ + public function apply() + { + $this->moduleDataSetup + ->getConnection() + ->update($this->moduleDataSetup->getTable('newsletter_template'), ['is_legacy' => '1']); + } + + /** + * @inheritDoc + */ + public static function getDependencies() + { + return []; + } + + /** + * @inheritDoc + */ + public function getAliases() + { + return []; + } +} diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/CheckSubscribedNewsletterActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/CheckSubscribedNewsletterActionGroup.xml new file mode 100644 index 0000000000000..feedd58c911aa --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/CheckSubscribedNewsletterActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CheckSubscribedNewsletterActionGroup"> + <annotations> + <description>Goes to the Storefront Newsletter Management page. Validates that the 'Subscription' checkbox is checked.</description> + </annotations> + + <amOnPage url="{{StorefrontNewsletterManagePage.url}}" stepKey="goToNewsletterManage"/> + <seeCheckboxIsChecked selector="{{StorefrontNewsletterManageSection.subscriptionCheckbox}}" stepKey="checkSubscribedNewsletter"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/StorefrontCreateNewAccountNewsletterCheckedActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/StorefrontCreateNewAccountNewsletterCheckedActionGroup.xml new file mode 100644 index 0000000000000..ff543febcd9a3 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/StorefrontCreateNewAccountNewsletterCheckedActionGroup.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"> + <!--Create an Account. Check Sign Up for Newsletter checkbox --> + <actionGroup name="StorefrontCreateNewAccountNewsletterCheckedActionGroup" extends="SignUpNewUserFromStorefrontActionGroup"> + <annotations> + <description>EXTENDS: SignUpNewUserFromStorefrontActionGroup. Clicks on 'Sign Up for Newsletter'. Validates that the Subscription Confirmation message is present and correct.</description> + </annotations> + + <click selector="{{StorefrontCustomerCreateFormSection.signUpForNewsletter}}" stepKey="selectSignUpForNewsletterCheckbox" after="fillLastName"/> + <see stepKey="seeDescriptionNewsletter" userInput='You are subscribed to "General Subscription".' selector="{{CustomerMyAccountPage.DescriptionNewsletter}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/StorefrontCreateNewAccountNewsletterUncheckedActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/StorefrontCreateNewAccountNewsletterUncheckedActionGroup.xml new file mode 100644 index 0000000000000..d6b0adff53a86 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/StorefrontCreateNewAccountNewsletterUncheckedActionGroup.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="StorefrontCreateNewAccountNewsletterUncheckedActionGroup" extends="SignUpNewUserFromStorefrontActionGroup"> + <annotations> + <description>EXTENDS: SignUpNewUserFromStorefrontActionGroup. Validates that the you are NOT subscribed message is present and correct.</description> + </annotations> + <arguments> + <argument name="Customer"/> + <argument name="Store"/> + </arguments> + + <amOnPage stepKey="amOnStorefrontPage" url="{{Store.code}}"/> + <see stepKey="seeDescriptionNewsletter" userInput="You aren't subscribed to our newsletter." selector="{{CustomerMyAccountPage.DescriptionNewsletter}}"/> + <see stepKey="seeThankYouMessage" userInput="Thank you for registering with NewStore."/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminNewsletterTemplateActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/SwitchToPreviewIframeActionGroup.xml similarity index 100% rename from app/code/Magento/Newsletter/Test/Mftf/ActionGroup/AdminNewsletterTemplateActionGroup.xml rename to app/code/Magento/Newsletter/Test/Mftf/ActionGroup/SwitchToPreviewIframeActionGroup.xml diff --git a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml b/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml deleted file mode 100644 index 69b973dbddbda..0000000000000 --- a/app/code/Magento/Newsletter/Test/Mftf/ActionGroup/VerifySubscribedNewsletterDisplayedActionGroup.xml +++ /dev/null @@ -1,45 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Create an Account. Check Sign Up for Newsletter checkbox --> - <actionGroup name="StorefrontCreateNewAccountNewsletterChecked" extends="SignUpNewUserFromStorefrontActionGroup"> - <annotations> - <description>EXTENDS: SignUpNewUserFromStorefrontActionGroup. Clicks on 'Sign Up for Newsletter'. Validates that the Subscription Confirmation message is present and correct.</description> - </annotations> - - <click selector="{{StorefrontCustomerCreateFormSection.signUpForNewsletter}}" stepKey="selectSignUpForNewsletterCheckbox" after="fillLastName"/> - <see stepKey="seeDescriptionNewsletter" userInput='You are subscribed to "General Subscription".' selector="{{CustomerMyAccountPage.DescriptionNewsletter}}"/> - </actionGroup> - - <!--Create an Account. Uncheck Sign Up for Newsletter checkbox --> - <actionGroup name="StorefrontCreateNewAccountNewsletterUnchecked" extends="SignUpNewUserFromStorefrontActionGroup"> - <annotations> - <description>EXTENDS: SignUpNewUserFromStorefrontActionGroup. Validates that the you are NOT subscribed message is present and correct.</description> - </annotations> - <arguments> - <argument name="Customer"/> - <argument name="Store"/> - </arguments> - - <amOnPage stepKey="amOnStorefrontPage" url="{{Store.code}}"/> - <see stepKey="seeDescriptionNewsletter" userInput="You aren't subscribed to our newsletter." selector="{{CustomerMyAccountPage.DescriptionNewsletter}}"/> - <see stepKey="seeThankYouMessage" userInput="Thank you for registering with NewStore."/> - </actionGroup> - - <!--Check Subscribed Newsletter via StoreFront--> - <actionGroup name="CheckSubscribedNewsletterActionGroup"> - <annotations> - <description>Goes to the Storefront Newsletter Management page. Validates that the 'Subscription' checkbox is checked.</description> - </annotations> - - <amOnPage url="{{StorefrontNewsletterManagePage.url}}" stepKey="goToNewsletterManage"/> - <seeCheckboxIsChecked selector="{{StorefrontNewsletterManageSection.subscriptionCheckbox}}" stepKey="checkSubscribedNewsletter"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Page/AdminNewsletterSubscriberPage.xml b/app/code/Magento/Newsletter/Test/Mftf/Page/AdminNewsletterSubscriberPage.xml new file mode 100644 index 0000000000000..0193064c8756c --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Page/AdminNewsletterSubscriberPage.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminNewsletterSubscriberPage" url="newsletter/subscriber/index/" area="admin" module="Magento_Newsletter"> + <section name="AdminNewsletterSubscriberGridSection"/> + </page> +</pages> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterSubscriberGridSection.xml b/app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterSubscriberGridSection.xml new file mode 100644 index 0000000000000..3332041817150 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Section/AdminNewsletterSubscriberGridSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminNewsletterSubscriberGridSection"> + <element name="email" type="text" selector="//table[contains(@class, 'data-grid')]/tbody/tr[{{row}}][@data-role='row']/td[@data-column='email']" parameterized="true"/> + <element name="type" type="text" selector="//table[contains(@class, 'data-grid')]/tbody/tr[{{row}}][@data-role='row']/td[@data-column='type']" parameterized="true"/> + <element name="firstName" type="text" selector="//table[contains(@class, 'data-grid')]/tbody/tr[{{row}}][@data-role='row']/td[@data-column='firstname']" parameterized="true"/> + <element name="lastName" type="text" selector="//table[contains(@class, 'data-grid')]/tbody/tr[{{row}}][@data-role='row']/td[@data-column='lastname']" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddImageToWYSIWYGNewsletterTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddImageToWYSIWYGNewsletterTest.xml index 0371c0265d149..4992a36c0b3cb 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddImageToWYSIWYGNewsletterTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddImageToWYSIWYGNewsletterTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="login"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <!--Create a newsletter template that contains an image--> @@ -33,16 +33,16 @@ <waitForElementVisible selector="{{NewsletterWYSIWYGSection.TinyMCE4}}" stepKey="waitForTinyMCE4"/> <click selector="{{TinyMCESection.InsertImageIcon}}" stepKey="clickInsertImageIcon" /> <waitForPageLoad stepKey="waitForPageLoad" /> - <actionGroup ref="clickBrowseBtnOnUploadPopup" stepKey="clickBrowserBtn"/> - <actionGroup ref="VerifyMediaGalleryStorageActions" stepKey="VerifyMediaGalleryStorageBtn"/> - <actionGroup ref="CreateImageFolder" stepKey="CreateImageFolder"> + <actionGroup ref="ClickBrowseBtnOnUploadPopupActionGroup" stepKey="clickBrowserBtn"/> + <actionGroup ref="VerifyMediaGalleryStorageActionsActionGroup" stepKey="VerifyMediaGalleryStorageBtn"/> + <actionGroup ref="CreateImageFolderActionGroup" stepKey="CreateImageFolder"> <argument name="ImageFolder" value="ImageFolder"/> </actionGroup> - <actionGroup ref="attachImage" stepKey="attachImage1"> + <actionGroup ref="AttachImageActionGroup" stepKey="attachImage1"> <argument name="Image" value="ImageUpload3"/> </actionGroup> - <actionGroup ref="saveImage" stepKey="insertImage"/> - <actionGroup ref="fillOutUploadImagePopup" stepKey="fillOutUploadImagePopup" /> + <actionGroup ref="SaveImageActionGroup" stepKey="insertImage"/> + <actionGroup ref="FillOutUploadImagePopupActionGroup" stepKey="fillOutUploadImagePopup" /> <click selector="{{BasicFieldNewsletterSection.save}}" stepKey="clickSaveTemplate"/> <waitForPageLoad stepKey="waitForPageLoad10" /> <!--Preview the newsletter template--> @@ -57,7 +57,7 @@ <seeElementInDOM selector="{{StorefrontNewsletterSection.imageSource(ImageUpload3.fileName)}}" stepKey="assertMediaSource"/> <closeTab stepKey="closeTab"/> <after> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddVariableToWYSIWYGNewsletterTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddVariableToWYSIWYGNewsletterTest.xml index b6f78b6f33792..958340671ac43 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddVariableToWYSIWYGNewsletterTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddVariableToWYSIWYGNewsletterTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <!--Create Custom Variable--> @@ -99,7 +99,7 @@ <dontSee userInput="{{customVariable.html}}" stepKey="dontSeeCustomVariableName" /> <closeTab stepKey="closeTab"/> <after> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddWidgetToWYSIWYGNewsletterTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddWidgetToWYSIWYGNewsletterTest.xml index 273a39a312132..29c125a52c9e2 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddWidgetToWYSIWYGNewsletterTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddWidgetToWYSIWYGNewsletterTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="login"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <amOnPage url="{{NewsletterTemplateForm.url}}" stepKey="amOnNewsletterTemplatePage"/> @@ -54,7 +54,7 @@ <see userInput="Home page" stepKey="seeHomePageCMSPage"/> <closeTab stepKey="closeTab"/> <after> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminNameEmptyForGuestTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminNameEmptyForGuestTest.xml new file mode 100644 index 0000000000000..07c7cc050d5cf --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminNameEmptyForGuestTest.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminNameEmptyForGuestTest"> + <annotations> + <features value="Newsletter"/> + <group value="Newsletter"/> + <title value="Empty name for Guest Customer"/> + <description value="'Customer First Name' and 'Customer Last Name' should be empty for Guest Customer in Newsletter Subscribers Grid"/> + <severity value="MINOR"/> + </annotations> + + <before> + <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> + </before> + + <after> + <actionGroup ref="logout" stepKey="adminLogout"/> + </after> + + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> + <waitForPageLoad stepKey="waitForCustomerPage"/> + <scrollTo selector="{{StorefrontCustomerFooterSection.footerBlock}}" stepKey="scrollToFooter"/> + <fillField userInput="{{Simple_US_Customer.email}}" selector="{{StorefrontCustomerFooterSection.formSubscribe}}" + stepKey="giveEmail"/> + <click selector="{{StorefrontCustomerFooterSection.buttonSubscribe}}" stepKey="clickSubscribeButton"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <see selector="{{StorefrontCustomerMessagesSection.successMessage}}" + userInput="Thank you for your subscription." stepKey="checkSuccessMessage"/> + + <amOnPage url="{{AdminNewsletterSubscriberPage.url}}" stepKey="amOnNewsletterSubscribersPage"/> + <see selector="{{AdminNewsletterSubscriberGridSection.email('1')}}" userInput="{{Simple_US_Customer.email}}" + stepKey="checkEmail"/> + <see selector="{{AdminNewsletterSubscriberGridSection.type('1')}}" userInput="Guest" stepKey="checkType"/> + <see selector="{{AdminNewsletterSubscriberGridSection.firstName('1')}}" userInput="" stepKey="checkFirstName"/> + <see selector="{{AdminNewsletterSubscriberGridSection.lastName('1')}}" userInput="" stepKey="checkLastName"/> + </test> +</tests> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/VerifySubscribedNewsletterDisplayedTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/VerifySubscribedNewsletterDisplayedTest.xml index 4d60b7676605e..200eb0e49f5b2 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/VerifySubscribedNewsletterDisplayedTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/VerifySubscribedNewsletterDisplayedTest.xml @@ -37,13 +37,13 @@ <argument name="StoreGroup" value="staticStoreGroup"/> <argument name="customStore" value="staticStore"/> </actionGroup> - <actionGroup ref="EnableWebUrlOptions" stepKey="addStoreCodeToUrls"/> + <actionGroup ref="EnableWebUrlOptionsActionGroup" stepKey="addStoreCodeToUrls"/> <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> <!--Delete created data and set Default Configuration--> - <actionGroup ref="ResetWebUrlOptions" stepKey="resetUrlOption"/> + <actionGroup ref="ResetWebUrlOptionsActionGroup" stepKey="resetUrlOption"/> <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> <argument name="websiteName" value="Second"/> </actionGroup> @@ -53,14 +53,14 @@ </after> <!--Go to store front (default) and click Create an Account.--> - <actionGroup ref="StorefrontCreateNewAccountNewsletterChecked" stepKey="SignUpNewUser"> + <actionGroup ref="StorefrontCreateNewAccountNewsletterCheckedActionGroup" stepKey="SignUpNewUser"> <argument name="Customer" value="CustomerEntityOne"/> </actionGroup> <!--Sign Out--> - <amOnPage url="customer/account/logout/" stepKey="customerOnLogoutPage"/> + <amOnPage url="{{StorefrontCustomerLogoutPage.url}}" stepKey="customerOnLogoutPage"/> <waitForPageLoad stepKey="waitLogoutCustomer"/> <!--Create new Account with the same email address. (unchecked Sign Up for Newsletter checkbox)--> - <actionGroup ref="StorefrontCreateNewAccountNewsletterUnchecked" stepKey="createNewAccountNewsletterUnchecked"> + <actionGroup ref="StorefrontCreateNewAccountNewsletterUncheckedActionGroup" stepKey="createNewAccountNewsletterUnchecked"> <argument name="Customer" value="CustomerEntityOne"/> <argument name="Store" value="staticStore"/> </actionGroup> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnNewsletterTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnNewsletterTest.xml index 722a0dd22119d..0c20357127e6d 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnNewsletterTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/VerifyTinyMCEv4IsNativeWYSIWYGOnNewsletterTest.xml @@ -19,7 +19,7 @@ </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> </before> <amOnPage url="{{NewsletterTemplateForm.url}}" stepKey="amOnNewsletterTemplatePage"/> @@ -48,7 +48,7 @@ <see userInput="Hello World From Newsletter Template!" stepKey="seeContent" /> <closeTab stepKey="closeTab"/> <after> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/ObserverTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/ObserverTest.php new file mode 100644 index 0000000000000..7bf6d1e1c8671 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Unit/Model/ObserverTest.php @@ -0,0 +1,63 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Newsletter\Test\Unit\Model; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Newsletter\Model\Observer; +use Magento\Newsletter\Model\ResourceModel\Queue\Collection; +use Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory; +use PHPUnit\Framework\TestCase; + +class ObserverTest extends TestCase +{ + /** + * @var Observer + */ + private $model; + + /** + * @var CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $collectionFactoryMock; + + /** + * Setup environment for test + */ + protected function setUp() + { + $objectManager = new ObjectManagerHelper($this); + + $this->collectionFactoryMock = $this->createPartialMock( + CollectionFactory::class, + ['create'] + ); + + $this->model = $objectManager->getObject( + Observer::class, + [ + 'queueCollectionFactory' => $this->collectionFactoryMock + ] + ); + } + + /** + * Test scheduledSend() method + */ + public function testScheduledSend() + { + $collectionMock = $this->createMock(Collection::class); + $this->collectionFactoryMock->expects($this->once())->method('create')->willReturn($collectionMock); + $collectionMock->expects($this->once())->method('setPageSize')->with(3)->willReturnSelf(); + $collectionMock->expects($this->once())->method('setCurPage')->with(1)->willReturnSelf(); + $collectionMock->expects($this->once())->method('addOnlyForSendingFilter')->willReturnSelf(); + $collectionMock->expects($this->once())->method('load')->willReturnSelf(); + $collectionMock->expects($this->once())->method('walk')->with('sendPerSubscriber', [20]); + + $this->model->scheduledSend(); + } +} diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php index e809b7e37a432..52b3df8cb8aa6 100644 --- a/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php +++ b/app/code/Magento/Newsletter/Test/Unit/Model/Plugin/CustomerPluginTest.php @@ -5,149 +5,160 @@ */ namespace Magento\Newsletter\Test\Unit\Model\Plugin; +use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\Config\Share; use Magento\Customer\Model\ResourceModel\CustomerRepository; use Magento\Customer\Api\Data\CustomerExtensionInterface; use Magento\Framework\Api\ExtensionAttributesFactory; -use Magento\Newsletter\Model\ResourceModel\Subscriber; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Newsletter\Model\Plugin\CustomerPlugin; +use Magento\Newsletter\Model\ResourceModel\Subscriber\Collection; +use Magento\Newsletter\Model\ResourceModel\Subscriber\CollectionFactory; +use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Newsletter\Model\SubscriptionManagerInterface; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Store\Model\Website; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class CustomerPluginTest extends \PHPUnit\Framework\TestCase +/** + * Class to test Newsletter Plugin for customer + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class CustomerPluginTest extends TestCase { /** - * @var \Magento\Newsletter\Model\Plugin\CustomerPlugin + * @var SubscriberFactory|MockObject */ - private $plugin; + private $subscriberFactory; /** - * @var \Magento\Newsletter\Model\SubscriberFactory|\PHPUnit_Framework_MockObject_MockObject + * @var ExtensionAttributesFactory|MockObject */ - private $subscriberFactory; + private $extensionFactory; /** - * @var \Magento\Newsletter\Model\Subscriber|\PHPUnit_Framework_MockObject_MockObject + * @var CollectionFactory|MockObject */ - private $subscriber; + private $collectionFactory; /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + * @var SubscriptionManagerInterface|MockObject */ - private $objectManager; + private $subscriptionManager; /** - * @var ExtensionAttributesFactory|\PHPUnit_Framework_MockObject_MockObject + * @var Share|MockObject */ - private $extensionFactoryMock; + private $shareConfig; /** - * @var CustomerExtensionInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|MockObject */ - private $customerExtensionMock; + private $storeManager; /** - * @var Subscriber|\PHPUnit_Framework_MockObject_MockObject + * @var ObjectManager */ - private $subscriberResourceMock; + private $objectManager; /** - * @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerPlugin */ - private $customerMock; + private $plugin; + /** + * @inheritdoc + */ protected function setUp() { - $this->subscriberFactory = $this->getMockBuilder(\Magento\Newsletter\Model\SubscriberFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->subscriber = $this->getMockBuilder(\Magento\Newsletter\Model\Subscriber::class) - ->setMethods( - [ - 'loadByEmail', - 'getId', - 'delete', - 'updateSubscription', - 'subscribeCustomerById', - 'unsubscribeCustomerById', - 'isSubscribed', - ] - )->disableOriginalConstructor() - ->getMock(); - $this->extensionFactoryMock = $this->getMockBuilder(ExtensionAttributesFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->customerExtensionMock = $this->getMockBuilder(CustomerExtensionInterface::class) - ->setMethods(['getIsSubscribed', 'setIsSubscribed']) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $this->subscriberResourceMock = $this->getMockBuilder(Subscriber::class) - ->disableOriginalConstructor() - ->getMock(); - $this->customerMock = $this->getMockBuilder(CustomerInterface::class) - ->setMethods(['getExtensionAttributes']) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $this->subscriberFactory->expects($this->any())->method('create')->willReturn($this->subscriber); - $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - + $this->subscriberFactory = $this->createMock(SubscriberFactory::class); + $this->extensionFactory = $this->createMock(ExtensionAttributesFactory::class); + $this->collectionFactory = $this->createMock(CollectionFactory::class); + $this->subscriptionManager = $this->createMock(SubscriptionManagerInterface::class); + $this->shareConfig = $this->createMock(Share::class); + $this->storeManager = $this->createMock(StoreManagerInterface::class); + $this->objectManager = new ObjectManager($this); $this->plugin = $this->objectManager->getObject( - \Magento\Newsletter\Model\Plugin\CustomerPlugin::class, + CustomerPlugin::class, [ 'subscriberFactory' => $this->subscriberFactory, - 'extensionFactory' => $this->extensionFactoryMock, - 'subscriberResource' => $this->subscriberResourceMock, + 'extensionFactory' => $this->extensionFactory, + 'collectionFactory' => $this->collectionFactory, + 'subscriptionManager' => $this->subscriptionManager, + 'shareConfig' => $this->shareConfig, + 'storeManager' => $this->storeManager, ] ); } /** - * @param bool $subscriptionOriginalValue - * @param bool $subscriptionNewValue + * Test to update customer subscription after save customer + * + * @param int|null $originalStatus + * @param bool|null $newValue + * @param bool|null $expectedSubscribe * @dataProvider afterSaveDataProvider - * @return void */ - public function testAfterSave($subscriptionOriginalValue, $subscriptionNewValue) + public function testAfterSave(?int $originalStatus, ?bool $newValue, ?bool $expectedSubscribe) { - $customerId = 1; - /** @var CustomerInterface | \PHPUnit_Framework_MockObject_MockObject $result */ - $result = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); - /** @var CustomerRepository | \PHPUnit_Framework_MockObject_MockObject $subject */ - $subject = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class); + $storeId = 2; + $websiteId = 1; + $customerId = 3; + $customerEmail = 'email@example.com'; + + $store = $this->createMock(StoreInterface::class); + $store->method('getId')->willReturn($storeId); + $store->method('getWebsiteId')->willReturn($websiteId); + $this->storeManager->method('getStore')->willReturn($store); - /** @var CustomerExtensionInterface|\PHPUnit_Framework_MockObject_MockObject $resultExtensionAttributes */ - $resultExtensionAttributes = $this->getMockBuilder(CustomerExtensionInterface::class) - ->setMethods(['getIsSubscribed', 'setIsSubscribed']) - ->getMockForAbstractClass(); - $result->expects($this->atLeastOnce())->method('getId')->willReturn($customerId); - $result->expects($this->any())->method('getExtensionAttributes')->willReturn(null); - $this->extensionFactoryMock->expects($this->any()) - ->method('create') - ->willReturn($resultExtensionAttributes); - $result->expects($this->once()) - ->method('setExtensionAttributes') - ->with($resultExtensionAttributes) + $subscriber = $this->createMock(Subscriber::class); + $subscriber->method('getStatus')->willReturn($originalStatus); + $subscriber->method('getEmail')->willReturn($customerEmail); + $subscriber->method('isSubscribed')->willReturn($originalStatus === Subscriber::STATUS_SUBSCRIBED); + $subscriber->expects($this->once()) + ->method('loadByCustomer') + ->with($customerId, $websiteId) + ->willReturnSelf(); + $subscriber->expects($this->once()) + ->method('loadBySubscriberEmail') + ->with($customerEmail, $websiteId) ->willReturnSelf(); - $this->customerMock->expects($this->once()) - ->method('getExtensionAttributes') - ->willReturn($this->customerExtensionMock); - $resultExtensionAttributes->expects($this->any()) - ->method('getIsSubscribed') - ->willReturn($subscriptionOriginalValue); - $this->customerExtensionMock->expects($this->any()) - ->method('getIsSubscribed') - ->willReturn($subscriptionNewValue); + $this->subscriberFactory->method('create')->willReturn($subscriber); - if ($subscriptionOriginalValue !== $subscriptionNewValue) { - if ($subscriptionNewValue) { - $this->subscriber->expects($this->once())->method('subscribeCustomerById')->with($customerId); - } else { - $this->subscriber->expects($this->once())->method('unsubscribeCustomerById')->with($customerId); - } - $this->subscriber->expects($this->once())->method('isSubscribed')->willReturn($subscriptionNewValue); - $resultExtensionAttributes->expects($this->once())->method('setIsSubscribed')->with($subscriptionNewValue); + $customerExtension = $this->createPartialMock(CustomerExtensionInterface::class, ['getIsSubscribed']); + $customerExtension->method('getIsSubscribed')->willReturn($newValue); + /** @var CustomerInterface|MockObject $customer */ + $customer = $this->createMock(CustomerInterface::class); + $customer->method('getExtensionAttributes')->willReturn($customerExtension); + + $resultIsSubscribed = $newValue ?? $originalStatus === Subscriber::STATUS_SUBSCRIBED; + if ($expectedSubscribe !== null) { + $resultSubscriber = $this->createMock(Subscriber::class); + $resultSubscriber->method('isSubscribed')->willReturn($resultIsSubscribed); + $this->subscriptionManager->expects($this->once()) + ->method($expectedSubscribe ? 'subscribeCustomer' : 'unsubscribeCustomer') + ->with($customerId, $storeId) + ->willReturn($resultSubscriber); + } else { + $this->subscriptionManager->expects($this->never())->method('subscribeCustomer'); + $this->subscriptionManager->expects($this->never())->method('unsubscribeCustomer'); } + $resultExtension = $this->createPartialMock(CustomerExtensionInterface::class, ['setIsSubscribed']); + $resultExtension->expects($this->once())->method('setIsSubscribed')->with($resultIsSubscribed); + /** @var CustomerInterface|MockObject $result */ + $result = $this->createMock(CustomerInterface::class); + $result->method('getId')->willReturn($customerId); + $result->method('getEmail')->willReturn($customerEmail); + $result->method('getExtensionAttributes')->willReturn($resultExtension); - $this->assertEquals($result, $this->plugin->afterSave($subject, $result, $this->customerMock)); + /** @var CustomerRepository|MockObject $subject */ + $subject = $this->createMock(CustomerRepositoryInterface::class); + $this->assertEquals($result, $this->plugin->afterSave($subject, $result, $customer)); } /** @@ -156,115 +167,135 @@ public function testAfterSave($subscriptionOriginalValue, $subscriptionNewValue) public function afterSaveDataProvider() { return [ - [true, true], - [false, false], - [true, false], - [false, true], + [null, null, null], + [null, true, true], + [null, false, null], + [Subscriber::STATUS_SUBSCRIBED, null, null], + [Subscriber::STATUS_SUBSCRIBED, true, null], + [Subscriber::STATUS_SUBSCRIBED, false, false], + [Subscriber::STATUS_UNSUBSCRIBED, null, null], + [Subscriber::STATUS_UNSUBSCRIBED, true, true], + [Subscriber::STATUS_UNSUBSCRIBED, false, null], + [Subscriber::STATUS_UNCONFIRMED, null, true], + [Subscriber::STATUS_UNCONFIRMED, true, true], + [Subscriber::STATUS_UNCONFIRMED, false, true], ]; } + /** + * Test to delete subscriptions after delete customer + */ public function testAfterDelete() { - $subject = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class); - $customer = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); - $customer->expects($this->once())->method('getEmail')->willReturn('test@test.com'); - $this->subscriber->expects($this->once())->method('loadByEmail')->with('test@test.com')->willReturnSelf(); - $this->subscriber->expects($this->once())->method('getId')->willReturn(1); - $this->subscriber->expects($this->once())->method('delete')->willReturnSelf(); + $customerEmail = 'email@example.com'; + $websiteId = 1; + $storeIds = [1, 2]; + + $subscriber = $this->createMock(Subscriber::class); + $subscriber->expects($this->once())->method('delete'); + $collection = $this->createMock(Collection::class); + $collection->expects($this->once()) + ->method('addFieldToFilter') + ->with('subscriber_email', $customerEmail) + ->willReturnSelf(); + $collection->method('getIterator')->willReturn(new \ArrayIterator([$subscriber])); + $this->collectionFactory->expects($this->once())->method('create')->willReturn($collection); + $this->shareConfig->method('isWebsiteScope')->willReturn(false); + $website = $this->createMock(Website::class); + $website->method('getStoreIds')->willReturn($storeIds); + $this->storeManager->method('getWebsite')->with($websiteId)->willReturn($website); - $this->assertEquals(true, $this->plugin->afterDelete($subject, true, $customer)); + /** @var CustomerRepositoryInterface|MockObject $subject */ + $subject = $this->createMock(CustomerRepositoryInterface::class); + /** @var CustomerInterface|MockObject $customer */ + $customer = $this->createMock(CustomerInterface::class); + $customer->method('getEmail')->willReturn($customerEmail); + + $this->assertTrue($this->plugin->afterDelete($subject, true, $customer)); } + /** + * Test to delete subscriptions after delete customer by id + */ public function testAroundDeleteById() { $customerId = 1; + $customerEmail = 'test@test.com'; + $websiteId = 1; + $storeIds = [1, 2]; $deleteCustomerById = function () { return true; }; - $subject = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class); - $customer = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); - $subject->expects($this->once())->method('getById')->willReturn($customer); - $customer->expects($this->once())->method('getEmail')->willReturn('test@test.com'); - $this->subscriber->expects($this->once())->method('loadByEmail')->with('test@test.com')->willReturnSelf(); - $this->subscriber->expects($this->once())->method('getId')->willReturn(1); - $this->subscriber->expects($this->once())->method('delete')->willReturnSelf(); + $customer = $this->createMock(CustomerInterface::class); + $customer->expects($this->once())->method('getEmail')->willReturn($customerEmail); + /** @var CustomerRepositoryInterface|MockObject $subject */ + $subject = $this->createMock(CustomerRepositoryInterface::class); + $subject->expects($this->once())->method('getById')->with($customerId)->willReturn($customer); + + $subscriber = $this->createMock(Subscriber::class); + $subscriber->expects($this->once())->method('delete'); + $collection = $this->createMock(Collection::class); + $collection->expects($this->once()) + ->method('addFieldToFilter') + ->with('subscriber_email', $customerEmail) + ->willReturnSelf(); + $collection->method('getIterator')->willReturn(new \ArrayIterator([$subscriber])); + $this->collectionFactory->expects($this->once())->method('create')->willReturn($collection); + $this->shareConfig->method('isWebsiteScope')->willReturn(false); + $website = $this->createMock(Website::class); + $website->method('getStoreIds')->willReturn($storeIds); + $this->storeManager->method('getWebsite')->with($websiteId)->willReturn($website); - $this->assertEquals(true, $this->plugin->aroundDeleteById($subject, $deleteCustomerById, $customerId)); + $this->assertTrue($this->plugin->aroundDeleteById($subject, $deleteCustomerById, $customerId)); } /** - * @param int|null $subscriberStatusKey - * @param int|null $subscriberStatusValue - * @param bool $isSubscribed - * @dataProvider afterGetByIdDataProvider - * @return void + * Test to load extension attribute after get by id */ - public function testAfterGetByIdCreatesExtensionAttributesIfItIsNotSet( - $subscriberStatusKey, - $subscriberStatusValue, - $isSubscribed - ) { - $subject = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class); - $subscriber = [$subscriberStatusKey => $subscriberStatusValue]; + public function testAfterGetByIdCreatesExtensionAttributes(): void + { + $storeId = 2; + $websiteId = 1; + $customerId = 3; + $customerEmail = 'email@example.com'; + $subscribed = true; - $this->extensionFactoryMock->expects($this->any()) - ->method('create') - ->willReturn($this->customerExtensionMock); - $this->customerMock->expects($this->once()) - ->method('setExtensionAttributes') - ->with($this->customerExtensionMock) - ->willReturnSelf(); - $this->customerMock->expects($this->any()) - ->method('getId') - ->willReturn(1); - $this->subscriberResourceMock->expects($this->once()) - ->method('loadByCustomerData') - ->with($this->customerMock) - ->willReturn($subscriber); - $this->customerExtensionMock->expects($this->once())->method('setIsSubscribed')->with($isSubscribed); + $store = $this->createMock(StoreInterface::class); + $store->method('getId')->willReturn($storeId); + $store->method('getWebsiteId')->willReturn($websiteId); + $this->storeManager->method('getStore')->willReturn($store); - $this->assertEquals( - $this->customerMock, - $this->plugin->afterGetById($subject, $this->customerMock) - ); - } + /** @var CustomerInterface|MockObject $customer */ + $customer = $this->createMock(CustomerInterface::class); + $customer->method('getId')->willReturn($customerId); + $customer->method('getEmail')->willReturn($customerEmail); - public function testAfterGetByIdSetsIsSubscribedFlagIfItIsNotSet() - { - $subject = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class); - $subscriber = ['subscriber_id' => 1, 'subscriber_status' => 1]; - - $this->customerMock->expects($this->any()) - ->method('getExtensionAttributes') - ->willReturn($this->customerExtensionMock); - $this->customerExtensionMock->expects($this->any()) - ->method('getIsSubscribed') - ->willReturn(null); - $this->subscriberResourceMock->expects($this->once()) - ->method('loadByCustomerData') - ->with($this->customerMock) - ->willReturn($subscriber); - $this->customerExtensionMock->expects($this->once()) - ->method('setIsSubscribed') + $subscriber = $this->createMock(Subscriber::class); + $subscriber->method('getEmail')->willReturn($customerEmail); + $subscriber->method('isSubscribed')->willReturn($subscribed); + $subscriber->expects($this->once()) + ->method('loadByCustomer') + ->with($customerId, $websiteId) + ->willReturnSelf(); + $subscriber->expects($this->once()) + ->method('loadBySubscriberEmail') + ->with($customerEmail, $websiteId) ->willReturnSelf(); + $this->subscriberFactory->method('create')->willReturn($subscriber); - $this->assertEquals( - $this->customerMock, - $this->plugin->afterGetById($subject, $this->customerMock) + $customerExtension = $this->createPartialMock( + CustomerExtensionInterface::class, + ['getIsSubscribed', 'setIsSubscribed'] ); - } + $customerExtension->expects($this->once())->method('setIsSubscribed')->with($subscribed); + $this->extensionFactory->expects($this->once())->method('create')->willReturn($customerExtension); + $customer->expects($this->once())->method('setExtensionAttributes')->with($customerExtension); - /** - * @return array - */ - public function afterGetByIdDataProvider() - { - return [ - ['subscriber_status', 1, true], - ['subscriber_status', 2, false], - ['subscriber_status', 3, false], - ['subscriber_status', 4, false], - [null, null, false], - ]; + /** @var CustomerRepositoryInterface|MockObject $subject */ + $subject = $this->createMock(CustomerRepositoryInterface::class); + $this->assertEquals( + $customer, + $this->plugin->afterGetById($subject, $customer) + ); } } diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php index 6ccbba9f8828b..c3814563aa46c 100644 --- a/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php +++ b/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php @@ -5,118 +5,152 @@ */ namespace Magento\Newsletter\Test\Unit\Model; +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterfaceFactory; +use Magento\Customer\Model\Session; +use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\App\Area; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Mail\Template\TransportBuilder; +use Magento\Framework\Mail\TransportInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\Translate\Inline\StateInterface; +use Magento\Newsletter\Helper\Data; +use Magento\Newsletter\Model\Queue; use Magento\Newsletter\Model\Subscriber; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; /** + * Test Subscriber model functionality + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class SubscriberTest extends \PHPUnit\Framework\TestCase +class SubscriberTest extends TestCase { /** - * @var \Magento\Newsletter\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + * @var Data|MockObject */ - protected $newsletterData; + private $newsletterData; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ - protected $scopeConfig; + private $scopeConfig; /** - * @var \Magento\Framework\Mail\Template\TransportBuilder|\PHPUnit_Framework_MockObject_MockObject + * @var TransportBuilder|MockObject */ - protected $transportBuilder; + private $transportBuilder; /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StoreManagerInterface|MockObject */ - protected $storeManager; + private $storeManager; /** - * @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject + * @var Session|MockObject */ - protected $customerSession; + private $customerSession; /** - * @var \Magento\Customer\Api\CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerRepositoryInterface|MockObject */ - protected $customerRepository; + private $customerRepository; /** - * @var \Magento\Customer\Api\AccountManagementInterface|\PHPUnit_Framework_MockObject_MockObject + * @var AccountManagementInterface|MockObject */ - protected $customerAccountManagement; + private $customerAccountManagement; /** - * @var \Magento\Framework\Translate\Inline\StateInterface|\PHPUnit_Framework_MockObject_MockObject + * @var StateInterface|MockObject */ - protected $inlineTranslation; + private $inlineTranslation; /** - * @var \Magento\Newsletter\Model\ResourceModel\Subscriber|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Newsletter\Model\ResourceModel\Subscriber|MockObject */ - protected $resource; + private $resource; /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + * @var ObjectManager */ - protected $objectManager; + private $objectManager; /** - * @var \Magento\Framework\Api\DataObjectHelper|\PHPUnit_Framework_MockObject_MockObject + * @var DataObjectHelper|MockObject */ private $dataObjectHelper; /** - * @var \Magento\Customer\Api\Data\CustomerInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerInterfaceFactory|MockObject */ private $customerFactory; /** - * @var \Magento\Newsletter\Model\Subscriber + * @var Subscriber */ - protected $subscriber; + private $subscriber; + /** + * @inheritdoc + */ protected function setUp() { - $this->newsletterData = $this->createMock(\Magento\Newsletter\Helper\Data::class); - $this->scopeConfig = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); - $this->transportBuilder = $this->createPartialMock(\Magento\Framework\Mail\Template\TransportBuilder::class, [ + $this->newsletterData = $this->createMock(Data::class); + $this->scopeConfig = $this->createMock(ScopeConfigInterface::class); + $this->transportBuilder = $this->createPartialMock( + TransportBuilder::class, + [ 'setTemplateIdentifier', 'setTemplateOptions', 'setTemplateVars', 'setFrom', 'addTo', 'getTransport' - ]); - $this->storeManager = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); - $this->customerSession = $this->createPartialMock(\Magento\Customer\Model\Session::class, [ + ] + ); + $this->storeManager = $this->createMock(StoreManagerInterface::class); + $this->customerSession = $this->createPartialMock( + Session::class, + [ 'isLoggedIn', 'getCustomerDataObject', 'getCustomerId' - ]); - $this->customerRepository = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class); - $this->customerAccountManagement = $this->createMock(\Magento\Customer\Api\AccountManagementInterface::class); - $this->inlineTranslation = $this->createMock(\Magento\Framework\Translate\Inline\StateInterface::class); - $this->resource = $this->createPartialMock(\Magento\Newsletter\Model\ResourceModel\Subscriber::class, [ + ] + ); + $this->customerRepository = $this->createMock(CustomerRepositoryInterface::class); + $this->customerAccountManagement = $this->createMock(AccountManagementInterface::class); + $this->inlineTranslation = $this->createMock(StateInterface::class); + $this->resource = $this->createPartialMock( + \Magento\Newsletter\Model\ResourceModel\Subscriber::class, + [ 'loadByEmail', 'getIdFieldName', 'save', - 'loadByCustomerData', - 'received' - ]); - $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + 'loadByCustomer', + 'received', + 'loadBySubscriberEmail', + 'loadByCustomerId', + ] + ); + $this->objectManager = new ObjectManager($this); - $this->customerFactory = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterfaceFactory::class) + $this->customerFactory = $this->getMockBuilder(CustomerInterfaceFactory::class) ->setMethods(['create']) ->disableOriginalConstructor() ->getMock(); - $this->dataObjectHelper = $this->getMockBuilder(\Magento\Framework\Api\DataObjectHelper::class) + $this->dataObjectHelper = $this->getMockBuilder(DataObjectHelper::class) ->disableOriginalConstructor() ->getMock(); $this->subscriber = $this->objectManager->getObject( - \Magento\Newsletter\Model\Subscriber::class, + Subscriber::class, [ 'newsletterData' => $this->newsletterData, 'scopeConfig' => $this->scopeConfig, @@ -128,250 +162,78 @@ protected function setUp() 'inlineTranslation' => $this->inlineTranslation, 'resource' => $this->resource, 'customerFactory' => $this->customerFactory, - 'dataObjectHelper' => $this->dataObjectHelper - ] - ); - } - - public function testSubscribe() - { - $email = 'subscriber_email@magento.com'; - $storeId = 1; - $customerData = ['store_id' => $storeId, 'email' => $email]; - $storeModel = $this->getMockBuilder(\Magento\Store\Model\Store::class) - ->disableOriginalConstructor() - ->getMock(); - $this->storeManager->expects($this->any())->method('getStore')->willReturn($storeModel); - $storeModel->expects($this->any())->method('getId')->willReturn($storeId); - $customer = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); - $this->customerFactory->expects($this->once())->method('create')->willReturn($customer); - $this->dataObjectHelper->expects($this->once())->method('populateWithArray')->with( - $customer, - $customerData, - \Magento\Customer\Api\Data\CustomerInterface::class - ); - $this->resource->expects($this->any())->method('loadByCustomerData')->with($customer)->willReturn( - [ - 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED, - 'subscriber_email' => $email, - 'name' => 'subscriber_name' + 'dataObjectHelper' => $this->dataObjectHelper, ] ); - $this->scopeConfig->expects($this->any())->method('getValue')->willReturn(true); - $this->customerSession->expects($this->any())->method('isLoggedIn')->willReturn(true); - $customerDataModel = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); - $this->customerSession->expects($this->any())->method('getCustomerDataObject')->willReturn($customerDataModel); - $this->customerSession->expects($this->any())->method('getCustomerId')->willReturn(1); - $customerDataModel->expects($this->any())->method('getEmail')->willReturn($email); - $this->customerRepository->expects($this->any())->method('getById')->willReturn($customerDataModel); - $customerDataModel->expects($this->any())->method('getStoreId')->willReturn($storeId); - $customerDataModel->expects($this->any())->method('getId')->willReturn(1); - $this->sendEmailCheck(); - $this->resource->expects($this->atLeastOnce())->method('save')->willReturnSelf(); - - $this->assertEquals(Subscriber::STATUS_NOT_ACTIVE, $this->subscriber->subscribe($email)); } - public function testSubscribeNotLoggedIn() + /** + * Test to Load by subscriber email + * + * @return void + */ + public function testLoadBySubscriberEmail(): void { - $email = 'subscriber_email@magento.com'; - $storeId = 1; - $customerData = ['store_id' => $storeId, 'email' => $email]; - $storeModel = $this->getMockBuilder(\Magento\Store\Model\Store::class) - ->disableOriginalConstructor() - ->getMock(); - $this->storeManager->expects($this->any())->method('getStore')->willReturn($storeModel); - $storeModel->expects($this->any())->method('getId')->willReturn($storeId); - $customer = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); - $this->customerFactory->expects($this->once())->method('create')->willReturn($customer); - $this->dataObjectHelper->expects($this->once())->method('populateWithArray')->with( - $customer, - $customerData, - \Magento\Customer\Api\Data\CustomerInterface::class + $email = 'subscriber_email@example.com'; + $websiteId = 1; + $subscriberData = ['some_filed' => 'value']; + + $this->resource->expects($this->once()) + ->method('loadBySubscriberEmail') + ->with($email, $websiteId) + ->willReturn($subscriberData); + + $this->assertEquals( + $subscriberData, + $this->subscriber->loadBySubscriberEmail($email, $websiteId)->getData() ); - $this->resource->expects($this->any())->method('loadByCustomerData')->with($customer)->willReturn( - [ - 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED, - 'subscriber_email' => $email, - 'name' => 'subscriber_name' - ] - ); - $this->scopeConfig->expects($this->any())->method('getValue')->willReturn(true); - $this->customerSession->expects($this->any())->method('isLoggedIn')->willReturn(false); - $customerDataModel = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); - $this->customerSession->expects($this->any())->method('getCustomerDataObject')->willReturn($customerDataModel); - $this->customerSession->expects($this->any())->method('getCustomerId')->willReturn(1); - $customerDataModel->expects($this->any())->method('getEmail')->willReturn($email); - $this->customerRepository->expects($this->any())->method('getById')->willReturn($customerDataModel); - $customerDataModel->expects($this->any())->method('getStoreId')->willReturn($storeId); - $customerDataModel->expects($this->any())->method('getId')->willReturn(1); - $this->sendEmailCheck(); - $this->resource->expects($this->atLeastOnce())->method('save')->willReturnSelf(); - - $this->assertEquals(Subscriber::STATUS_NOT_ACTIVE, $this->subscriber->subscribe($email)); } - public function testUpdateSubscription() - { - $storeId = 2; - $customerId = 1; - $customerDataMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) - ->getMock(); - $this->customerRepository->expects($this->atLeastOnce()) - ->method('getById') - ->with($customerId)->willReturn($customerDataMock); - $this->resource->expects($this->atLeastOnce()) - ->method('loadByCustomerData') - ->with($customerDataMock) - ->willReturn( - [ - 'subscriber_id' => 1, - 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED - ] - ); - $customerDataMock->expects($this->atLeastOnce())->method('getId')->willReturn('id'); - $this->resource->expects($this->atLeastOnce())->method('save')->willReturnSelf(); - $this->customerAccountManagement->expects($this->once()) - ->method('getConfirmationStatus') - ->with($customerId) - ->willReturn('account_confirmation_required'); - $customerDataMock->expects($this->exactly(2))->method('getStoreId')->willReturn($storeId); - $customerDataMock->expects($this->once())->method('getEmail')->willReturn('email'); - - $storeModel = $this->getMockBuilder(\Magento\Store\Model\Store::class) - ->disableOriginalConstructor() - ->setMethods(['getId']) - ->getMock(); - $this->storeManager->expects($this->any())->method('getStore')->willReturn($storeModel); - - $this->assertEquals($this->subscriber, $this->subscriber->updateSubscription($customerId)); - } - - public function testUnsubscribeCustomerById() + /** + * Test to Load by customer + * + * @return void + */ + public function testLoadByCustomer(): void { - $storeId = 2; $customerId = 1; - $customerDataMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) - ->getMock(); - $this->customerRepository->expects($this->atLeastOnce()) - ->method('getById') - ->with($customerId)->willReturn($customerDataMock); - $this->resource->expects($this->atLeastOnce()) - ->method('loadByCustomerData') - ->with($customerDataMock) - ->willReturn( - [ - 'subscriber_id' => 1, - 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED - ] - ); - $customerDataMock->expects($this->atLeastOnce())->method('getId')->willReturn('id'); - $this->resource->expects($this->atLeastOnce())->method('save')->willReturnSelf(); - $customerDataMock->expects($this->exactly(2))->method('getStoreId')->willReturn($storeId); - $customerDataMock->expects($this->once())->method('getEmail')->willReturn('email'); - $this->sendEmailCheck(); + $websiteId = 1; + $subscriberData = ['some_filed' => 'value']; - $this->subscriber->unsubscribeCustomerById($customerId); - } - - public function testSubscribeCustomerById() - { - $storeId = 2; - $customerId = 1; - $customerDataMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) - ->getMock(); - $this->customerRepository->expects($this->atLeastOnce()) - ->method('getById') - ->with($customerId)->willReturn($customerDataMock); - $this->resource->expects($this->atLeastOnce()) - ->method('loadByCustomerData') - ->with($customerDataMock) - ->willReturn( - [ - 'subscriber_id' => 1, - 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED - ] - ); - $customerDataMock->expects($this->atLeastOnce())->method('getId')->willReturn('id'); - $this->resource->expects($this->atLeastOnce())->method('save')->willReturnSelf(); - $customerDataMock->expects($this->exactly(2))->method('getStoreId')->willReturn($storeId); - $customerDataMock->expects($this->once())->method('getEmail')->willReturn('email'); - $this->sendEmailCheck(); + $this->resource->expects($this->once()) + ->method('loadByCustomerId') + ->with($customerId, $websiteId) + ->willReturn($subscriberData); - $this->subscriber->subscribeCustomerById($customerId); - } - - public function testSubscribeCustomerById1() - { - $storeId = 2; - $customerId = 1; - $customerDataMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) - ->getMock(); - $this->customerRepository->expects($this->atLeastOnce()) - ->method('getById') - ->with($customerId)->willReturn($customerDataMock); - $this->resource->expects($this->atLeastOnce()) - ->method('loadByCustomerData') - ->with($customerDataMock) - ->willReturn( - [ - 'subscriber_id' => 1, - 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED - ] - ); - $customerDataMock->expects($this->atLeastOnce())->method('getId')->willReturn('id'); - $this->resource->expects($this->atLeastOnce())->method('save')->willReturnSelf(); - $customerDataMock->expects($this->exactly(2))->method('getStoreId')->willReturn($storeId); - $customerDataMock->expects($this->once())->method('getEmail')->willReturn('email'); - $this->sendEmailCheck(); - $this->customerAccountManagement->expects($this->once()) - ->method('getConfirmationStatus') - ->willReturn(\Magento\Customer\Api\AccountManagementInterface::ACCOUNT_CONFIRMATION_NOT_REQUIRED); - $this->scopeConfig->expects($this->atLeastOnce())->method('getValue')->with()->willReturn(true); - - $this->subscriber->subscribeCustomerById($customerId); - $this->assertEquals(Subscriber::STATUS_NOT_ACTIVE, $this->subscriber->getStatus()); - } - - public function testSubscribeCustomerByIdAfterConfirmation() - { - $storeId = 2; - $customerId = 1; - $customerDataMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) - ->getMock(); - $this->customerRepository->expects($this->atLeastOnce()) - ->method('getById') - ->with($customerId)->willReturn($customerDataMock); - $this->resource->expects($this->atLeastOnce()) - ->method('loadByCustomerData') - ->with($customerDataMock) - ->willReturn( - [ - 'subscriber_id' => 1, - 'subscriber_status' => Subscriber::STATUS_UNCONFIRMED - ] - ); - $customerDataMock->expects($this->atLeastOnce())->method('getId')->willReturn('id'); - $this->resource->expects($this->atLeastOnce())->method('save')->willReturnSelf(); - $customerDataMock->expects($this->exactly(2))->method('getStoreId')->willReturn($storeId); - $customerDataMock->expects($this->once())->method('getEmail')->willReturn('email'); - $this->sendEmailCheck(); - $this->customerAccountManagement->expects($this->never())->method('getConfirmationStatus'); - $this->scopeConfig->expects($this->atLeastOnce())->method('getValue')->with()->willReturn(true); - - $this->subscriber->updateSubscription($customerId); - $this->assertEquals(Subscriber::STATUS_SUBSCRIBED, $this->subscriber->getStatus()); + $this->assertEquals( + $subscriberData, + $this->subscriber->loadByCustomer($customerId, $websiteId)->getData() + ); } + /** + * Test to unsubscribe customer from newsletters + */ public function testUnsubscribe() { $this->resource->expects($this->once())->method('save')->willReturnSelf(); - $this->sendEmailCheck(); + $subscriberData = [ + 'store_id' => 2, + 'email' => 'subscriber_email@example.com', + 'name' => 'Subscriber Name', + ]; + $this->subscriber->setData($subscriberData); + $this->sendEmailCheck( + Subscriber::XML_PATH_UNSUBSCRIBE_EMAIL_TEMPLATE, + Subscriber::XML_PATH_UNSUBSCRIBE_EMAIL_IDENTITY + ); $this->assertEquals($this->subscriber, $this->subscriber->unsubscribe()); } /** + * Test to try unsubscribe customer from newsletters with wrong confirmation code + * * @expectedException \Magento\Framework\Exception\LocalizedException * @expectedExceptionMessage This is an invalid subscription confirmation code. */ @@ -383,6 +245,9 @@ public function testUnsubscribeException() $this->subscriber->unsubscribe(); } + /** + * Test to get subscriber full name + */ public function testGetSubscriberFullName() { $this->subscriber->setFirstname('John'); @@ -391,6 +256,9 @@ public function testGetSubscriberFullName() $this->assertEquals('John Doe', $this->subscriber->getSubscriberFullName()); } + /** + * Test to confirm customer subscription + */ public function testConfirm() { $code = 111; @@ -400,6 +268,9 @@ public function testConfirm() $this->assertTrue($this->subscriber->confirm($code)); } + /** + * Test to doesn't confirm customer subscription + */ public function testConfirmWrongCode() { $code = 111; @@ -408,9 +279,12 @@ public function testConfirmWrongCode() $this->assertFalse($this->subscriber->confirm($code)); } + /** + * Test to mark receiving subscriber of queue newsletter + */ public function testReceived() { - $queue = $this->getMockBuilder(\Magento\Newsletter\Model\Queue::class) + $queue = $this->getMockBuilder(Queue::class) ->disableOriginalConstructor() ->getMock(); $this->resource->expects($this->once())->method('received')->with($this->subscriber, $queue)->willReturnSelf(); @@ -419,28 +293,103 @@ public function testReceived() } /** - * @return $this + * Test to Sends out confirmation email + * + * @return void */ - protected function sendEmailCheck() + public function testSendConfirmationRequestEmail(): void { - $storeModel = $this->getMockBuilder(\Magento\Store\Model\Store::class) - ->disableOriginalConstructor() - ->setMethods(['getId']) - ->getMock(); - $transport = $this->createMock(\Magento\Framework\Mail\TransportInterface::class); - $this->scopeConfig->expects($this->any())->method('getValue')->willReturn(true); - $this->transportBuilder->expects($this->once())->method('setTemplateIdentifier')->willReturnSelf(); - $this->transportBuilder->expects($this->once())->method('setTemplateOptions')->willReturnSelf(); - $this->transportBuilder->expects($this->once())->method('setTemplateVars')->willReturnSelf(); - $this->transportBuilder->expects($this->once())->method('setFrom')->willReturnSelf(); - $this->transportBuilder->expects($this->once())->method('addTo')->willReturnSelf(); - $this->storeManager->expects($this->any())->method('getStore')->willReturn($storeModel); - $storeModel->expects($this->any())->method('getId')->willReturn(1); - $this->transportBuilder->expects($this->once())->method('getTransport')->willReturn($transport); + $confirmLink = 'confirm link'; + $storeId = 2; + $subscriberData = [ + 'store_id' => $storeId, + 'email' => 'subscriber_email@example.com', + 'name' => 'Subscriber Name', + ]; + $store = $this->createMock(StoreInterface::class); + $this->storeManager->method('getStore')->with($storeId)->willReturn($store); + $this->newsletterData->expects($this->once()) + ->method('getConfirmationUrl') + ->with($this->subscriber) + ->willReturn($confirmLink); + $this->subscriber->setData($subscriberData); + $this->sendEmailCheck( + Subscriber::XML_PATH_CONFIRM_EMAIL_TEMPLATE, + Subscriber::XML_PATH_CONFIRM_EMAIL_IDENTITY, + [ + 'store' => $store, + 'subscriber_data' => [ + 'confirmation_link' => $confirmLink, + ], + ] + ); + $this->assertEquals($this->subscriber, $this->subscriber->sendConfirmationRequestEmail()); + } + + /** + * Test to Sends out success email + * + * @return void + */ + public function testSendConfirmationSuccessEmail(): void + { + $subscriberData = [ + 'store_id' => 2, + 'email' => 'subscriber_email@example.com', + 'name' => 'Subscriber Name', + ]; + $this->subscriber->setData($subscriberData); + $this->sendEmailCheck( + Subscriber::XML_PATH_SUCCESS_EMAIL_TEMPLATE, + Subscriber::XML_PATH_SUCCESS_EMAIL_IDENTITY + ); + $this->assertEquals($this->subscriber, $this->subscriber->sendConfirmationSuccessEmail()); + } + + /** + * Check to send email + * + * @param string $templateConfigPath + * @param string $identityTemplatePath + * @return void + */ + private function sendEmailCheck(string $templateConfigPath, string $identityTemplatePath, array $vars = []): void + { + $template = 'email_template'; + $identity = 'email_identity'; + $vars += ['subscriber' => $this->subscriber]; + + $this->scopeConfig->method('getValue') + ->willReturnMap( + [ + [$templateConfigPath, ScopeInterface::SCOPE_STORE, $this->subscriber->getStoreId(), $template], + [$identityTemplatePath, ScopeInterface::SCOPE_STORE, $this->subscriber->getStoreId(), $identity], + ] + ); + $this->transportBuilder->expects($this->once()) + ->method('setTemplateIdentifier') + ->with($template) + ->willReturnSelf(); + $this->transportBuilder->expects($this->once()) + ->method('setTemplateOptions') + ->with(['area' => Area::AREA_FRONTEND, 'store' => $this->subscriber->getStoreId()]) + ->willReturnSelf(); + $this->transportBuilder->expects($this->once()) + ->method('setTemplateVars') + ->with($vars) + ->willReturnSelf(); + $this->transportBuilder->expects($this->once()) + ->method('setFrom') + ->with($identity) + ->willReturnSelf(); + $this->transportBuilder->expects($this->once()) + ->method('addTo') + ->with($this->subscriber->getEmail(), $this->subscriber->getName()) + ->willReturnSelf(); + $transport = $this->createMock(TransportInterface::class); $transport->expects($this->once())->method('sendMessage')->willReturnSelf(); + $this->transportBuilder->expects($this->once())->method('getTransport')->willReturn($transport); $this->inlineTranslation->expects($this->once())->method('suspend')->willReturnSelf(); $this->inlineTranslation->expects($this->once())->method('resume')->willReturnSelf(); - - return $this; } } diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/SubscriptionManagerTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/SubscriptionManagerTest.php new file mode 100644 index 0000000000000..ecb30f12742c8 --- /dev/null +++ b/app/code/Magento/Newsletter/Test/Unit/Model/SubscriptionManagerTest.php @@ -0,0 +1,651 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Newsletter\Test\Unit\Model; + +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Newsletter\Model\Subscriber; +use Magento\Newsletter\Model\SubscriberFactory; +use Magento\Newsletter\Model\SubscriptionManager; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; + +/** + * Test to update newsletter subscription status + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class SubscriptionManagerTest extends TestCase +{ + /** + * @var SubscriberFactory|MockObject + */ + private $subscriberFactory; + + /** + * @var LoggerInterface|MockObject + */ + private $logger; + + /** + * @var StoreManagerInterface|MockObject + */ + private $storeManager; + + /** + * @var ScopeConfigInterface|MockObject + */ + private $scopeConfig; + + /** + * @var AccountManagementInterface|MockObject + */ + private $customerAccountManagement; + + /** + * @var CustomerRepositoryInterface|MockObject + */ + private $customerRepository; + + /** + * @var SubscriptionManager + */ + private $subscriptionManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->subscriberFactory = $this->createMock(SubscriberFactory::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->storeManager = $this->createMock(StoreManagerInterface::class); + $this->scopeConfig = $this->createMock(ScopeConfigInterface::class); + $this->customerAccountManagement = $this->createMock(AccountManagementInterface::class); + $this->customerRepository = $this->createMock(CustomerRepositoryInterface::class); + + $objectManager = new ObjectManager($this); + $this->subscriptionManager = $objectManager->getObject( + SubscriptionManager::class, + [ + 'subscriberFactory' => $this->subscriberFactory, + 'logger' => $this->logger, + 'storeManager' => $this->storeManager, + 'scopeConfig' => $this->scopeConfig, + 'customerAccountManagement' => $this->customerAccountManagement, + 'customerRepository' => $this->customerRepository, + ] + ); + } + + /** + * Test to Subscribe to newsletters by email + * + * @param array $subscriberData + * @param string $email + * @param int $storeId + * @param bool $isConfirmNeed + * @param array $expectedData + * @dataProvider subscribeDataProvider + */ + public function testSubscribe( + array $subscriberData, + string $email, + int $storeId, + bool $isConfirmNeed, + array $expectedData + ): void { + $websiteId = 1; + $store = $this->createMock(StoreInterface::class); + $store->method('getWebsiteId')->willReturn($websiteId); + $this->storeManager->method('getStore')->with($storeId)->willReturn($store); + /** @var Subscriber|MockObject $subscriber */ + $subscriber = $this->createPartialMock( + Subscriber::class, + [ + 'loadBySubscriberEmail', + 'randomSequence', + 'save', + 'sendConfirmationRequestEmail', + 'sendConfirmationSuccessEmail', + 'sendUnsubscriptionEmail' + ] + ); + $subscriber->expects($this->once()) + ->method('loadBySubscriberEmail') + ->with($email, $websiteId) + ->willReturnSelf(); + $subscriber->setData($subscriberData); + if (empty($subscriberData['id'])) { + $subscriber->method('randomSequence')->willReturn($expectedData['subscriber_confirm_code']); + } + $this->subscriberFactory->method('create')->willReturn($subscriber); + $this->scopeConfig->method('isSetFlag') + ->with(Subscriber::XML_PATH_CONFIRMATION_FLAG, ScopeInterface::SCOPE_STORE, $storeId) + ->willReturn($isConfirmNeed); + + $this->assertEquals( + $subscriber, + $this->subscriptionManager->subscribe($email, $storeId) + ); + $this->assertEquals($subscriber->getData(), $expectedData); + } + + /** + * Subscribe customer data provider + * + * @return array + */ + public function subscribeDataProvider(): array + { + return [ + 'Subscribe new' => [ + 'subscriber_data' => [], + 'email' => 'email@example.com', + 'store_id' => 1, + 'is_confirm_need' => false, + 'expected_data' => [ + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + ], + 'Subscribe new: confirm required' => [ + 'subscriber_data' => [], + 'email' => 'email@example.com', + 'store_id' => 1, + 'is_confirm_need' => true, + 'expected_data' => [ + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_NOT_ACTIVE, + 'subscriber_confirm_code' => '', + ], + ], + 'Subscribe existing' => [ + 'subscriber_data' => [ + 'subscriber_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED, + 'subscriber_confirm_code' => '', + 'customer_id' => 0, + ], + 'email' => 'email@example.com', + 'store_id' => 1, + 'is_confirm_need' => false, + 'expected_data' => [ + 'subscriber_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED, + 'subscriber_confirm_code' => '', + 'customer_id' => 0, + ], + ], + ]; + } + + /** + * Test to Unsubscribe from newsletters by email + */ + public function testUnsubscribe(): void + { + $email = 'email@example.com'; + $storeId = 2; + $websiteId = 1; + $store = $this->createMock(StoreInterface::class); + $store->method('getWebsiteId')->willReturn($websiteId); + $this->storeManager->method('getStore')->with($storeId)->willReturn($store); + $confirmCode = 'confirm code'; + /** @var Subscriber|MockObject $subscriber */ + $subscriber = $this->createPartialMock( + Subscriber::class, + ['loadBySubscriberEmail', 'getId', 'setCheckCode', 'unsubscribe'] + ); + $subscriber->expects($this->once()) + ->method('loadBySubscriberEmail') + ->with($email, $websiteId) + ->willReturnSelf(); + $subscriber->method('getId')->willReturn(1); + $subscriber->expects($this->once())->method('setCheckCode')->with($confirmCode)->willReturnSelf(); + $subscriber->expects($this->once())->method('unsubscribe')->willReturnSelf(); + $this->subscriberFactory->method('create')->willReturn($subscriber); + + $this->assertEquals( + $subscriber, + $this->subscriptionManager->unsubscribe($email, $storeId, $confirmCode) + ); + } + + /** + * Test to Subscribe customer to newsletter + * + * @param array $subscriberData + * @param array $customerData + * @param int $storeId + * @param bool $isConfirmNeed + * @param array $expectedData + * @param bool $needToSendEmail + * @dataProvider subscribeCustomerDataProvider + */ + public function testSubscribeCustomer( + array $subscriberData, + array $customerData, + int $storeId, + bool $isConfirmNeed, + array $expectedData, + bool $needToSendEmail + ): void { + $websiteId = 1; + $customerId = $customerData['id']; + $store = $this->createMock(StoreInterface::class); + $store->method('getWebsiteId')->willReturn($websiteId); + $this->storeManager->method('getStore')->with($storeId)->willReturn($store); + /** @var CustomerInterface|MockObject $customer */ + $customer = $this->createMock(CustomerInterface::class); + $customer->method('getId')->willReturn($customerId); + $customer->method('getEmail')->willReturn($customerData['email']); + $this->customerRepository->method('getById')->with($customerId)->willReturn($customer); + /** @var Subscriber|MockObject $subscriber */ + $subscriber = $this->createPartialMock( + Subscriber::class, + [ + 'loadByCustomer', + 'loadBySubscriberEmail', + 'randomSequence', + 'save', + 'sendConfirmationRequestEmail', + 'sendConfirmationSuccessEmail', + 'sendUnsubscriptionEmail' + ] + ); + $subscriber->expects($this->once()) + ->method('loadByCustomer') + ->with($customerId, $websiteId) + ->willReturnSelf(); + if (empty($subscriberData['subscriber_id'])) { + $subscriber->expects($this->once()) + ->method('loadBySubscriberEmail') + ->with($customerData['email'], $websiteId) + ->willReturnSelf(); + } + $subscriber->setData($subscriberData); + if (empty($subscriberData['subscriber_id'])) { + $subscriber->method('randomSequence')->willReturn($expectedData['subscriber_confirm_code']); + } + $sendEmailMethod = $this->getSendEmailMethod($expectedData['subscriber_status'] ?? 0); + if ($needToSendEmail) { + $subscriber->expects($this->once())->method($sendEmailMethod); + } else { + $subscriber->expects($this->never())->method('sendConfirmationRequestEmail'); + $subscriber->expects($this->never())->method('sendConfirmationSuccessEmail'); + $subscriber->expects($this->never())->method('sendUnsubscriptionEmail'); + } + $this->subscriberFactory->method('create')->willReturn($subscriber); + $this->scopeConfig->method('isSetFlag') + ->with(Subscriber::XML_PATH_CONFIRMATION_FLAG, ScopeInterface::SCOPE_STORE, $storeId) + ->willReturn($isConfirmNeed); + $this->customerAccountManagement + ->method('getConfirmationStatus') + ->willReturn($customerData['confirmation_status']); + + $this->assertEquals( + $subscriber, + $this->subscriptionManager->subscribeCustomer($customerId, $storeId) + ); + $this->assertEquals($subscriber->getData(), $expectedData); + } + + /** + * Get expected send email method + * + * @param int $status + * @return string + */ + private function getSendEmailMethod(int $status): string + { + switch ($status) { + case Subscriber::STATUS_SUBSCRIBED: + $sendEmailMethod = 'sendConfirmationSuccessEmail'; + break; + case Subscriber::STATUS_NOT_ACTIVE: + $sendEmailMethod = 'sendConfirmationRequestEmail'; + break; + case Subscriber::STATUS_UNSUBSCRIBED: + $sendEmailMethod = 'sendUnsubscriptionEmail'; + break; + default: + $sendEmailMethod = ''; + } + + return $sendEmailMethod; + } + + /** + * Subscribe customer data provider + * + * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function subscribeCustomerDataProvider(): array + { + return [ + 'Subscribe new' => [ + 'subscriber_data' => [], + 'customer_data' => [ + 'id' => 1, + 'email' => 'email@example.com', + 'confirmation_status' => AccountManagementInterface::ACCOUNT_CONFIRMED, + ], + 'store_id' => 1, + 'is_confirm_need' => false, + 'expected_data' => [ + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + 'needToSendEmail' => true, + ], + 'Subscribe new: customer confirm required' => [ + 'subscriber_data' => [], + 'customer_data' => [ + 'id' => 1, + 'email' => 'email@example.com', + 'confirmation_status' => AccountManagementInterface::ACCOUNT_CONFIRMATION_REQUIRED, + ], + 'store_id' => 1, + 'is_confirm_need' => false, + 'expected_data' => [ + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_UNCONFIRMED, + 'subscriber_confirm_code' => '', + ], + 'needToSendEmail' => false, + ], + 'Subscribe existing' => [ + 'subscriber_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + 'customer_data' => [ + 'id' => 1, + 'email' => 'email@example.com', + 'confirmation_status' => AccountManagementInterface::ACCOUNT_CONFIRMED, + ], + 'store_id' => 1, + 'is_confirm_need' => false, + 'expected_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + 'needToSendEmail' => true, + ], + 'Subscribe existing: subscription confirm required' => [ + 'subscriber_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + 'customer_data' => [ + 'id' => 1, + 'email' => 'email@example.com', + 'confirmation_status' => AccountManagementInterface::ACCOUNT_CONFIRMED, + ], + 'store_id' => 1, + 'is_confirm_need' => true, + 'expected_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_NOT_ACTIVE, + 'subscriber_confirm_code' => '', + ], + 'needToSendEmail' => true, + ], + 'Update subscription data' => [ + 'subscriber_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + 'customer_data' => [ + 'id' => 1, + 'email' => 'email2@example.com', + 'confirmation_status' => AccountManagementInterface::ACCOUNT_CONFIRMED, + ], + 'store_id' => 2, + 'is_confirm_need' => false, + 'expected_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email2@example.com', + 'store_id' => 2, + 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + 'needToSendEmail' => false, + ], + 'Update subscription data: subscription confirm required ' => [ + 'subscriber_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_NOT_ACTIVE, + 'subscriber_confirm_code' => '', + ], + 'customer_data' => [ + 'id' => 1, + 'email' => 'email2@example.com', + 'confirmation_status' => AccountManagementInterface::ACCOUNT_CONFIRMED, + ], + 'store_id' => 2, + 'is_confirm_need' => true, + 'expected_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email2@example.com', + 'store_id' => 2, + 'subscriber_status' => Subscriber::STATUS_NOT_ACTIVE, + 'subscriber_confirm_code' => '', + ], + 'needToSendEmail' => true, + ], + ]; + } + + /** + * Test to Unsubscribe customer from newsletter + * + * @param array $subscriberData + * @param array $customerData + * @param int $storeId + * @param array $expectedData + * @param bool $needToSendEmail + * @dataProvider unsubscribeCustomerDataProvider + */ + public function testUnsubscribeCustomer( + array $subscriberData, + array $customerData, + int $storeId, + array $expectedData, + bool $needToSendEmail + ): void { + $websiteId = 1; + $customerId = $customerData['id']; + $store = $this->createMock(StoreInterface::class); + $store->method('getWebsiteId')->willReturn($websiteId); + $this->storeManager->method('getStore')->with($storeId)->willReturn($store); + /** @var CustomerInterface|MockObject $customer */ + $customer = $this->createMock(CustomerInterface::class); + $customer->method('getId')->willReturn($customerId); + $customer->method('getEmail')->willReturn($customerData['email']); + $this->customerRepository->method('getById')->with($customerId)->willReturn($customer); + /** @var Subscriber|MockObject $subscriber */ + $subscriber = $this->createPartialMock( + Subscriber::class, + [ + 'loadByCustomer', + 'loadBySubscriberEmail', + 'randomSequence', + 'save', + 'sendConfirmationRequestEmail', + 'sendConfirmationSuccessEmail', + 'sendUnsubscriptionEmail' + ] + ); + $subscriber->expects($this->once()) + ->method('loadByCustomer') + ->with($customerId, $websiteId) + ->willReturnSelf(); + if (empty($subscriberData['subscriber_id'])) { + $subscriber->expects($this->once()) + ->method('loadBySubscriberEmail') + ->with($customerData['email'], $websiteId) + ->willReturnSelf(); + } + $subscriber->setData($subscriberData); + $sendEmailMethod = $this->getSendEmailMethod($expectedData['subscriber_status'] ?? 0); + if ($needToSendEmail) { + $subscriber->expects($this->once())->method($sendEmailMethod); + } else { + $subscriber->expects($this->never())->method('sendConfirmationRequestEmail'); + $subscriber->expects($this->never())->method('sendConfirmationSuccessEmail'); + $subscriber->expects($this->never())->method('sendUnsubscriptionEmail'); + } + $this->subscriberFactory->method('create')->willReturn($subscriber); + + $this->assertEquals( + $subscriber, + $this->subscriptionManager->unsubscribeCustomer($customerId, $storeId) + ); + $this->assertEquals($subscriber->getData(), $expectedData); + } + + /** + * Unsubscribe customer data provider + * + * @return array + */ + public function unsubscribeCustomerDataProvider(): array + { + return [ + 'Unsubscribe new' => [ + 'subscriber_data' => [], + 'customer_data' => [ + 'id' => 1, + 'email' => 'email@example.com', + ], + 'store_id' => 1, + 'expected_data' => [ + ], + 'needToSendEmail' => false, + ], + 'Unsubscribe existing' => [ + 'subscriber_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_SUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + 'customer_data' => [ + 'id' => 1, + 'email' => 'email@example.com', + ], + 'store_id' => 1, + 'expected_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + 'needToSendEmail' => true, + ], + 'Unsubscribe existing: subscription confirm required' => [ + 'subscriber_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_NOT_ACTIVE, + 'subscriber_confirm_code' => '', + ], + 'customer_data' => [ + 'id' => 1, + 'email' => 'email@example.com', + ], + 'store_id' => 1, + 'expected_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_NOT_ACTIVE, + 'subscriber_confirm_code' => '', + ], + 'needToSendEmail' => false, + ], + 'Update subscription data' => [ + 'subscriber_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email@example.com', + 'store_id' => 1, + 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + 'customer_data' => [ + 'id' => 1, + 'email' => 'email2@example.com', + ], + 'store_id' => 2, + 'expected_data' => [ + 'subscriber_id' => 1, + 'customer_id' => 1, + 'subscriber_email' => 'email2@example.com', + 'store_id' => 2, + 'subscriber_status' => Subscriber::STATUS_UNSUBSCRIBED, + 'subscriber_confirm_code' => '', + ], + 'needToSendEmail' => false, + ], + ]; + } +} diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/TemplateTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/TemplateTest.php index 52bb803dd377f..b53ac39f0e4e2 100644 --- a/app/code/Magento/Newsletter/Test/Unit/Model/TemplateTest.php +++ b/app/code/Magento/Newsletter/Test/Unit/Model/TemplateTest.php @@ -197,20 +197,20 @@ protected function getModelMock(array $mockedMethods = []) public function testGetProcessedTemplateSubject() { - $model = $this->getModelMock([ - 'getTemplateFilter', - 'getDesignConfig', - 'applyDesignConfig', - 'setVariables', - ]); + $model = $this->getModelMock( + [ + 'getTemplateFilter', + 'getDesignConfig', + 'applyDesignConfig', + 'setVariables', + ] + ); $templateSubject = 'templateSubject'; $model->setTemplateSubject($templateSubject); + $model->setTemplateId('foobar'); - $filterTemplate = $this->getMockBuilder(\Magento\Framework\Filter\Template::class) - ->setMethods(['setVariables', 'setStoreId', 'filter']) - ->disableOriginalConstructor() - ->getMock(); + $filterTemplate = $this->createMock(\Magento\Framework\Filter\Template::class); $model->expects($this->once()) ->method('getTemplateFilter') ->will($this->returnValue($filterTemplate)); @@ -221,6 +221,11 @@ public function testGetProcessedTemplateSubject() ->with($templateSubject) ->will($this->returnValue($expectedResult)); + $filterTemplate->expects($this->exactly(2)) + ->method('setStrictMode') + ->withConsecutive([$this->equalTo(false)], [$this->equalTo(true)]) + ->willReturnOnConsecutiveCalls(true, false); + $variables = ['key' => 'value']; $filterTemplate->expects($this->once()) ->method('setVariables') @@ -245,21 +250,24 @@ public function testGetProcessedTemplateSubject() */ public function testGetProcessedTemplate($variables, $templateType, $storeId, $expectedVariables, $expectedResult) { + class_exists(\Magento\Newsletter\Model\Template\Filter::class, true); $filterTemplate = $this->getMockBuilder(\Magento\Newsletter\Model\Template\Filter::class) - ->setMethods([ - 'setUseSessionInUrl', - 'setPlainTemplateMode', - 'setIsChildTemplate', - 'setDesignParams', - 'setVariables', - 'setStoreId', - 'filter', - 'getStoreId', - 'getInlineCssFiles', - ]) + ->setMethods( + [ + 'setUseSessionInUrl', + 'setPlainTemplateMode', + 'setIsChildTemplate', + 'setDesignParams', + 'setVariables', + 'setStoreId', + 'filter', + 'getStoreId', + 'getInlineCssFiles', + 'setStrictMode', + ] + ) ->disableOriginalConstructor() ->getMock(); - $filterTemplate->expects($this->once()) ->method('setUseSessionInUrl') ->with(false) @@ -281,12 +289,15 @@ public function testGetProcessedTemplate($variables, $templateType, $storeId, $e ->method('getStoreId') ->will($this->returnValue($storeId)); + $filterTemplate->expects($this->exactly(2)) + ->method('setStrictMode') + ->withConsecutive([$this->equalTo(true)], [$this->equalTo(false)]) + ->willReturnOnConsecutiveCalls(false, true); + // The following block of code tests to ensure that the store id of the subscriber will be used, if the // 'subscriber' variable is set. $subscriber = $this->getMockBuilder(\Magento\Newsletter\Model\Subscriber::class) - ->setMethods([ - 'getStoreId', - ]) + ->setMethods(['getStoreId']) ->disableOriginalConstructor() ->getMock(); $subscriber->expects($this->once()) @@ -296,18 +307,20 @@ public function testGetProcessedTemplate($variables, $templateType, $storeId, $e $variables['subscriber'] = $subscriber; $expectedVariables['store'] = $this->store; - - $model = $this->getModelMock([ - 'getDesignParams', - 'applyDesignConfig', - 'getTemplateText', - 'isPlain', - ]); + $model = $this->getModelMock( + [ + 'getDesignParams', + 'applyDesignConfig', + 'getTemplateText', + 'isPlain', + ] + ); $filterTemplate->expects($this->any()) ->method('setVariables') ->with(array_merge(['this' => $model], $expectedVariables)); $model->setTemplateFilter($filterTemplate); $model->setTemplateType($templateType); + $model->setTemplateId('123'); $designParams = [ 'area' => \Magento\Framework\App\Area::AREA_FRONTEND, diff --git a/app/code/Magento/Newsletter/etc/db_schema.xml b/app/code/Magento/Newsletter/etc/db_schema.xml index 257416d0bc465..c038b02404875 100644 --- a/app/code/Magento/Newsletter/etc/db_schema.xml +++ b/app/code/Magento/Newsletter/etc/db_schema.xml @@ -54,6 +54,8 @@ default="1" comment="Template Actual"/> <column xsi:type="timestamp" name="added_at" on_update="false" nullable="true" comment="Added At"/> <column xsi:type="timestamp" name="modified_at" on_update="false" nullable="true" comment="Modified At"/> + <column xsi:type="boolean" name="is_legacy" nullable="false" + default="false" comment="Should the template render in legacy mode"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="template_id"/> </constraint> diff --git a/app/code/Magento/Newsletter/etc/di.xml b/app/code/Magento/Newsletter/etc/di.xml index 179ec19cccfc5..3c35936a2e8aa 100644 --- a/app/code/Magento/Newsletter/etc/di.xml +++ b/app/code/Magento/Newsletter/etc/di.xml @@ -25,4 +25,11 @@ <plugin name="update_newsletter_subscription_on_customer_update" type="Magento\Newsletter\Model\Plugin\CustomerPlugin"/> </type> + <type name="Magento\Newsletter\Model\Subscriber"> + <arguments> + <argument name="customerSession" xsi:type="object">Magento\Customer\Model\Session\Proxy</argument> + </arguments> + </type> + <preference for="Magento\Newsletter\Model\SubscriptionManagerInterface" + type="Magento\Newsletter\Model\SubscriptionManager"/> </config> diff --git a/app/code/Magento/Newsletter/registration.php b/app/code/Magento/Newsletter/registration.php index 421b594ad6b88..b1a5714ff8a4d 100644 --- a/app/code/Magento/Newsletter/registration.php +++ b/app/code/Magento/Newsletter/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Newsletter', __DIR__); diff --git a/app/code/Magento/Newsletter/view/adminhtml/templates/template/edit.phtml b/app/code/Magento/Newsletter/view/adminhtml/templates/template/edit.phtml index c49a5c61a7172..abc56070b6892 100644 --- a/app/code/Magento/Newsletter/view/adminhtml/templates/template/edit.phtml +++ b/app/code/Magento/Newsletter/view/adminhtml/templates/template/edit.phtml @@ -17,12 +17,13 @@ use Magento\Framework\App\TemplateTypesInterface; </div> <?= /* @noEscape */ $block->getForm() ?> </form> -<form action="<?= $block->escapeUrl($block->getPreviewUrl()) ?>" method="get" id="newsletter_template_preview_form" target="_blank"> +<form action="<?= $block->escapeUrl($block->getPreviewUrl()) ?>" method="post" id="newsletter_template_preview_form" target="_blank"> <div class="no-display"> <input type="hidden" id="preview_type" name="type" value="<?= /* @noEscape */ $block->isTextType() ? 1 : 2 ?>" /> <input type="hidden" id="preview_text" name="text" value="" /> <input type="hidden" id="preview_styles" name="styles" value="" /> <input type="hidden" id="preview_id" name="id" value="" /> + <input type="hidden" name="form_key" value="<?= $block->escapeHtmlAttr($block->getFormKey()) ?>" > </div> </form> <script> diff --git a/app/code/Magento/Newsletter/view/frontend/email/subscr_confirm.html b/app/code/Magento/Newsletter/view/frontend/email/subscr_confirm.html index eaf760c080370..beeda47d9d738 100644 --- a/app/code/Magento/Newsletter/view/frontend/email/subscr_confirm.html +++ b/app/code/Magento/Newsletter/view/frontend/email/subscr_confirm.html @@ -6,14 +6,13 @@ --> <!--@subject {{trans "Newsletter subscription confirmation"}} @--> <!--@vars { -"var customer.name":"Customer Name", -"var subscriber.getConfirmationLink()":"Subscriber Confirmation URL" +"var subscriber_data.confirmation_link":"Subscriber Confirmation URL" } @--> {{template config_path="design/email/header_template"}} <p class="greeting">{{trans "Thank you for subscribing to our newsletter."}}</p> <p>{{trans "To begin receiving the newsletter, you must first confirm your subscription by clicking on the following link:"}}</p> -<p><a href="{{var subscriber.getConfirmationLink()}}">{{var subscriber.getConfirmationLink()}}</a></p> +<p><a href="{{var subscriber_data.confirmation_link}}">{{var subscriber_data.confirmation_link}}</a></p> {{template config_path="design/email/footer_template"}} diff --git a/app/code/Magento/OfflinePayments/Observer/BeforeOrderPaymentSaveObserver.php b/app/code/Magento/OfflinePayments/Observer/BeforeOrderPaymentSaveObserver.php index 16ef7ecdf5011..daf1cef3fff60 100644 --- a/app/code/Magento/OfflinePayments/Observer/BeforeOrderPaymentSaveObserver.php +++ b/app/code/Magento/OfflinePayments/Observer/BeforeOrderPaymentSaveObserver.php @@ -3,7 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); namespace Magento\OfflinePayments\Observer; @@ -11,7 +10,6 @@ use Magento\OfflinePayments\Model\Banktransfer; use Magento\OfflinePayments\Model\Cashondelivery; use Magento\OfflinePayments\Model\Checkmo; -use Magento\Sales\Model\Order\Payment; /** * Sets payment additional information. @@ -19,22 +17,24 @@ class BeforeOrderPaymentSaveObserver implements ObserverInterface { /** - * Sets current instructions for bank transfer account. + * Sets current instructions for bank transfer account * * @param \Magento\Framework\Event\Observer $observer * @return void - * @throws \Magento\Framework\Exception\LocalizedException */ - public function execute(\Magento\Framework\Event\Observer $observer): void + public function execute(\Magento\Framework\Event\Observer $observer) { - /** @var Payment $payment */ + /** @var \Magento\Sales\Model\Order\Payment $payment */ $payment = $observer->getEvent()->getPayment(); $instructionMethods = [ Banktransfer::PAYMENT_METHOD_BANKTRANSFER_CODE, Cashondelivery::PAYMENT_METHOD_CASHONDELIVERY_CODE ]; if (in_array($payment->getMethod(), $instructionMethods)) { - $payment->setAdditionalInformation('instructions', $this->getInstructions($payment)); + $payment->setAdditionalInformation( + 'instructions', + $payment->getMethodInstance()->getInstructions() + ); } elseif ($payment->getMethod() === Checkmo::PAYMENT_METHOD_CHECKMO_CODE) { $methodInstance = $payment->getMethodInstance(); if (!empty($methodInstance->getPayableTo())) { @@ -45,17 +45,4 @@ public function execute(\Magento\Framework\Event\Observer $observer): void } } } - - /** - * Retrieve store-specific payment method instructions, or already saved if exists. - * - * @param Payment $payment - * @return string|null - * @throws \Magento\Framework\Exception\LocalizedException - */ - private function getInstructions(Payment $payment): ?string - { - return $payment->getAdditionalInformation('instructions') - ?: $payment->getMethodInstance()->getConfigData('instructions', $payment->getOrder()->getStoreId()); - } } diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Observer/BeforeOrderPaymentSaveObserverTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Observer/BeforeOrderPaymentSaveObserverTest.php index 57c5ec533dc64..51edaea0e659c 100644 --- a/app/code/Magento/OfflinePayments/Test/Unit/Observer/BeforeOrderPaymentSaveObserverTest.php +++ b/app/code/Magento/OfflinePayments/Test/Unit/Observer/BeforeOrderPaymentSaveObserverTest.php @@ -11,7 +11,6 @@ use Magento\OfflinePayments\Model\Banktransfer; use Magento\OfflinePayments\Model\Cashondelivery; use Magento\OfflinePayments\Observer\BeforeOrderPaymentSaveObserver; -use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Payment; use PHPUnit_Framework_MockObject_MockObject as MockObject; use Magento\OfflinePayments\Model\Checkmo; @@ -77,12 +76,19 @@ public function testBeforeOrderPaymentSaveWithInstructions($methodCode) $this->payment->expects(self::once()) ->method('getMethod') ->willReturn($methodCode); - $this->payment->method('getAdditionalInformation') - ->with('instructions') - ->willReturn('payment configuration'); $this->payment->expects(self::once()) ->method('setAdditionalInformation') ->with('instructions', 'payment configuration'); + $method = $this->getMockBuilder(Banktransfer::class) + ->disableOriginalConstructor() + ->getMock(); + + $method->expects(self::once()) + ->method('getInstructions') + ->willReturn('payment configuration'); + $this->payment->expects(self::once()) + ->method('getMethodInstance') + ->willReturn($method); $this->_model->execute($this->observer); } diff --git a/app/code/Magento/OfflinePayments/composer.json b/app/code/Magento/OfflinePayments/composer.json index 7bf4b78628a70..4de112ac72152 100644 --- a/app/code/Magento/OfflinePayments/composer.json +++ b/app/code/Magento/OfflinePayments/composer.json @@ -8,8 +8,7 @@ "php": "~7.1.3||~7.2.0||~7.3.0", "magento/framework": "*", "magento/module-checkout": "*", - "magento/module-payment": "*", - "magento/module-sales": "*" + "magento/module-payment": "*" }, "suggest": { "magento/module-config": "*" diff --git a/app/code/Magento/OfflinePayments/registration.php b/app/code/Magento/OfflinePayments/registration.php index 4b5473194f1e4..f3d1fd1488c76 100644 --- a/app/code/Magento/OfflinePayments/registration.php +++ b/app/code/Magento/OfflinePayments/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_OfflinePayments', __DIR__); diff --git a/app/code/Magento/OfflineShipping/Model/Carrier/Freeshipping.php b/app/code/Magento/OfflineShipping/Model/Carrier/Freeshipping.php index 674e6b8089787..a1fca2b155f11 100644 --- a/app/code/Magento/OfflineShipping/Model/Carrier/Freeshipping.php +++ b/app/code/Magento/OfflineShipping/Model/Carrier/Freeshipping.php @@ -63,6 +63,24 @@ public function __construct( parent::__construct($scopeConfig, $rateErrorFactory, $logger, $data); } + /** + * Check subtotal for allowed free shipping + * + * @param RateRequest $request + * + * @return bool + */ + private function isFreeShippingRequired(RateRequest $request): bool + { + $minSubtotal = $request->getPackageValueWithDiscount(); + if ($request->getBaseSubtotalWithDiscountInclTax() + && $this->getConfigFlag('tax_including')) { + $minSubtotal = $request->getBaseSubtotalWithDiscountInclTax(); + } + + return $minSubtotal >= $this->getConfigData('free_shipping_subtotal'); + } + /** * FreeShipping Rates Collector * @@ -80,10 +98,7 @@ public function collectRates(RateRequest $request) $this->_updateFreeMethodQuote($request); - if ($request->getFreeShipping() || $request->getPackageValueWithDiscount() >= $this->getConfigData( - 'free_shipping_subtotal' - ) - ) { + if ($request->getFreeShipping() || $this->isFreeShippingRequired($request)) { /** @var \Magento\Quote\Model\Quote\Address\RateResult\Method $method */ $method = $this->_rateMethodFactory->create(); diff --git a/app/code/Magento/OfflineShipping/Test/Mftf/Data/MultishippingConfigData.xml b/app/code/Magento/OfflineShipping/Test/Mftf/Data/MultishippingConfigData.xml new file mode 100644 index 0000000000000..569eddf336782 --- /dev/null +++ b/app/code/Magento/OfflineShipping/Test/Mftf/Data/MultishippingConfigData.xml @@ -0,0 +1,41 @@ +<?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="EnableFreeShippingMethod"> + <data key="path">carriers/freeshipping/active</data> + <data key="label">Yes</data> + <data key="value">1</data> + </entity> + <entity name="DisableFreeShippingMethod"> + <data key="path">carriers/freeshipping/active</data> + <data key="label">No</data> + <data key="value">0</data> + </entity> + <entity name="EnableFlatRateShippingMethod"> + <data key="path">carriers/flatrate/active</data> + <data key="label">Yes</data> + <data key="value">1</data> + </entity> + <entity name="DisableFlatRateShippingMethod"> + <data key="path">carriers/flatrate/active</data> + <data key="label">No</data> + <data key="value">0</data> + </entity> + <entity name="EnableMultiShippingCheckoutMultiple"> + <data key="path">multishipping/options/checkout_multiple</data> + <data key="label">Yes</data> + <data key="value">1</data> + </entity> + <entity name="DisableMultiShippingCheckoutMultiple"> + <data key="path">multishipping/options/checkout_multiple</data> + <data key="label">No</data> + <data key="value">0</data> + </entity> +</entities> diff --git a/app/code/Magento/OfflineShipping/Test/Mftf/Test/StorefrontFreeShippingDisplayWithInclTaxOptionTest.xml b/app/code/Magento/OfflineShipping/Test/Mftf/Test/StorefrontFreeShippingDisplayWithInclTaxOptionTest.xml new file mode 100644 index 0000000000000..d2e092283e9bd --- /dev/null +++ b/app/code/Magento/OfflineShipping/Test/Mftf/Test/StorefrontFreeShippingDisplayWithInclTaxOptionTest.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="StorefrontFreeShippingDisplayWithInclTaxOptionTest"> + <annotations> + <features value="Shipping"/> + <stories value="Offline Shipping Methods"/> + <title value="Free Shipping Minimum Order Amount Excluding/Including Tax options"/> + <description value="Free Shipping Minimum Order Amount Excluding/Including Tax options"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-20613"/> + <useCaseId value="MC-18457"/> + <group value="shipping"/> + </annotations> + <before> + <createData entity="SimpleProduct2" stepKey="createSimpleProduct"> + <field key="price">100.00</field> + </createData> + <!-- Enable free shipping method --> + <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShippingMethod"/> + <createData entity="setFreeShippingSubtotal" stepKey="setFreeShippingSubtotal"/> + <createData entity="SetTaxIncluding" stepKey="setTaxIncluding"/> + <!-- Tax configuration (Store>Configuration; Sales>Tax) --> + <createData entity="Tax_Config_CA" stepKey="configureTaxForCA"/> + <createData entity="defaultTaxRule" stepKey="createTaxRule"/> + </before> + <after> + <!-- Disable free shipping method --> + <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShippingMethod"/> + <createData entity="setFreeShippingSubtotalToDefault" stepKey="setFreeShippingSubtotalToDefault"/> + <createData entity="SetTaxIncludingToDefault" stepKey="setTaxIncludingToDefault"/> + <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> + <createData entity="DefaultTaxConfig" stepKey="resetTaxConfiguration"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + </after> + <!-- Add simple product to cart --> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <!-- Assert that taxes are applied correctly for CA --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForCart"/> + <waitForElementVisible selector="{{CheckoutPaymentSection.tax}}" stepKey="waitForOverviewVisible"/> + <waitForElement time="30" selector="{{CheckoutCartSummarySection.estimateShippingAndTaxForm}}" stepKey="waitForEstimateShippingAndTaxForm"/> + <waitForElement time="30" selector="{{CheckoutCartSummarySection.shippingMethodForm}}" stepKey="waitForShippingMethodForm"/> + <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> + <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUSCountry"/> + <waitForPageLoad stepKey="waitForSelectCountry"/> + <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="California" stepKey="selectCaliforniaRegion"/> + <waitForPageLoad stepKey="waitForSelectRegion"/> + <see selector="{{CheckoutPaymentSection.tax}}" userInput="$8.25" stepKey="seeTaxForCA"/> + <!-- See available Free Shipping option --> + <actionGroup ref="StorefrontAssertShippingMethodPresentInCartActionGroup" stepKey="assertShippingMethodLabel"> + <argument name="shippingMethod" value="{{freeTitleDefault.value}}"/> + </actionGroup> + <!-- Change State to New York --> + <selectOption selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="{{US_Address_NY.state}}" stepKey="selectAnotherState"/> + <waitForPageLoad stepKey="waitForShippingMethodLoad"/> + <dontSee selector="{{CheckoutCartSummarySection.shippingMethodLabel}}" userInput="{{freeTitleDefault.value}}" stepKey="assertShippingMethodIsNotPresentInCart"/> + </test> +</tests> diff --git a/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/FreeshippingTest.php b/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/FreeshippingTest.php new file mode 100644 index 0000000000000..7f8959e610d42 --- /dev/null +++ b/app/code/Magento/OfflineShipping/Test/Unit/Model/Carrier/FreeshippingTest.php @@ -0,0 +1,200 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\OfflineShipping\Test\Unit\Model\Carrier; + +use Magento\Quote\Model\Quote\Address\RateResult\Method; +use Magento\Shipping\Model\Rate\Result; +use Magento\OfflineShipping\Model\Carrier\Freeshipping; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Quote\Model\Quote\Address\RateResult\MethodFactory; +use Magento\Shipping\Model\Rate\ResultFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Quote\Model\Quote\Address\RateRequest; +use Magento\Store\Model\ScopeInterface; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; +use Magento\Quote\Model\Quote\Item as QuoteItem; +use PHPUnit\Framework\MockObject\Matcher\InvokedCount; + +/** + * Class for test free shipping + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class FreeshippingTest extends TestCase +{ + /** + * @var Freeshipping + */ + private $model; + + /** + * @var ScopeConfigInterface|MockObject + */ + private $scopeConfigMock; + + /** + * @var ResultFactory|MockObject + */ + private $resultFactoryMock; + + /** + * @var MethodFactory|MockObject + */ + private $methodFactoryMock; + + /** + * @var ObjectManager + */ + private $helper; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->scopeConfigMock = $this->getMockBuilder(\Magento\Framework\App\Config::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->resultFactoryMock = $this->getMockBuilder(ResultFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->methodFactoryMock = $this + ->getMockBuilder(MethodFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->helper = new ObjectManager($this); + $this->model = $this->helper->getObject( + Freeshipping::class, + [ + 'scopeConfig' => $this->scopeConfigMock, + '_rateResultFactory' => $this->resultFactoryMock, + '_rateMethodFactory' => $this->methodFactoryMock, + ] + ); + } + + /** + * Test for collect rate free shipping with tax options + * + * @param int $subtotalInclTax + * @param int $minOrderAmount + * @param int $packageValueWithDiscount + * @param int $baseSubtotalWithDiscountInclTax + * @param InvokedCount $expectedCallAppend + * + * @return void + * @dataProvider freeShippingWithSubtotalTaxDataProvider + */ + public function testCollectRatesFreeShippingWithTaxOptions( + int $subtotalInclTax, + int $minOrderAmount, + int $packageValueWithDiscount, + int $baseSubtotalWithDiscountInclTax, + InvokedCount $expectedCallAppend + ): void { + /** @var RateRequest|MockObject $request */ + $request = $this->getMockBuilder(RateRequest::class) + ->disableOriginalConstructor() + ->setMethods( + [ + 'getAllItems', + 'getPackageQty', + 'getFreeShipping', + 'getBaseSubtotalWithDiscountInclTax', + 'getPackageValueWithDiscount', + ] + ) + ->getMock(); + $item = $this->getMockBuilder(QuoteItem::class) + ->disableOriginalConstructor() + ->getMock(); + $this->scopeConfigMock->expects($this->at(0)) + ->method('isSetFlag') + ->willReturn(true); + $this->scopeConfigMock->expects($this->at(1)) + ->method('isSetFlag') + ->with( + 'carriers/freeshipping/tax_including', + ScopeInterface::SCOPE_STORE, + null + ) + ->willReturn($subtotalInclTax); + $this->scopeConfigMock->expects($this->at(2)) + ->method('getValue') + ->with( + 'carriers/freeshipping/free_shipping_subtotal', + ScopeInterface::SCOPE_STORE, + null + ) + ->willReturn($minOrderAmount); + $method = $this->getMockBuilder(Method::class) + ->disableOriginalConstructor() + ->setMethods(['setCarrier', 'setCarrierTitle', 'setMethod', 'setMethodTitle', 'setPrice', 'setCost']) + ->getMock(); + $resultModel = $this->getMockBuilder(Result::class) + ->disableOriginalConstructor() + ->setMethods(['append']) + ->getMock(); + $this->resultFactoryMock->method('create') + ->willReturn($resultModel); + $request->method('getPackageValueWithDiscount') + ->willReturn($packageValueWithDiscount); + $request->method('getAllItems') + ->willReturn([$item]); + $request->method('getFreeShipping') + ->willReturn(false); + $request->method('getBaseSubtotalWithDiscountInclTax') + ->willReturn($baseSubtotalWithDiscountInclTax); + $this->methodFactoryMock->method('create')->willReturn($method); + + $resultModel->expects($expectedCallAppend) + ->method('append') + ->with($method); + + $this->model->collectRates($request); + } + + /** + * @return array + */ + public function freeShippingWithSubtotalTaxDataProvider(): array + { + return [ + [ + 'subtotalInclTax' => 1, + 'minOrderAmount' => 10, + 'packageValueWithDiscount' => 8, + 'baseSubtotalWithDiscountInclTax' => 15, + 'expectedCallAppend' => $this->once(), + + ], + [ + 'subtotalInclTax' => 1, + 'minOrderAmount' => 20, + 'packageValueWithDiscount' => 8, + 'baseSubtotalWithDiscountInclTax' => 15, + 'expectedCallAppend' => $this->never(), + + ], + [ + 'subtotalInclTax' => 0, + 'minOrderAmount' => 10, + 'packageValueWithDiscount' => 8, + 'baseSubtotalWithDiscountInclTax' => 15, + 'expectedCallAppend' => $this->never(), + + ], + ]; + } +} diff --git a/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml b/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml index 2b29d2211b9d1..cb75bddf4d7bd 100644 --- a/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml +++ b/app/code/Magento/OfflineShipping/etc/adminhtml/system.xml @@ -127,6 +127,10 @@ <label>Minimum Order Amount</label> <validate>validate-number validate-zero-or-greater</validate> </field> + <field id="tax_including" translate="label" sortOrder="5" type="select" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> + <label>Include Tax to Amount</label> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + </field> <field id="name" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Method Name</label> </field> diff --git a/app/code/Magento/OfflineShipping/registration.php b/app/code/Magento/OfflineShipping/registration.php index ce3611a90c657..5c2ff2d82f1fd 100644 --- a/app/code/Magento/OfflineShipping/registration.php +++ b/app/code/Magento/OfflineShipping/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_OfflineShipping', __DIR__); diff --git a/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php b/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php index ef85d6faf2371..88619673ad425 100644 --- a/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php +++ b/app/code/Magento/PageCache/Model/Layout/LayoutPlugin.php @@ -7,6 +7,8 @@ /** * Class LayoutPlugin + * + * Plugin for Magento\Framework\View\Layout */ class LayoutPlugin { @@ -20,22 +22,31 @@ class LayoutPlugin */ protected $response; + /** + * @var \Magento\Framework\App\MaintenanceMode + */ + private $maintenanceMode; + /** * Constructor * * @param \Magento\Framework\App\ResponseInterface $response - * @param \Magento\PageCache\Model\Config $config + * @param \Magento\PageCache\Model\Config $config + * @param \Magento\Framework\App\MaintenanceMode $maintenanceMode */ public function __construct( \Magento\Framework\App\ResponseInterface $response, - \Magento\PageCache\Model\Config $config + \Magento\PageCache\Model\Config $config, + \Magento\Framework\App\MaintenanceMode $maintenanceMode ) { $this->response = $response; $this->config = $config; + $this->maintenanceMode = $maintenanceMode; } /** * Set appropriate Cache-Control headers + * * We have to set public headers in order to tell Varnish and Builtin app that page should be cached * * @param \Magento\Framework\View\Layout $subject @@ -44,7 +55,7 @@ public function __construct( */ public function afterGenerateXml(\Magento\Framework\View\Layout $subject, $result) { - if ($subject->isCacheable() && $this->config->isEnabled()) { + if ($subject->isCacheable() && !$this->maintenanceMode->isOn() && $this->config->isEnabled()) { $this->response->setPublicHeaders($this->config->getTtl()); } return $result; @@ -68,6 +79,7 @@ public function afterGetOutput(\Magento\Framework\View\Layout $subject, $result) if ($isVarnish && $isEsiBlock) { continue; } + // phpcs:ignore $tags = array_merge($tags, $block->getIdentities()); } } diff --git a/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance.php b/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance.php deleted file mode 100644 index 7017da27eee93..0000000000000 --- a/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance.php +++ /dev/null @@ -1,108 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\PageCache\Observer; - -use Magento\Framework\Event\ObserverInterface; -use Magento\Framework\Event\Observer; -use Magento\Framework\App\Cache\Manager; -use Magento\PageCache\Model\Cache\Type as PageCacheType; -use Magento\PageCache\Observer\SwitchPageCacheOnMaintenance\PageCacheState; - -/** - * Switch Page Cache on maintenance. - */ -class SwitchPageCacheOnMaintenance implements ObserverInterface -{ - /** - * @var Manager - */ - private $cacheManager; - - /** - * @var PageCacheState - */ - private $pageCacheStateStorage; - - /** - * @param Manager $cacheManager - * @param PageCacheState $pageCacheStateStorage - */ - public function __construct(Manager $cacheManager, PageCacheState $pageCacheStateStorage) - { - $this->cacheManager = $cacheManager; - $this->pageCacheStateStorage = $pageCacheStateStorage; - } - - /** - * Switches Full Page Cache. - * - * Depending on enabling or disabling Maintenance Mode it turns off or restores Full Page Cache state. - * - * @param Observer $observer - * @return void - */ - public function execute(Observer $observer): void - { - if ($observer->getData('isOn')) { - $this->pageCacheStateStorage->save($this->isFullPageCacheEnabled()); - $this->turnOffFullPageCache(); - } else { - $this->restoreFullPageCacheState(); - } - } - - /** - * Turns off Full Page Cache. - * - * @return void - */ - private function turnOffFullPageCache(): void - { - if (!$this->isFullPageCacheEnabled()) { - return; - } - - $this->cacheManager->clean([PageCacheType::TYPE_IDENTIFIER]); - $this->cacheManager->setEnabled([PageCacheType::TYPE_IDENTIFIER], false); - } - - /** - * Full Page Cache state. - * - * @return bool - */ - private function isFullPageCacheEnabled(): bool - { - $cacheStatus = $this->cacheManager->getStatus(); - - if (!array_key_exists(PageCacheType::TYPE_IDENTIFIER, $cacheStatus)) { - return false; - } - - return (bool)$cacheStatus[PageCacheType::TYPE_IDENTIFIER]; - } - - /** - * Restores Full Page Cache state. - * - * Returns FPC to previous state that was before maintenance mode turning on. - * - * @return void - */ - private function restoreFullPageCacheState(): void - { - $storedPageCacheState = $this->pageCacheStateStorage->isEnabled(); - $this->pageCacheStateStorage->flush(); - - if ($storedPageCacheState) { - $this->cacheManager->setEnabled([PageCacheType::TYPE_IDENTIFIER], true); - } - } -} diff --git a/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php b/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php index e4cadf728f2ea..da6a71a0c2655 100644 --- a/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php +++ b/app/code/Magento/PageCache/Observer/SwitchPageCacheOnMaintenance/PageCacheState.php @@ -13,7 +13,11 @@ use Magento\Framework\App\Filesystem\DirectoryList; /** - * Page Cache state. + * Class PageCacheState + * + * Page Cache State Observer + * + * @deprecated Originally used by now removed observer SwitchPageCacheOnMaintenance */ class PageCacheState { diff --git a/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearCacheActionGroup.xml b/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearCacheActionGroup.xml index 1eec4b40db68d..ea76b133bb414 100644 --- a/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearCacheActionGroup.xml +++ b/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearCacheActionGroup.xml @@ -12,23 +12,10 @@ <annotations> <description>Goes to the Admin Cache Management page. Clicks on 'Flush Magento Cache'.</description> </annotations> - - <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/admin/cache/" stepKey="goToNewCustomVarialePage"/> + + <amOnPage url="{{AdminCacheManagementPage.url}}" stepKey="goToCacheManagement"/> <waitForPageLoad stepKey="waitForPageLoad"/> <click selector="{{AdminCacheManagementSection.FlushMagentoCache}}" stepKey="clickFlushMagentoCache"/> <waitForPageLoad stepKey="waitForCacheFlush"/> </actionGroup> - - <actionGroup name="clearPageCache"> - <annotations> - <description>Goes to the Admin Cache Management page. Selects 'Refresh'. Checks the 'Page Cache' row. Clicks on Submit.</description> - </annotations> - - <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/admin/cache/" stepKey="amOnCacheManagementPage"/> - <waitForPageLoad stepKey="waitForCacheManagement"/> - <selectOption selector="{{AdminCacheManagementSection.massActionSelect}}" userInput="refresh" stepKey="selectRefresh"/> - <click selector="{{AdminCacheManagementSection.pageCacheCheckbox}}" stepKey="selectPageCache"/> - <click selector="{{AdminCacheManagementSection.massActionSubmit}}" stepKey="submitCacheForm"/> - <waitForPageLoad stepKey="waitForCacheFlush"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearPageCacheActionGroup.xml b/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearPageCacheActionGroup.xml index 88ed167e24e1a..3c0d2aa8082b1 100644 --- a/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearPageCacheActionGroup.xml +++ b/app/code/Magento/PageCache/Test/Mftf/ActionGroup/ClearPageCacheActionGroup.xml @@ -13,7 +13,7 @@ <description>Goes to the Admin Cache Management page. Selects 'Refresh'. Checks the 'Page Cache' row. Clicks on Submit.</description> </annotations> - <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/admin/cache/" stepKey="goToCacheManagementPage"/> + <amOnPage url="{{AdminCacheManagementPage.url}}" stepKey="goToCacheManagementPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> <click selector="{{AdminCacheManagementSection.actionDropDown}}" stepKey="actionSelection"/> <click selector="{{AdminCacheManagementSection.refreshOption}}" stepKey="selectRefreshOption"/> diff --git a/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest.xml b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest.xml index a3c9e7b39217d..e7dc40c20f4dd 100644 --- a/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest.xml +++ b/app/code/Magento/PageCache/Test/Mftf/Test/NewProductsListWidgetTest.xml @@ -9,21 +9,21 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="NewProductsListWidgetSimpleProductTest"> - <actionGroup ref="clearPageCache" stepKey="clearPageCache" before="amOnCmsPage"/> + <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" before="amOnCmsPage"/> </test> <test name="NewProductsListWidgetConfigurableProductTest"> - <actionGroup ref="clearPageCache" stepKey="clearPageCache" after="clickSaveProduct"/> + <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" after="clickSaveProduct"/> </test> <test name="NewProductsListWidgetGroupedProductTest"> - <actionGroup ref="clearPageCache" stepKey="clearPageCache" after="clickSaveProduct"/> + <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" after="clickSaveProduct"/> </test> <test name="NewProductsListWidgetVirtualProductTest"> - <actionGroup ref="clearPageCache" stepKey="clearPageCache" before="amOnCmsPage"/> + <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" before="amOnCmsPage"/> </test> <test name="NewProductsListWidgetBundleProductTest"> - <actionGroup ref="clearPageCache" stepKey="clearPageCache" after="clickSaveProduct"/> + <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" after="clickSaveProduct"/> </test> <test name="NewProductsListWidgetDownloadableProductTest"> - <actionGroup ref="clearPageCache" stepKey="clearPageCache" after="clickSaveProduct"/> + <actionGroup ref="ClearPageCacheActionGroup" stepKey="clearPageCache" after="clickSaveProduct"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/PageCache/Test/Unit/Model/Layout/LayoutPluginTest.php b/app/code/Magento/PageCache/Test/Unit/Model/Layout/LayoutPluginTest.php index 6c39fe1e7979c..e2bc7f237ab0a 100644 --- a/app/code/Magento/PageCache/Test/Unit/Model/Layout/LayoutPluginTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Model/Layout/LayoutPluginTest.php @@ -27,6 +27,11 @@ class LayoutPluginTest extends \PHPUnit\Framework\TestCase */ protected $configMock; + /** + * @var \Magento\Framework\App\MaintenanceMode|\PHPUnit\Framework\MockObject\MockObject + */ + private $maintenanceModeMock; + protected function setUp() { $this->layoutMock = $this->getMockForAbstractClass( @@ -40,27 +45,33 @@ protected function setUp() ); $this->responseMock = $this->createMock(\Magento\Framework\App\Response\Http::class); $this->configMock = $this->createMock(\Magento\PageCache\Model\Config::class); + $this->maintenanceModeMock = $this->createMock(\Magento\Framework\App\MaintenanceMode::class); $this->model = new \Magento\PageCache\Model\Layout\LayoutPlugin( $this->responseMock, - $this->configMock + $this->configMock, + $this->maintenanceModeMock ); } /** * @param $cacheState * @param $layoutIsCacheable + * @param $maintenanceModeIsEnabled + * * @dataProvider afterGenerateXmlDataProvider */ - public function testAfterGenerateXml($cacheState, $layoutIsCacheable) + public function testAfterGenerateXml($cacheState, $layoutIsCacheable, $maintenanceModeIsEnabled) { $maxAge = 180; $result = 'test'; $this->layoutMock->expects($this->once())->method('isCacheable')->will($this->returnValue($layoutIsCacheable)); $this->configMock->expects($this->any())->method('isEnabled')->will($this->returnValue($cacheState)); + $this->maintenanceModeMock->expects($this->any())->method('isOn') + ->will($this->returnValue($maintenanceModeIsEnabled)); - if ($layoutIsCacheable && $cacheState) { + if ($layoutIsCacheable && $cacheState && !$maintenanceModeIsEnabled) { $this->configMock->expects($this->once())->method('getTtl')->will($this->returnValue($maxAge)); $this->responseMock->expects($this->once())->method('setPublicHeaders')->with($maxAge); } else { @@ -76,10 +87,11 @@ public function testAfterGenerateXml($cacheState, $layoutIsCacheable) public function afterGenerateXmlDataProvider() { return [ - 'Full_cache state is true, Layout is cache-able' => [true, true], - 'Full_cache state is true, Layout is not cache-able' => [true, false], - 'Full_cache state is false, Layout is not cache-able' => [false, false], - 'Full_cache state is false, Layout is cache-able' => [false, true] + 'Full_cache state is true, Layout is cache-able' => [true, true, false], + 'Full_cache state is true, Layout is not cache-able' => [true, false, false], + 'Full_cache state is false, Layout is not cache-able' => [false, false, false], + 'Full_cache state is false, Layout is cache-able' => [false, true, false], + 'Full_cache state is true, Layout is cache-able, Maintenance mode is enabled' => [true, true, true], ]; } diff --git a/app/code/Magento/PageCache/Test/Unit/Observer/SwitchPageCacheOnMaintenanceTest.php b/app/code/Magento/PageCache/Test/Unit/Observer/SwitchPageCacheOnMaintenanceTest.php deleted file mode 100644 index 2dbb815c70925..0000000000000 --- a/app/code/Magento/PageCache/Test/Unit/Observer/SwitchPageCacheOnMaintenanceTest.php +++ /dev/null @@ -1,164 +0,0 @@ -<?php -/** - * - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -declare(strict_types=1); - -namespace Magento\PageCache\Test\Unit\Observer; - -use PHPUnit\Framework\TestCase; -use Magento\PageCache\Observer\SwitchPageCacheOnMaintenance; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Framework\App\Cache\Manager; -use Magento\Framework\Event\Observer; -use Magento\PageCache\Model\Cache\Type as PageCacheType; -use Magento\PageCache\Observer\SwitchPageCacheOnMaintenance\PageCacheState; - -/** - * SwitchPageCacheOnMaintenance observer test. - */ -class SwitchPageCacheOnMaintenanceTest extends TestCase -{ - /** - * @var SwitchPageCacheOnMaintenance - */ - private $model; - - /** - * @var Manager|\PHPUnit\Framework\MockObject\MockObject - */ - private $cacheManager; - - /** - * @var PageCacheState|\PHPUnit\Framework\MockObject\MockObject - */ - private $pageCacheStateStorage; - - /** - * @var Observer|\PHPUnit\Framework\MockObject\MockObject - */ - private $observer; - - /** - * @inheritdoc - */ - protected function setUp(): void - { - $objectManager = new ObjectManager($this); - $this->cacheManager = $this->createMock(Manager::class); - $this->pageCacheStateStorage = $this->createMock(PageCacheState::class); - $this->observer = $this->createMock(Observer::class); - - $this->model = $objectManager->getObject(SwitchPageCacheOnMaintenance::class, [ - 'cacheManager' => $this->cacheManager, - 'pageCacheStateStorage' => $this->pageCacheStateStorage, - ]); - } - - /** - * Tests execute when setting maintenance mode to on. - * - * @param array $cacheStatus - * @param bool $cacheState - * @param int $flushCacheCalls - * @return void - * @dataProvider enablingPageCacheStateProvider - */ - public function testExecuteWhileMaintenanceEnabling( - array $cacheStatus, - bool $cacheState, - int $flushCacheCalls - ): void { - $this->observer->method('getData') - ->with('isOn') - ->willReturn(true); - $this->cacheManager->method('getStatus') - ->willReturn($cacheStatus); - - // Page Cache state will be stored. - $this->pageCacheStateStorage->expects($this->once()) - ->method('save') - ->with($cacheState); - - // Page Cache will be cleaned and disabled - $this->cacheManager->expects($this->exactly($flushCacheCalls)) - ->method('clean') - ->with([PageCacheType::TYPE_IDENTIFIER]); - $this->cacheManager->expects($this->exactly($flushCacheCalls)) - ->method('setEnabled') - ->with([PageCacheType::TYPE_IDENTIFIER], false); - - $this->model->execute($this->observer); - } - - /** - * Tests execute when setting Maintenance Mode to off. - * - * @param bool $storedCacheState - * @param int $enableCacheCalls - * @return void - * @dataProvider disablingPageCacheStateProvider - */ - public function testExecuteWhileMaintenanceDisabling(bool $storedCacheState, int $enableCacheCalls): void - { - $this->observer->method('getData') - ->with('isOn') - ->willReturn(false); - - $this->pageCacheStateStorage->method('isEnabled') - ->willReturn($storedCacheState); - - // Nullify Page Cache state. - $this->pageCacheStateStorage->expects($this->once()) - ->method('flush'); - - // Page Cache will be enabled. - $this->cacheManager->expects($this->exactly($enableCacheCalls)) - ->method('setEnabled') - ->with([PageCacheType::TYPE_IDENTIFIER]); - - $this->model->execute($this->observer); - } - - /** - * Page Cache state data provider. - * - * @return array - */ - public function enablingPageCacheStateProvider(): array - { - return [ - 'page_cache_is_enable' => [ - 'cache_status' => [PageCacheType::TYPE_IDENTIFIER => 1], - 'cache_state' => true, - 'flush_cache_calls' => 1, - ], - 'page_cache_is_missing_in_system' => [ - 'cache_status' => [], - 'cache_state' => false, - 'flush_cache_calls' => 0, - ], - 'page_cache_is_disable' => [ - 'cache_status' => [PageCacheType::TYPE_IDENTIFIER => 0], - 'cache_state' => false, - 'flush_cache_calls' => 0, - ], - ]; - } - - /** - * Page Cache state data provider. - * - * @return array - */ - public function disablingPageCacheStateProvider(): array - { - return [ - ['stored_cache_state' => true, 'enable_cache_calls' => 1], - ['stored_cache_state' => false, 'enable_cache_calls' => 0], - ]; - } -} diff --git a/app/code/Magento/PageCache/etc/events.xml b/app/code/Magento/PageCache/etc/events.xml index 3f0a2532ae60a..7584f5f36d69c 100644 --- a/app/code/Magento/PageCache/etc/events.xml +++ b/app/code/Magento/PageCache/etc/events.xml @@ -57,7 +57,4 @@ <event name="customer_logout"> <observer name="FlushFormKey" instance="Magento\PageCache\Observer\FlushFormKey"/> </event> - <event name="maintenance_mode_changed"> - <observer name="page_cache_switcher_for_maintenance" instance="Magento\PageCache\Observer\SwitchPageCacheOnMaintenance"/> - </event> </config> diff --git a/app/code/Magento/PageCache/registration.php b/app/code/Magento/PageCache/registration.php index 32628ae4446c9..69d49f120d4cf 100644 --- a/app/code/Magento/PageCache/registration.php +++ b/app/code/Magento/PageCache/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_PageCache', __DIR__); diff --git a/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js b/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js index 735fe9a6cb236..41a32ab8a49c8 100644 --- a/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js +++ b/app/code/Magento/PageCache/view/frontend/web/js/page-cache.js @@ -112,11 +112,14 @@ define([ * @private */ _create: function () { - var formKey = $.mage.cookies.get('form_key'); + var formKey = $.mage.cookies.get('form_key'), + options = { + secure: window.cookiesConfig ? window.cookiesConfig.secure : false + }; if (!formKey) { formKey = generateRandomString(this.options.allowedCharacters, this.options.length); - $.mage.cookies.set('form_key', formKey); + $.mage.cookies.set('form_key', formKey, options); } $(this.options.inputSelector).val(formKey); } diff --git a/app/code/Magento/Payment/Block/Info/Instructions.php b/app/code/Magento/Payment/Block/Info/Instructions.php index f670fa6925bfb..687c6b54a2f4f 100644 --- a/app/code/Magento/Payment/Block/Info/Instructions.php +++ b/app/code/Magento/Payment/Block/Info/Instructions.php @@ -25,18 +25,6 @@ class Instructions extends \Magento\Payment\Block\Info */ protected $_template = 'Magento_Payment::info/instructions.phtml'; - /** - * Gets payment method title for appropriate store. - * - * @return string - * @throws \Magento\Framework\Exception\LocalizedException - */ - public function getTitle() - { - return $this->getInfo()->getAdditionalInformation('method_title') - ?: $this->getMethod()->getConfigData('title', $this->getInfo()->getOrder()->getStoreId()); - } - /** * Get instructions text from order payment * (or from config, if instructions are missed in payment) diff --git a/app/code/Magento/Payment/Model/Info.php b/app/code/Magento/Payment/Model/Info.php index 3b7f93be776ee..3ca9b072e8321 100644 --- a/app/code/Magento/Payment/Model/Info.php +++ b/app/code/Magento/Payment/Model/Info.php @@ -38,7 +38,7 @@ class Info extends AbstractExtensibleModel implements InfoInterface * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory - * @param \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory, + * @param \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory * @param \Magento\Payment\Helper\Data $paymentData * @param \Magento\Framework\Encryption\EncryptorInterface $encryptor * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource @@ -188,6 +188,7 @@ public function getAdditionalInformation($key = null) */ public function unsAdditionalInformation($key = null) { + $this->_initAdditionalInformation(); if ($key && isset($this->_additionalInformation[$key])) { unset($this->_additionalInformation[$key]); return $this->setData('additional_information', $this->_additionalInformation); diff --git a/app/code/Magento/Payment/Test/Mftf/Data/PaymentMethodData.xml b/app/code/Magento/Payment/Test/Mftf/Data/PaymentMethodData.xml index 14c8bd0fecde7..eeae53d082443 100644 --- a/app/code/Magento/Payment/Test/Mftf/Data/PaymentMethodData.xml +++ b/app/code/Magento/Payment/Test/Mftf/Data/PaymentMethodData.xml @@ -11,4 +11,8 @@ <entity name="PaymentMethodCheckMoneyOrder" type="payment_method"> <data key="method">checkmo</data> </entity> + + <entity name="CashOnDeliveryPaymentMethodDefault" type="cashondelivery_payment_method"> + <requiredEntity type="active">CashOnDeliveryEnableConfigData</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Payment/Test/Mftf/Metadata/payment_method-meta.xml b/app/code/Magento/Payment/Test/Mftf/Metadata/payment_method-meta.xml index 39506a682038f..3ad3a0e1c60de 100644 --- a/app/code/Magento/Payment/Test/Mftf/Metadata/payment_method-meta.xml +++ b/app/code/Magento/Payment/Test/Mftf/Metadata/payment_method-meta.xml @@ -11,4 +11,18 @@ <operation name="CreatePaymentMethod" dataType="payment_method" type="create"> <field key="method">string</field> </operation> + <operation name="cashondeliveryPaymentMethodSetup" dataType="cashondelivery_payment_method" type="create" auth="adminFormKey" url="/admin/system_config/save/section/payment/" method="POST"> + <object key="groups" dataType="cashondelivery_payment_method"> + <object key="cashondelivery" dataType="cashondelivery_payment_method"> + <object key="fields" dataType="cashondelivery_payment_method"> + <object key="active" dataType="active"> + <field key="value">string</field> + </object> + <object key="title" dataType="title"> + <field key="value">string</field> + </object> + </object> + </object> + </object> + </operation> </operations> diff --git a/app/code/Magento/Payment/Test/Unit/Block/Info/InstructionsTest.php b/app/code/Magento/Payment/Test/Unit/Block/Info/InstructionsTest.php index 88144b6e05c62..68c76d94e02ae 100644 --- a/app/code/Magento/Payment/Test/Unit/Block/Info/InstructionsTest.php +++ b/app/code/Magento/Payment/Test/Unit/Block/Info/InstructionsTest.php @@ -4,12 +4,11 @@ * See COPYING.txt for license details. */ +/** + * Test class for \Magento\Payment\Block\Info\Instructions + */ namespace Magento\Payment\Test\Unit\Block\Info; -use Magento\Payment\Model\MethodInterface; -use Magento\Sales\Model\Order; -use PHPUnit\Framework\MockObject\MockObject; - class InstructionsTest extends \PHPUnit\Framework\TestCase { /** @@ -26,59 +25,10 @@ protected function setUp() { $context = $this->createMock(\Magento\Framework\View\Element\Template\Context::class); $this->_instructions = new \Magento\Payment\Block\Info\Instructions($context); - $this->_info = $this->getMockBuilder(\Magento\Payment\Model\Info::class) - ->setMethods( - [ - 'getOrder', - 'getAdditionalInformation', - 'getMethodInstance' - ] - ) - ->disableOriginalConstructor() - ->getMock(); + $this->_info = $this->createMock(\Magento\Payment\Model\Info::class); $this->_instructions->setData('info', $this->_info); } - /** - * @return void - * @throws \Magento\Framework\Exception\LocalizedException - */ - public function testGetTitleFromPaymentAdditionalData() - { - $this->_info->method('getAdditionalInformation') - ->with('method_title') - ->willReturn('payment_method_title'); - - $this->getMethod()->expects($this->never()) - ->method('getConfigData'); - - $this->assertEquals($this->_instructions->getTitle(), 'payment_method_title'); - } - - /** - * @return void - * @throws \Magento\Framework\Exception\LocalizedException - */ - public function testGetTitleFromPaymentMethodConfig() - { - $this->_info->method('getAdditionalInformation') - ->with('method_title') - ->willReturn(null); - - $this->getMethod()->expects($this->once()) - ->method('getConfigData') - ->with('title', null) - ->willReturn('payment_method_title'); - - $order = $this->getOrder(); - $this->_info->method('getOrder')->willReturn($order); - - $this->assertEquals($this->_instructions->getTitle(), 'payment_method_title'); - } - - /** - * @return void - */ public function testGetInstructionAdditionalInformation() { $this->_info->expects($this->once()) @@ -91,13 +41,10 @@ public function testGetInstructionAdditionalInformation() $this->assertEquals('get the instruction here', $this->_instructions->getInstructions()); } - /** - * @return void - */ public function testGetInstruction() { $methodInstance = $this->getMockBuilder( - MethodInterface::class + \Magento\Payment\Model\MethodInterface::class )->getMockForAbstractClass(); $methodInstance->expects($this->once()) ->method('getConfigData') @@ -112,27 +59,4 @@ public function testGetInstruction() ->willReturn($methodInstance); $this->assertEquals('get the instruction here', $this->_instructions->getInstructions()); } - - /** - * @return MethodInterface|MockObject - */ - private function getMethod() - { - $method = $this->getMockBuilder(MethodInterface::class) - ->getMockForAbstractClass(); - $this->_info->method('getMethodInstance') - ->willReturn($method); - - return $method; - } - - /** - * @return Order|MockObject - */ - private function getOrder() - { - return $this->getMockBuilder(Order::class) - ->disableOriginalConstructor() - ->getMock(); - } } diff --git a/app/code/Magento/Payment/registration.php b/app/code/Magento/Payment/registration.php index 12f826a2ad5ce..9b1fb56a89600 100644 --- a/app/code/Magento/Payment/registration.php +++ b/app/code/Magento/Payment/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Payment', __DIR__); diff --git a/app/code/Magento/Payment/view/adminhtml/templates/info/instructions.phtml b/app/code/Magento/Payment/view/adminhtml/templates/info/instructions.phtml index 904f0bd2e2cdc..f60c1d063addf 100644 --- a/app/code/Magento/Payment/view/adminhtml/templates/info/instructions.phtml +++ b/app/code/Magento/Payment/view/adminhtml/templates/info/instructions.phtml @@ -9,7 +9,7 @@ * @see \Magento\Payment\Block\Info */ ?> -<p><?= $block->escapeHtml($block->getTitle()) ?></p> +<p><?= $block->escapeHtml($block->getMethod()->getTitle()) ?></p> <?php if ($block->getInstructions()) : ?> <table> <tbody> diff --git a/app/code/Magento/Payment/view/frontend/templates/info/instructions.phtml b/app/code/Magento/Payment/view/frontend/templates/info/instructions.phtml index a8d2b15c3ea31..60efae16b1711 100644 --- a/app/code/Magento/Payment/view/frontend/templates/info/instructions.phtml +++ b/app/code/Magento/Payment/view/frontend/templates/info/instructions.phtml @@ -10,7 +10,7 @@ */ ?> <dl class="payment-method"> - <dt class="title"><?= $block->escapeHtml($block->getTitle()) ?></dt> + <dt class="title"><?= $block->escapeHtml($block->getMethod()->getTitle()) ?></dt> <?php if ($block->getInstructions()) : ?> <dd class="content"><?= /* @noEscape */ nl2br($block->escapeHtml($block->getInstructions())) ?></dd> <?php endif; ?> diff --git a/app/code/Magento/Paypal/Model/SmartButtonConfig.php b/app/code/Magento/Paypal/Model/SmartButtonConfig.php index ede9cacf25d40..59e4db6d84201 100644 --- a/app/code/Magento/Paypal/Model/SmartButtonConfig.php +++ b/app/code/Magento/Paypal/Model/SmartButtonConfig.php @@ -83,7 +83,7 @@ public function getConfig(string $page): array 'allowedFunding' => $this->getAllowedFunding($page), 'disallowedFunding' => $this->getDisallowedFunding(), 'styles' => $this->getButtonStyles($page), - 'isVisibleOnProductPage' => $this->config->getValue('visible_on_product'), + 'isVisibleOnProductPage' => (bool)$this->config->getValue('visible_on_product'), 'isGuestCheckoutAllowed' => $isGuestCheckoutAllowed ]; } diff --git a/app/code/Magento/Paypal/Test/Mftf/ActionGroup/AddProductToCheckoutPageActionGroup.xml b/app/code/Magento/Paypal/Test/Mftf/ActionGroup/AddProductToCheckoutPageActionGroup.xml new file mode 100644 index 0000000000000..bf2643fc535f7 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/ActionGroup/AddProductToCheckoutPageActionGroup.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddProductToCheckoutPageActionGroup"> + <annotations> + <description>Goes to the provided Category page on the Storefront. Adds the 1st Product to the Cart. Goes to Checkout. Select the Shipping Method. Selects PayPal as the Payment Method.</description> + </annotations> + <arguments> + <argument name="Category"/> + </arguments> + + <amOnPage url="{{StorefrontCategoryPage.url(Category.name)}}" stepKey="onCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> + <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> + <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> + <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + <click selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask2"/> + <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForNextButton"/> + <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNext"/> + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" stepKey="waitForPlaceOrderButton"/> + <click selector="{{CheckoutPaymentSection.PayPalPaymentRadio}}" stepKey="clickPayPalCheckbox"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Paypal/Test/Mftf/ActionGroup/CheckEnableOptionPayPalConfigurationActionGroup.xml b/app/code/Magento/Paypal/Test/Mftf/ActionGroup/CheckEnableOptionPayPalConfigurationActionGroup.xml new file mode 100644 index 0000000000000..6fe01799c5422 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/ActionGroup/CheckEnableOptionPayPalConfigurationActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CheckEnableOptionPayPalConfigurationActionGroup"> + <annotations> + <description>Expands the 'OTHER PAYPAL PAYMENT SOLUTIONS' tab on the Admin Configuration page. Enables the provided PayPal Config type for the provided Country Code.</description> + </annotations> + <arguments> + <argument name="payPalConfigType"/> + <argument name="enabledOption" type="string"/> + <argument name="countryCode" type="string" defaultValue="us"/> + </arguments> + + <waitForElementVisible selector="{{OtherPayPalPaymentsConfigSection.expandTab(countryCode)}}" stepKey="waitForOtherPayPalPaymentsSection"/> + <conditionalClick selector="{{OtherPayPalPaymentsConfigSection.expandTab(countryCode)}}" dependentSelector="{{OtherPayPalPaymentsConfigSection.expandedTab(countryCode)}}" visible="false" stepKey="clickOtherPayPalPaymentsSection"/> + <waitForElementVisible selector="{{payPalConfigType.configureBtn(countryCode)}}" stepKey="waitForWPSExpressConfigureBtn"/> + <click selector="{{payPalConfigType.configureBtn(countryCode)}}" stepKey="clickWPSExpressConfigureBtn1"/> + <waitForElementVisible selector="{{payPalConfigType.enableSolution(countryCode)}}" stepKey="waitForWPSExpressEnable"/> + <seeOptionIsSelected selector="{{payPalConfigType.enableSolution(countryCode)}}" userInput="{{enabledOption}}" stepKey="seeSelectedOption"/> + <click selector="{{payPalConfigType.configureBtn(countryCode)}}" stepKey="clickWPSExpressConfigureBtn2"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Paypal/Test/Mftf/ActionGroup/ConfigPayPalExpressCheckoutActionGroup.xml b/app/code/Magento/Paypal/Test/Mftf/ActionGroup/ConfigPayPalExpressCheckoutActionGroup.xml new file mode 100644 index 0000000000000..90f0a25a8cd69 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/ActionGroup/ConfigPayPalExpressCheckoutActionGroup.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ConfigPayPalExpressCheckoutActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page for 'Payment Methods'. Fills in the provided PayPal credentials and other details. Clicks on Save.</description> + </annotations> + <arguments> + <argument name="credentials" defaultValue="_CREDS"/> + <argument name="countryCode" type="string" defaultValue="us"/> + </arguments> + + <amOnPage url="{{AdminConfigPaymentMethodsPage.url}}" stepKey="navigateToPaymentConfigurationPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <click selector="{{PayPalExpressCheckoutConfigSection.configureBtn(countryCode)}}" stepKey="clickPayPalConfigureBtn"/> + <waitForElementVisible selector="{{PayPalAdvancedSettingConfigSection.advancedSettingTab(countryCode)}}" stepKey="waitForAdvancedSettingTab"/> + <fillField selector ="{{PayPalExpressCheckoutConfigSection.email(countryCode)}}" userInput="{{credentials.magento/paypal_express_checkout_us_business_account}}" stepKey="inputEmailAssociatedWithPayPalMerchantAccount"/> + <selectOption selector ="{{PayPalExpressCheckoutConfigSection.apiMethod(countryCode)}}" userInput="API Signature" stepKey="inputAPIAuthenticationMethods"/> + <fillField selector ="{{PayPalExpressCheckoutConfigSection.username(countryCode)}}" userInput="{{credentials.magento/paypal_express_checkout_us_api_username}}" stepKey="inputAPIUsername"/> + <fillField selector ="{{PayPalExpressCheckoutConfigSection.password(countryCode)}}" userInput="{{credentials.magento/paypal_express_checkout_us_api_password}}" stepKey="inputAPIPassword"/> + <fillField selector ="{{PayPalExpressCheckoutConfigSection.signature(countryCode)}}" userInput="{{credentials.magento/paypal_express_checkout_us_api_signature}}" stepKey="inputAPISignature"/> + <selectOption selector ="{{PayPalExpressCheckoutConfigSection.sandboxMode(countryCode)}}" userInput="Yes" stepKey="enableSandboxMode"/> + <selectOption selector="{{PayPalExpressCheckoutConfigSection.enableSolution(countryCode)}}" userInput="Yes" stepKey="enableSolution"/> + <fillField selector ="{{PayPalExpressCheckoutConfigSection.merchantID(countryCode)}}" userInput="{{credentials.magento/paypal_express_checkout_us_merchant_id}}" stepKey="inputMerchantID"/> + <!--Save configuration--> + <click selector="{{AdminConfigSection.saveButton}}" stepKey="saveConfig"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Paypal/Test/Mftf/ActionGroup/CreatePayPalOrderWithSelectedPaymentMethodActionGroup.xml b/app/code/Magento/Paypal/Test/Mftf/ActionGroup/CreatePayPalOrderWithSelectedPaymentMethodActionGroup.xml new file mode 100644 index 0000000000000..49ebe90418436 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/ActionGroup/CreatePayPalOrderWithSelectedPaymentMethodActionGroup.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreatePayPalOrderWithSelectedPaymentMethodActionGroup" extends="CreateOrderToPrintPageActionGroup"> + <annotations> + <description>EXTENDS: CreateOrderToPrintPageActionGroup. Clicks on PayPal. Fills the PayPay details in the modal. PLEASE NOTE: The PayPal Payment credentials are Hardcoded using 'Payer'.</description> + </annotations> + <arguments> + <argument name="payerName" defaultValue="MPI" type="string"/> + <argument name="credentials" defaultValue="_CREDS"/> + </arguments> + + <!-- click on PayPal payment radio button --> + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" stepKey="waitForPlaceOrderButton"/> + <click selector="{{CheckoutPaymentSection.PayPalPaymentRadio}}" stepKey="clickPlaceOrder"/> + + <!--set ID for iframe of PayPal group button--> + <executeJS function="jQuery('.zoid-component-frame.zoid-visible').attr('id', 'myIframe')" stepKey="clickOrderLink"/> + + <!--switch to iframe of PayPal group button--> + <switchToIFrame userInput="myIframe" stepKey="clickPrintOrderLink"/> + <waitForElementVisible selector="{{CheckoutPaymentSection.PayPalBtn}}" stepKey="waitForPayPalBtn"/> + <click selector="{{CheckoutPaymentSection.PayPalBtn}}" stepKey="clickPayPalBtn"/> + <switchToIFrame stepKey="switchBack1"/> + + <!--Check in-context--> + <switchToNextTab stepKey="switchToInContentTab"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <seeCurrentUrlMatches regex="~\//www.sandbox.paypal.com/~" stepKey="seeCurrentUrlMatchesConfigPath1"/> + <waitForElement selector="{{PayPalPaymentSection.email}}" stepKey="waitForLoginForm" /> + <fillField selector="{{PayPalPaymentSection.email}}" userInput="{{credentials.magento/paypal_sandbox_login_email}}" stepKey="fillEmail"/> + <fillField selector="{{PayPalPaymentSection.password}}" userInput="{{credentials.magento/paypal_sandbox_login_password}}" stepKey="fillPassword"/> + <click selector="{{PayPalPaymentSection.loginBtn}}" stepKey="login"/> + <waitForPageLoad stepKey="wait"/> + <see userInput="{{payerName}}" selector="{{PayPalPaymentSection.reviewUserInfo}}" stepKey="seePayerName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Paypal/Test/Mftf/ActionGroup/EnablePayPalConfigurationActionGroup.xml b/app/code/Magento/Paypal/Test/Mftf/ActionGroup/EnablePayPalConfigurationActionGroup.xml new file mode 100644 index 0000000000000..b653858f770e9 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/ActionGroup/EnablePayPalConfigurationActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="EnablePayPalConfigurationActionGroup"> + <annotations> + <description>Expands the 'OTHER PAYPAL PAYMENT SOLUTIONS' tab on the Admin Configuration page. Enables the provided PayPal Config type for the provided Country Code.</description> + </annotations> + <arguments> + <argument name="payPalConfigType"/> + <argument name="countryCode" type="string" defaultValue="us"/> + </arguments> + + <waitForElementVisible selector="{{OtherPayPalPaymentsConfigSection.expandTab(countryCode)}}" stepKey="waitForOtherPayPalPaymentsSection"/> + <conditionalClick selector="{{OtherPayPalPaymentsConfigSection.expandTab(countryCode)}}" dependentSelector="{{OtherPayPalPaymentsConfigSection.expandedTab(countryCode)}}" visible="false" stepKey="clickOtherPayPalPaymentsSection"/> + <waitForElementVisible selector="{{payPalConfigType.configureBtn(countryCode)}}" stepKey="waitForWPSExpressConfigureBtn"/> + <click selector="{{payPalConfigType.configureBtn(countryCode)}}" stepKey="clickWPSExpressConfigureBtn"/> + <waitForElementVisible selector="{{payPalConfigType.enableSolution(countryCode)}}" stepKey="waitForWPSExpressEnable"/> + <selectOption selector="{{payPalConfigType.enableSolution(countryCode)}}" userInput="Yes" stepKey="enableWPSExpressSolution"/> + <seeInPopup userInput="There is already another PayPal solution enabled. Enable this solution instead?" stepKey="seeAlertMessage"/> + <acceptPopup stepKey="acceptEnablePopUp"/> + <click selector="{{AdminConfigSection.saveButton}}" stepKey="saveConfig"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Paypal/Test/Mftf/ActionGroup/EnablePayPalSolutionWithoutSaveActionGroup.xml b/app/code/Magento/Paypal/Test/Mftf/ActionGroup/EnablePayPalSolutionWithoutSaveActionGroup.xml new file mode 100644 index 0000000000000..51b70b10a8827 --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/ActionGroup/EnablePayPalSolutionWithoutSaveActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="EnablePayPalSolutionWithoutSaveActionGroup" > + <annotations> + <description>Expands the 'OTHER PAYPAL PAYMENT SOLUTIONS' tab on the Admin Configuration page. Enables the provided PayPal Config type for the provided Country Code without saving.</description> + </annotations> + <arguments> + <argument name="payPalConfigType"/> + <argument name="countryCode" type="string" defaultValue="us"/> + </arguments> + <waitForElementVisible selector="{{OtherPayPalPaymentsConfigSection.expandTab(countryCode)}}" stepKey="waitForOtherPayPalPaymentsSection"/> + <conditionalClick selector="{{OtherPayPalPaymentsConfigSection.expandTab(countryCode)}}" dependentSelector="{{OtherPayPalPaymentsConfigSection.expandedTab(countryCode)}}" visible="false" stepKey="clickOtherPayPalPaymentsSection"/> + <waitForElementVisible selector="{{payPalConfigType.configureBtn(countryCode)}}" stepKey="waitForWPSExpressConfigureBtn"/> + <click selector="{{payPalConfigType.configureBtn(countryCode)}}" stepKey="clickWPSExpressConfigureBtn"/> + <waitForElementVisible selector="{{payPalConfigType.enableSolution(countryCode)}}" stepKey="waitForWPSExpressEnable"/> + <selectOption selector="{{payPalConfigType.enableSolution(countryCode)}}" userInput="Yes" stepKey="enableWPSExpressSolution"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Paypal/Test/Mftf/ActionGroup/OtherPayPalConfigurationActionGroup.xml b/app/code/Magento/Paypal/Test/Mftf/ActionGroup/OtherPayPalConfigurationActionGroup.xml deleted file mode 100644 index c3e99854ce3ac..0000000000000 --- a/app/code/Magento/Paypal/Test/Mftf/ActionGroup/OtherPayPalConfigurationActionGroup.xml +++ /dev/null @@ -1,64 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="EnablePayPalConfiguration"> - <annotations> - <description>Expands the 'OTHER PAYPAL PAYMENT SOLUTIONS' tab on the Admin Configuration page. Enables the provided PayPal Config type for the provided Country Code.</description> - </annotations> - <arguments> - <argument name="payPalConfigType"/> - <argument name="countryCode" type="string" defaultValue="us"/> - </arguments> - - <waitForElementVisible selector="{{OtherPayPalPaymentsConfigSection.expandTab(countryCode)}}" stepKey="waitForOtherPayPalPaymentsSection"/> - <conditionalClick selector="{{OtherPayPalPaymentsConfigSection.expandTab(countryCode)}}" dependentSelector="{{OtherPayPalPaymentsConfigSection.expandedTab(countryCode)}}" visible="false" stepKey="clickOtherPayPalPaymentsSection"/> - <waitForElementVisible selector="{{payPalConfigType.configureBtn(countryCode)}}" stepKey="waitForWPSExpressConfigureBtn"/> - <click selector="{{payPalConfigType.configureBtn(countryCode)}}" stepKey="clickWPSExpressConfigureBtn"/> - <waitForElementVisible selector="{{payPalConfigType.enableSolution(countryCode)}}" stepKey="waitForWPSExpressEnable"/> - <selectOption selector="{{payPalConfigType.enableSolution(countryCode)}}" userInput="Yes" stepKey="enableWPSExpressSolution"/> - <seeInPopup userInput="There is already another PayPal solution enabled. Enable this solution instead?" stepKey="seeAlertMessage"/> - <acceptPopup stepKey="acceptEnablePopUp"/> - <click selector="{{AdminConfigSection.saveButton}}" stepKey="saveConfig"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - </actionGroup> - <actionGroup name="EnablePayPalSolutionWithoutSave" > - <annotations> - <description>Expands the 'OTHER PAYPAL PAYMENT SOLUTIONS' tab on the Admin Configuration page. Enables the provided PayPal Config type for the provided Country Code without saving.</description> - </annotations> - <arguments> - <argument name="payPalConfigType"/> - <argument name="countryCode" type="string" defaultValue="us"/> - </arguments> - <waitForElementVisible selector="{{OtherPayPalPaymentsConfigSection.expandTab(countryCode)}}" stepKey="waitForOtherPayPalPaymentsSection"/> - <conditionalClick selector="{{OtherPayPalPaymentsConfigSection.expandTab(countryCode)}}" dependentSelector="{{OtherPayPalPaymentsConfigSection.expandedTab(countryCode)}}" visible="false" stepKey="clickOtherPayPalPaymentsSection"/> - <waitForElementVisible selector="{{payPalConfigType.configureBtn(countryCode)}}" stepKey="waitForWPSExpressConfigureBtn"/> - <click selector="{{payPalConfigType.configureBtn(countryCode)}}" stepKey="clickWPSExpressConfigureBtn"/> - <waitForElementVisible selector="{{payPalConfigType.enableSolution(countryCode)}}" stepKey="waitForWPSExpressEnable"/> - <selectOption selector="{{payPalConfigType.enableSolution(countryCode)}}" userInput="Yes" stepKey="enableWPSExpressSolution"/> - </actionGroup> - <actionGroup name="CheckEnableOptionPayPalConfiguration"> - <annotations> - <description>Expands the 'OTHER PAYPAL PAYMENT SOLUTIONS' tab on the Admin Configuration page. Enables the provided PayPal Config type for the provided Country Code.</description> - </annotations> - <arguments> - <argument name="payPalConfigType"/> - <argument name="enabledOption" type="string"/> - <argument name="countryCode" type="string" defaultValue="us"/> - </arguments> - - <waitForElementVisible selector="{{OtherPayPalPaymentsConfigSection.expandTab(countryCode)}}" stepKey="waitForOtherPayPalPaymentsSection"/> - <conditionalClick selector="{{OtherPayPalPaymentsConfigSection.expandTab(countryCode)}}" dependentSelector="{{OtherPayPalPaymentsConfigSection.expandedTab(countryCode)}}" visible="false" stepKey="clickOtherPayPalPaymentsSection"/> - <waitForElementVisible selector="{{payPalConfigType.configureBtn(countryCode)}}" stepKey="waitForWPSExpressConfigureBtn"/> - <click selector="{{payPalConfigType.configureBtn(countryCode)}}" stepKey="clickWPSExpressConfigureBtn1"/> - <waitForElementVisible selector="{{payPalConfigType.enableSolution(countryCode)}}" stepKey="waitForWPSExpressEnable"/> - <seeOptionIsSelected selector="{{payPalConfigType.enableSolution(countryCode)}}" userInput="{{enabledOption}}" stepKey="seeSelectedOption"/> - <click selector="{{payPalConfigType.configureBtn(countryCode)}}" stepKey="clickWPSExpressConfigureBtn2"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Paypal/Test/Mftf/ActionGroup/PayPalExpressCheckoutConfigurationActionGroup.xml b/app/code/Magento/Paypal/Test/Mftf/ActionGroup/PayPalExpressCheckoutConfigurationActionGroup.xml deleted file mode 100644 index 4d752d8377640..0000000000000 --- a/app/code/Magento/Paypal/Test/Mftf/ActionGroup/PayPalExpressCheckoutConfigurationActionGroup.xml +++ /dev/null @@ -1,118 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="ConfigPayPalExpressCheckout"> - <annotations> - <description>Goes to the 'Configuration' page for 'Payment Methods'. Fills in the provided PayPal credentials and other details. Clicks on Save.</description> - </annotations> - <arguments> - <argument name="credentials" defaultValue="_CREDS"/> - <argument name="countryCode" type="string" defaultValue="us"/> - </arguments> - - <amOnPage url="{{AdminConfigPaymentMethodsPage.url}}" stepKey="navigateToPaymentConfigurationPage"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <click selector="{{PayPalExpressCheckoutConfigSection.configureBtn(countryCode)}}" stepKey="clickPayPalConfigureBtn"/> - <waitForElementVisible selector="{{PayPalAdvancedSettingConfigSection.advancedSettingTab(countryCode)}}" stepKey="waitForAdvancedSettingTab"/> - <fillField selector ="{{PayPalExpressCheckoutConfigSection.email(countryCode)}}" userInput="{{credentials.magento/paypal_express_checkout_us_business_account}}" stepKey="inputEmailAssociatedWithPayPalMerchantAccount"/> - <selectOption selector ="{{PayPalExpressCheckoutConfigSection.apiMethod(countryCode)}}" userInput="API Signature" stepKey="inputAPIAuthenticationMethods"/> - <fillField selector ="{{PayPalExpressCheckoutConfigSection.username(countryCode)}}" userInput="{{credentials.magento/paypal_express_checkout_us_api_username}}" stepKey="inputAPIUsername"/> - <fillField selector ="{{PayPalExpressCheckoutConfigSection.password(countryCode)}}" userInput="{{credentials.magento/paypal_express_checkout_us_api_password}}" stepKey="inputAPIPassword"/> - <fillField selector ="{{PayPalExpressCheckoutConfigSection.signature(countryCode)}}" userInput="{{credentials.magento/paypal_express_checkout_us_api_signature}}" stepKey="inputAPISignature"/> - <selectOption selector ="{{PayPalExpressCheckoutConfigSection.sandboxMode(countryCode)}}" userInput="Yes" stepKey="enableSandboxMode"/> - <selectOption selector="{{PayPalExpressCheckoutConfigSection.enableSolution(countryCode)}}" userInput="Yes" stepKey="enableSolution"/> - <fillField selector ="{{PayPalExpressCheckoutConfigSection.merchantID(countryCode)}}" userInput="{{credentials.magento/paypal_express_checkout_us_merchant_id}}" stepKey="inputMerchantID"/> - <!--Save configuration--> - <click selector="{{AdminConfigSection.saveButton}}" stepKey="saveConfig"/> - </actionGroup> - - <actionGroup name="SampleConfigPayPalExpressCheckout"> - <annotations> - <description>Goes to the 'Configuration' page for 'Payment Methods'. Fills in the provided Sample PayPal credentials and other details. Clicks on Save.</description> - </annotations> - <arguments> - <argument name="credentials" defaultValue="SamplePaypalExpressConfig"/> - <argument name="countryCode" type="string" defaultValue="us"/> - </arguments> - <amOnPage url="{{AdminConfigPaymentMethodsPage.url}}" stepKey="navigateToPaymentConfigurationPage"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <click selector="{{PayPalExpressCheckoutConfigSection.configureBtn(countryCode)}}" stepKey="clickPayPalConfigureBtn"/> - <waitForElementVisible selector="{{PayPalAdvancedSettingConfigSection.advancedSettingTab(countryCode)}}" stepKey="waitForAdvancedSettingTab"/> - <fillField selector ="{{PayPalExpressCheckoutConfigSection.email(countryCode)}}" userInput="{{credentials.paypal_express_email}}" stepKey="inputEmailAssociatedWithPayPalMerchantAccount"/> - <selectOption selector ="{{PayPalExpressCheckoutConfigSection.apiMethod(countryCode)}}" userInput="API Signature" stepKey="inputAPIAuthenticationMethods"/> - <fillField selector ="{{PayPalExpressCheckoutConfigSection.username(countryCode)}}" userInput="{{credentials.paypal_express_api_username}}" stepKey="inputAPIUsername"/> - <fillField selector ="{{PayPalExpressCheckoutConfigSection.password(countryCode)}}" userInput="{{credentials.paypal_express_api_password}}" stepKey="inputAPIPassword"/> - <fillField selector ="{{PayPalExpressCheckoutConfigSection.signature(countryCode)}}" userInput="{{credentials.paypal_express_api_signature}}" stepKey="inputAPISignature"/> - <selectOption selector ="{{PayPalExpressCheckoutConfigSection.sandboxMode(countryCode)}}" userInput="Yes" stepKey="enableSandboxMode"/> - <selectOption selector="{{PayPalExpressCheckoutConfigSection.enableSolution(countryCode)}}" userInput="Yes" stepKey="enableSolution"/> - <fillField selector ="{{PayPalExpressCheckoutConfigSection.merchantID(countryCode)}}" userInput="{{credentials.paypal_express_merchantID}}" stepKey="inputMerchantID"/> - <!--Save configuration--> - <click selector="{{AdminConfigSection.saveButton}}" stepKey="saveConfig"/> - </actionGroup> - - <actionGroup name="CreatePayPalOrderWithSelectedPaymentMethodActionGroup" extends="CreateOrderToPrintPageActionGroup"> - <annotations> - <description>EXTENDS: CreateOrderToPrintPageActionGroup. Clicks on PayPal. Fills the PayPay details in the modal. PLEASE NOTE: The PayPal Payment credentials are Hardcoded using 'Payer'.</description> - </annotations> - <arguments> - <argument name="payerName" defaultValue="MPI" type="string"/> - <argument name="credentials" defaultValue="_CREDS"/> - </arguments> - - <!-- click on PayPal payment radio button --> - <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" stepKey="waitForPlaceOrderButton"/> - <click selector="{{CheckoutPaymentSection.PayPalPaymentRadio}}" stepKey="clickPlaceOrder"/> - - <!--set ID for iframe of PayPal group button--> - <executeJS function="jQuery('.zoid-component-frame.zoid-visible').attr('id', 'myIframe')" stepKey="clickOrderLink"/> - - <!--switch to iframe of PayPal group button--> - <switchToIFrame userInput="myIframe" stepKey="clickPrintOrderLink"/> - <waitForElementVisible selector="{{CheckoutPaymentSection.PayPalBtn}}" stepKey="waitForPayPalBtn"/> - <click selector="{{CheckoutPaymentSection.PayPalBtn}}" stepKey="clickPayPalBtn"/> - <switchToIFrame stepKey="switchBack1"/> - - <!--Check in-context--> - <switchToNextTab stepKey="switchToInContentTab"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <seeCurrentUrlMatches regex="~\//www.sandbox.paypal.com/~" stepKey="seeCurrentUrlMatchesConfigPath1"/> - <waitForElement selector="{{PayPalPaymentSection.email}}" stepKey="waitForLoginForm" /> - <fillField selector="{{PayPalPaymentSection.email}}" userInput="{{credentials.magento/paypal_sandbox_login_email}}" stepKey="fillEmail"/> - <fillField selector="{{PayPalPaymentSection.password}}" userInput="{{credentials.magento/paypal_sandbox_login_password}}" stepKey="fillPassword"/> - <click selector="{{PayPalPaymentSection.loginBtn}}" stepKey="login"/> - <waitForPageLoad stepKey="wait"/> - <see userInput="{{payerName}}" selector="{{PayPalPaymentSection.reviewUserInfo}}" stepKey="seePayerName"/> - </actionGroup> - - <actionGroup name="addProductToCheckoutPage"> - <annotations> - <description>Goes to the provided Category page on the Storefront. Adds the 1st Product to the Cart. Goes to Checkout. Select the Shipping Method. Selects PayPal as the Payment Method.</description> - </annotations> - <arguments> - <argument name="Category"/> - </arguments> - - <amOnPage url="{{StorefrontCategoryPage.url(Category.name)}}" stepKey="onCategoryPage"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> - <click selector="{{StorefrontCategoryMainSection.AddToCartBtn}}" stepKey="addToCart"/> - <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="waitForProductAdded"/> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> - <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - <click selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask2"/> - <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForNextButton"/> - <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNext"/> - <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" stepKey="waitForPlaceOrderButton"/> - <click selector="{{CheckoutPaymentSection.PayPalPaymentRadio}}" stepKey="clickPayPalCheckbox"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Paypal/Test/Mftf/ActionGroup/SampleConfigPayPalExpressCheckoutActionGroup.xml b/app/code/Magento/Paypal/Test/Mftf/ActionGroup/SampleConfigPayPalExpressCheckoutActionGroup.xml new file mode 100644 index 0000000000000..23d956c8e9b8f --- /dev/null +++ b/app/code/Magento/Paypal/Test/Mftf/ActionGroup/SampleConfigPayPalExpressCheckoutActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SampleConfigPayPalExpressCheckoutActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page for 'Payment Methods'. Fills in the provided Sample PayPal credentials and other details. Clicks on Save.</description> + </annotations> + <arguments> + <argument name="credentials" defaultValue="SamplePaypalExpressConfig"/> + <argument name="countryCode" type="string" defaultValue="us"/> + </arguments> + <amOnPage url="{{AdminConfigPaymentMethodsPage.url}}" stepKey="navigateToPaymentConfigurationPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <click selector="{{PayPalExpressCheckoutConfigSection.configureBtn(countryCode)}}" stepKey="clickPayPalConfigureBtn"/> + <waitForElementVisible selector="{{PayPalAdvancedSettingConfigSection.advancedSettingTab(countryCode)}}" stepKey="waitForAdvancedSettingTab"/> + <fillField selector ="{{PayPalExpressCheckoutConfigSection.email(countryCode)}}" userInput="{{credentials.paypal_express_email}}" stepKey="inputEmailAssociatedWithPayPalMerchantAccount"/> + <selectOption selector ="{{PayPalExpressCheckoutConfigSection.apiMethod(countryCode)}}" userInput="API Signature" stepKey="inputAPIAuthenticationMethods"/> + <fillField selector ="{{PayPalExpressCheckoutConfigSection.username(countryCode)}}" userInput="{{credentials.paypal_express_api_username}}" stepKey="inputAPIUsername"/> + <fillField selector ="{{PayPalExpressCheckoutConfigSection.password(countryCode)}}" userInput="{{credentials.paypal_express_api_password}}" stepKey="inputAPIPassword"/> + <fillField selector ="{{PayPalExpressCheckoutConfigSection.signature(countryCode)}}" userInput="{{credentials.paypal_express_api_signature}}" stepKey="inputAPISignature"/> + <selectOption selector ="{{PayPalExpressCheckoutConfigSection.sandboxMode(countryCode)}}" userInput="Yes" stepKey="enableSandboxMode"/> + <selectOption selector="{{PayPalExpressCheckoutConfigSection.enableSolution(countryCode)}}" userInput="Yes" stepKey="enableSolution"/> + <fillField selector ="{{PayPalExpressCheckoutConfigSection.merchantID(countryCode)}}" userInput="{{credentials.paypal_express_merchantID}}" stepKey="inputMerchantID"/> + <!--Save configuration--> + <click selector="{{AdminConfigSection.saveButton}}" stepKey="saveConfig"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPal.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPal.xml index cfc7d66ba23cf..e918a417dcfde 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPal.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPal.xml @@ -20,7 +20,7 @@ </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="SampleConfigPayPalExpressCheckout" stepKey="ConfigPayPalExpress"> + <actionGroup ref="SampleConfigPayPalExpressCheckoutActionGroup" stepKey="ConfigPayPalExpress"> <argument name="credentials" value="SamplePaypalExpressConfig"/> </actionGroup> </before> @@ -38,36 +38,36 @@ <waitForPageLoad stepKey="waitForPageLoad1"/> <!-- Enable WPS Express --> <comment userInput="Enable WPS Express" stepKey="enableWPSExpressComment"/> - <actionGroup ref="EnablePayPalConfiguration" stepKey="EnableWPSExpress"> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> <argument name="payPalConfigType" value="WPSExpressConfigSection"/> <argument name="countryCode" value="gb"/> </actionGroup> <!-- Check only the correct solution is enabled --> <comment userInput="Check only the correct solution is enabled" stepKey="checkOnlyTheCorrectSolutionIsEnabledComment1"/> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkPayPalExpressIsDisabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> <argument name="payPalConfigType" value="PayPalExpressCheckoutConfigSection"/> <argument name="enabledOption" value="No"/> <argument name="countryCode" value="gb"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkWPSExpressIsEnabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> <argument name="payPalConfigType" value="WPSExpressConfigSection"/> <argument name="enabledOption" value="Yes"/> <argument name="countryCode" value="gb"/> </actionGroup> <!-- Enable Pro Hosted With Express Checkout --> <comment userInput="Enable Pro Hosted With Express Checkout" stepKey="enableProHostedWithExpressCheckoutComment"/> - <actionGroup ref="EnablePayPalConfiguration" stepKey="EnableProHostedWithExpressCheckou"> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> <argument name="payPalConfigType" value="PaymentsProHostedWithExpressCheckoutConfigSection"/> <argument name="countryCode" value="gb"/> </actionGroup> <!-- Check only the correct solution is enabled --> <comment userInput="Check only the correct solution is enabled" stepKey="checkOnlyTheCorrectSolutionIsEnabledComment2"/> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkWPSExpressIsDisabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> <argument name="payPalConfigType" value="WPSExpressConfigSection"/> <argument name="enabledOption" value="No"/> <argument name="countryCode" value="gb"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkProHostedIsEnabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> <argument name="payPalConfigType" value="PaymentsProHostedWithExpressCheckoutConfigSection"/> <argument name="enabledOption" value="Yes"/> <argument name="countryCode" value="gb"/> @@ -84,30 +84,30 @@ <group value="paypal"/> </annotations> <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Japan" stepKey="setMerchantCountry"/> - <actionGroup ref="EnablePayPalConfiguration" stepKey="EnableWPSExpress"> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> <argument name="payPalConfigType" value="WPSOtherConfigSection"/> <argument name="countryCode" value="jp"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkPayPalExpressIsDisabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> <argument name="enabledOption" value="No"/> <argument name="countryCode" value="jp"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkWPSExpressIsEnabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> <argument name="payPalConfigType" value="WPSOtherConfigSection"/> <argument name="enabledOption" value="Yes"/> <argument name="countryCode" value="jp"/> </actionGroup> - <actionGroup ref="EnablePayPalConfiguration" stepKey="EnableProHostedWithExpressCheckou"> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> <argument name="countryCode" value="jp"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkWPSExpressIsDisabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> <argument name="payPalConfigType" value="WPSOtherConfigSection"/> <argument name="enabledOption" value="No"/> <argument name="countryCode" value="jp"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkProHostedIsEnabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> <argument name="enabledOption" value="Yes"/> <argument name="countryCode" value="jp"/> @@ -124,30 +124,30 @@ <group value="paypal"/> </annotations> <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="France" stepKey="setMerchantCountry"/> - <actionGroup ref="EnablePayPalConfiguration" stepKey="EnableWPSExpress"> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> <argument name="payPalConfigType" value="WPSOtherConfigSection"/> <argument name="countryCode" value="fr"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkPayPalExpressIsDisabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> <argument name="enabledOption" value="No"/> <argument name="countryCode" value="fr"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkWPSExpressIsEnabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> <argument name="payPalConfigType" value="WPSOtherConfigSection"/> <argument name="enabledOption" value="Yes"/> <argument name="countryCode" value="fr"/> </actionGroup> - <actionGroup ref="EnablePayPalConfiguration" stepKey="EnableProHostedWithExpressCheckou"> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> <argument name="countryCode" value="fr"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkWPSExpressIsDisabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> <argument name="payPalConfigType" value="WPSOtherConfigSection"/> <argument name="enabledOption" value="No"/> <argument name="countryCode" value="fr"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkProHostedIsEnabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> <argument name="enabledOption" value="Yes"/> <argument name="countryCode" value="fr"/> @@ -164,30 +164,30 @@ <group value="paypal"/> </annotations> <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Hong Kong SAR China" stepKey="setMerchantCountry"/> - <actionGroup ref="EnablePayPalConfiguration" stepKey="EnableWPSExpress"> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> <argument name="payPalConfigType" value="WPSOtherConfigSection"/> <argument name="countryCode" value="hk"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkPayPalExpressIsDisabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> <argument name="enabledOption" value="No"/> <argument name="countryCode" value="hk"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkWPSExpressIsEnabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> <argument name="payPalConfigType" value="WPSOtherConfigSection"/> <argument name="enabledOption" value="Yes"/> <argument name="countryCode" value="hk"/> </actionGroup> - <actionGroup ref="EnablePayPalConfiguration" stepKey="EnableProHostedWithExpressCheckou"> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> <argument name="countryCode" value="hk"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkWPSExpressIsDisabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> <argument name="payPalConfigType" value="WPSOtherConfigSection"/> <argument name="enabledOption" value="No"/> <argument name="countryCode" value="hk"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkProHostedIsEnabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> <argument name="enabledOption" value="Yes"/> <argument name="countryCode" value="hk"/> @@ -204,30 +204,30 @@ <group value="paypal"/> </annotations> <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Italy" stepKey="setMerchantCountry"/> - <actionGroup ref="EnablePayPalConfiguration" stepKey="EnableWPSExpress"> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> <argument name="payPalConfigType" value="WPSOtherConfigSection"/> <argument name="countryCode" value="it"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkPayPalExpressIsDisabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> <argument name="enabledOption" value="No"/> <argument name="countryCode" value="it"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkWPSExpressIsEnabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> <argument name="payPalConfigType" value="WPSOtherConfigSection"/> <argument name="enabledOption" value="Yes"/> <argument name="countryCode" value="it"/> </actionGroup> - <actionGroup ref="EnablePayPalConfiguration" stepKey="EnableProHostedWithExpressCheckou"> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> <argument name="countryCode" value="it"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkWPSExpressIsDisabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> <argument name="payPalConfigType" value="WPSOtherConfigSection"/> <argument name="enabledOption" value="No"/> <argument name="countryCode" value="it"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkProHostedIsEnabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> <argument name="enabledOption" value="Yes"/> <argument name="countryCode" value="it"/> @@ -244,30 +244,30 @@ <group value="paypal"/> </annotations> <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Spain" stepKey="setMerchantCountry"/> - <actionGroup ref="EnablePayPalConfiguration" stepKey="EnableWPSExpress"> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableWPSExpress"> <argument name="payPalConfigType" value="WPSOtherConfigSection"/> <argument name="countryCode" value="es"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkPayPalExpressIsDisabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkPayPalExpressIsDisabled"> <argument name="payPalConfigType" value="PayPalExpressCheckoutOtherCountryConfigSection"/> <argument name="enabledOption" value="No"/> <argument name="countryCode" value="es"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkWPSExpressIsEnabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsEnabled"> <argument name="payPalConfigType" value="WPSOtherConfigSection"/> <argument name="enabledOption" value="Yes"/> <argument name="countryCode" value="es"/> </actionGroup> - <actionGroup ref="EnablePayPalConfiguration" stepKey="EnableProHostedWithExpressCheckou"> + <actionGroup ref="EnablePayPalConfigurationActionGroup" stepKey="EnableProHostedWithExpressCheckou"> <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> <argument name="countryCode" value="es"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkWPSExpressIsDisabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkWPSExpressIsDisabled"> <argument name="payPalConfigType" value="WPSOtherConfigSection"/> <argument name="enabledOption" value="No"/> <argument name="countryCode" value="es"/> </actionGroup> - <actionGroup ref="CheckEnableOptionPayPalConfiguration" stepKey="checkProHostedIsEnabled"> + <actionGroup ref="CheckEnableOptionPayPalConfigurationActionGroup" stepKey="checkProHostedIsEnabled"> <argument name="payPalConfigType" value="WebsitePaymentsPlusConfigSection"/> <argument name="enabledOption" value="Yes"/> <argument name="countryCode" value="es"/> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminOnePayPalSolutionsEnabledAtTheSameTimeTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminOnePayPalSolutionsEnabledAtTheSameTimeTest.xml index c653c1f03fd74..fd0e74f38287d 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Test/AdminOnePayPalSolutionsEnabledAtTheSameTimeTest.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminOnePayPalSolutionsEnabledAtTheSameTimeTest.xml @@ -39,13 +39,13 @@ <!--Try to enable express checkout Solution--> <comment userInput="Try to enable express checkout Solution" stepKey="commentTryEnableExpressCheckout"/> <amOnPage url="{{AdminConfigPaymentMethodsPage.url}}" stepKey="navigateToPaymentConfigurationPage"/> - <actionGroup ref="EnablePayPalSolutionWithoutSave" stepKey="enableExpressCheckout"> + <actionGroup ref="EnablePayPalSolutionWithoutSaveActionGroup" stepKey="enableExpressCheckout"> <argument name="payPalConfigType" value="WPSExpressConfigSection"/> <argument name="countryCode" value="us"/> </actionGroup> <click selector="{{AdminConfigSection.saveButton}}" stepKey="saveConfig"/> <waitForPageLoad stepKey="waitForPageLoad2"/> - <actionGroup ref="EnablePayPalSolutionWithoutSave" stepKey="enableExpressCheckout2"> + <actionGroup ref="EnablePayPalSolutionWithoutSaveActionGroup" stepKey="enableExpressCheckout2"> <argument name="payPalConfigType" value="PayPalExpressCheckoutConfigSection"/> <argument name="countryCode" value="us"/> </actionGroup> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/StorefrontCheckCreditButtonConfigurationTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/StorefrontCheckCreditButtonConfigurationTest.xml index d8cea82bcc2f5..78a27106bf406 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Test/StorefrontCheckCreditButtonConfigurationTest.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Test/StorefrontCheckCreditButtonConfigurationTest.xml @@ -65,7 +65,7 @@ <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer"> <argument name="Customer" value="$$createCustomer$$"/> </actionGroup> - <actionGroup ref="addProductToCheckoutPage" stepKey="addProductToCheckoutPage"> + <actionGroup ref="AddProductToCheckoutPageActionGroup" stepKey="addProductToCheckoutPage"> <argument name="Category" value="$$createPreReqCategory$$"/> </actionGroup> <!--set ID for iframe of PayPal group button--> diff --git a/app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/System/Config/Fieldset/GroupTest.php b/app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/System/Config/Fieldset/GroupTest.php new file mode 100644 index 0000000000000..1bea6b11b966b --- /dev/null +++ b/app/code/Magento/Paypal/Test/Unit/Block/Adminhtml/System/Config/Fieldset/GroupTest.php @@ -0,0 +1,110 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Paypal\Test\Unit\Block\Adminhtml\System\Config\Fieldset; + +class GroupTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var Group + */ + private $_model; + + /** + * @var \Magento\Framework\Data\Form\Element\AbstractElement + */ + private $_element; + + /** + * @var \Magento\Backend\Model\Auth\Session|\PHPUnit_Framework_MockObject_MockObject + */ + private $_authSession; + + /** + * @var \Magento\User\Model\User|\PHPUnit_Framework_MockObject_MockObject + */ + private $_user; + + /** + * @var \Magento\Config\Model\Config\Structure\Element\Group|\PHPUnit_Framework_MockObject_MockObject + */ + private $_group; + + /** + * @inheritdoc + */ + protected function setUp() + { + $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->_group = $this->createMock(\Magento\Config\Model\Config\Structure\Element\Group::class); + $this->_element = $this->getMockForAbstractClass( + \Magento\Framework\Data\Form\Element\AbstractElement::class, + [], + '', + false, + true, + true, + ['getHtmlId', 'getElementHtml', 'getName', 'getElements', 'getId'] + ); + $this->_element->expects($this->any()) + ->method('getHtmlId') + ->will($this->returnValue('html id')); + $this->_element->expects($this->any()) + ->method('getElementHtml') + ->will($this->returnValue('element html')); + $this->_element->expects($this->any()) + ->method('getName') + ->will($this->returnValue('name')); + $this->_element->expects($this->any()) + ->method('getElements') + ->will($this->returnValue([])); + $this->_element->expects($this->any()) + ->method('getId') + ->will($this->returnValue('id')); + $this->_user = $this->createMock(\Magento\User\Model\User::class); + $this->_authSession = $this->createMock(\Magento\Backend\Model\Auth\Session::class); + $this->_authSession->expects($this->any()) + ->method('__call') + ->with('getUser') + ->will($this->returnValue($this->_user)); + $this->_model = $helper->getObject( + \Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Group::class, + ['authSession' => $this->_authSession] + ); + $this->_model->setGroup($this->_group); + } + + /** + * @param mixed $expanded + * @param int $expected + * @dataProvider isCollapseStateDataProvider + */ + public function testIsCollapseState($expanded, $expected) + { + $this->_user->setExtra(['configState' => []]); + $this->_element->setGroup(isset($expanded) ? ['expanded' => $expanded] : []); + $html = $this->_model->render($this->_element); + $this->assertContains( + '<input id="' . $this->_element->getHtmlId() . '-state" name="config_state[' + . $this->_element->getId() . ']" type="hidden" value="' . $expected . '" />', + $html + ); + } + + /** + * @return array + */ + public function isCollapseStateDataProvider() + { + return [ + [null, 0], + [false, 0], + ['', 0], + [1, 1], + ['1', 1], + ]; + } +} diff --git a/app/code/Magento/Paypal/Test/Unit/Model/_files/expected_config.php b/app/code/Magento/Paypal/Test/Unit/Model/_files/expected_config.php index 478607f9956e6..7256984ab5226 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/_files/expected_config.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/_files/expected_config.php @@ -32,7 +32,7 @@ 'label' => 'installment', 'installmentperiod' => 0 ], - 'isVisibleOnProductPage' => 0, + 'isVisibleOnProductPage' => false, 'isGuestCheckoutAllowed' => true ] ], @@ -62,7 +62,7 @@ 'label' => 'installment', 'installmentperiod' => 0 ], - 'isVisibleOnProductPage' => 0, + 'isVisibleOnProductPage' => false, 'isGuestCheckoutAllowed' => true ] ], @@ -91,7 +91,7 @@ 'shape' => 'rect', 'label' => 'paypal' ], - 'isVisibleOnProductPage' => 0, + 'isVisibleOnProductPage' => false, 'isGuestCheckoutAllowed' => true ] ], @@ -120,7 +120,7 @@ 'shape' => 'rect', 'label' => 'paypal' ], - 'isVisibleOnProductPage' => 0, + 'isVisibleOnProductPage' => false, 'isGuestCheckoutAllowed' => true ] ], @@ -149,7 +149,7 @@ 'shape' => 'rect', 'label' => 'paypal', ], - 'isVisibleOnProductPage' => 0, + 'isVisibleOnProductPage' => false, 'isGuestCheckoutAllowed' => true ] ] diff --git a/app/code/Magento/Paypal/etc/adminhtml/system.xml b/app/code/Magento/Paypal/etc/adminhtml/system.xml index ca886c7827ffd..88bb61f2cdc99 100644 --- a/app/code/Magento/Paypal/etc/adminhtml/system.xml +++ b/app/code/Magento/Paypal/etc/adminhtml/system.xml @@ -13,7 +13,7 @@ <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded</frontend_model> <field id="merchant_country" type="select" translate="label comment" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Merchant Country</label> - <comment>If not specified, Default Country from General Config will be used</comment> + <comment>If not specified, Default Country from General Config will be used.</comment> <frontend_model>Magento\Paypal\Block\Adminhtml\System\Config\Field\Country</frontend_model> <source_model>Magento\Paypal\Model\System\Config\Source\MerchantCountry</source_model> <backend_model>Magento\Paypal\Model\System\Config\Backend\MerchantCountry</backend_model> diff --git a/app/code/Magento/Paypal/registration.php b/app/code/Magento/Paypal/registration.php index f915d1d7b18d7..5b86f0d1ed627 100644 --- a/app/code/Magento/Paypal/registration.php +++ b/app/code/Magento/Paypal/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Paypal', __DIR__); diff --git a/app/code/Magento/Paypal/view/adminhtml/web/styles.css b/app/code/Magento/Paypal/view/adminhtml/web/styles.css index 9d63dbff5f3f9..ee0bb1d0c420b 100644 --- a/app/code/Magento/Paypal/view/adminhtml/web/styles.css +++ b/app/code/Magento/Paypal/view/adminhtml/web/styles.css @@ -28,9 +28,9 @@ .paypal-recommended-header > .admin__collapsible-block > a::before {content: "" !important;} .paypal-other-header > .admin__collapsible-block > a::before {content: '' !important; width: 0; height: 0; border-color: transparent; border-top-color: #000; border-style: solid; border-width: .8rem .5rem 0 .5rem; margin-top:1px; transition: all .2s linear;} .paypal-other-header > .admin__collapsible-block > a.open::before {border-color: transparent; border-bottom-color: #000; border-width: 0 .5rem .8rem .5rem;} -.paypal-other-header > .admin__collapsible-block > a {color: #007bdb !important; text-align: right;} .payments-other-header > .admin__collapsible-block > a, -.paypal-recommended-header > .admin__collapsible-block > a {display: inline-block;} +.paypal-recommended-header > .admin__collapsible-block > a, +.paypal-other-header > .admin__collapsible-block > a {display: inline-block;} .payments-other-header > .admin__collapsible-block > a::before, .paypal-recommended-header > .admin__collapsible-block > a::before {content: '' !important; width: 0; height: 0; border-color: transparent; border-top-color: #000; border-style: solid; border-width: .8rem .5rem 0 .5rem; margin-top:1px; transition: all .2s linear;} .payments-other-header > .admin__collapsible-block > a.open::before, diff --git a/app/code/Magento/PaypalCaptcha/registration.php b/app/code/Magento/PaypalCaptcha/registration.php index 4dac0582a6d1b..79f8a56411078 100644 --- a/app/code/Magento/PaypalCaptcha/registration.php +++ b/app/code/Magento/PaypalCaptcha/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_PaypalCaptcha', __DIR__); diff --git a/app/code/Magento/PaypalGraphQl/registration.php b/app/code/Magento/PaypalGraphQl/registration.php index 2e1676bc087cc..d3eb5dd3095a6 100644 --- a/app/code/Magento/PaypalGraphQl/registration.php +++ b/app/code/Magento/PaypalGraphQl/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_PaypalGraphQl', __DIR__); diff --git a/app/code/Magento/Persistent/Model/QuoteManager.php b/app/code/Magento/Persistent/Model/QuoteManager.php index 8ae22e4c26c6f..ebddfe5a43600 100644 --- a/app/code/Magento/Persistent/Model/QuoteManager.php +++ b/app/code/Magento/Persistent/Model/QuoteManager.php @@ -89,7 +89,6 @@ public function setGuest($checkQuote = false) ->setCustomerLastname(null) ->setCustomerGroupId(\Magento\Customer\Api\Data\GroupInterface::NOT_LOGGED_IN_ID) ->setIsPersistent(false) - ->setCustomerIsGuest(true) ->removeAllAddresses(); //Create guest addresses $quote->getShippingAddress(); @@ -121,7 +120,6 @@ public function convertCustomerCartToGuest() ->setCustomerEmail(null) ->setCustomerFirstname(null) ->setCustomerLastname(null) - ->setCustomerGroupId(\Magento\Customer\Api\Data\GroupInterface::NOT_LOGGED_IN_ID) ->setIsPersistent(false); $quote->getAddressesCollection()->walk('setCustomerAddressId', ['customerAddressId' => null]); $quote->getAddressesCollection()->walk('setCustomerId', ['customerId' => null]); diff --git a/app/code/Magento/Persistent/Test/Mftf/ActionGroup/CustomerLoginOnStorefrontWithRememberMeCheckedActionGroup.xml b/app/code/Magento/Persistent/Test/Mftf/ActionGroup/CustomerLoginOnStorefrontWithRememberMeCheckedActionGroup.xml new file mode 100644 index 0000000000000..6a28829034611 --- /dev/null +++ b/app/code/Magento/Persistent/Test/Mftf/ActionGroup/CustomerLoginOnStorefrontWithRememberMeCheckedActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CustomerLoginOnStorefrontWithRememberMeCheckedActionGroup" extends="LoginToStorefrontActionGroup"> + <annotations> + <description>EXTENDS: LoginToStorefrontActionGroup. Checks the 'Remember Me' checkbox.</description> + </annotations> + + <checkOption selector="{{StorefrontCustomerSignInFormSection.rememberMe}}" before="clickSignInAccountButton" stepKey="checkRememberMe"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Persistent/Test/Mftf/ActionGroup/CustomerLoginOnStorefrontWithRememberMeUnCheckedActionGroup.xml b/app/code/Magento/Persistent/Test/Mftf/ActionGroup/CustomerLoginOnStorefrontWithRememberMeUnCheckedActionGroup.xml new file mode 100644 index 0000000000000..080dde1f60f93 --- /dev/null +++ b/app/code/Magento/Persistent/Test/Mftf/ActionGroup/CustomerLoginOnStorefrontWithRememberMeUnCheckedActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CustomerLoginOnStorefrontWithRememberMeUnCheckedActionGroup" extends="LoginToStorefrontActionGroup"> + <annotations> + <description>EXTENDS: LoginToStorefrontActionGroup. Uncheck the 'Remember Me' checkbox.</description> + </annotations> + + <uncheckOption selector="{{StorefrontCustomerSignInFormSection.rememberMe}}" before="clickSignInAccountButton" stepKey="unCheckRememberMe"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontAssertPersistentCustomerWelcomeMessageActionGroup.xml b/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontAssertPersistentCustomerWelcomeMessageActionGroup.xml new file mode 100644 index 0000000000000..54105dc0d31ad --- /dev/null +++ b/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontAssertPersistentCustomerWelcomeMessageActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAssertPersistentCustomerWelcomeMessageActionGroup"> + <arguments> + <argument name="customerFullName" type="string" /> + </arguments> + <waitForPageLoad stepKey="waitForPageLoad"/> + <see selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" userInput="Welcome, {{customerFullName}}! Not you?" stepKey="verifyMessage" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontAssertPersistentCustomerWelcomeMessageNotPresentActionGroup.xml b/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontAssertPersistentCustomerWelcomeMessageNotPresentActionGroup.xml new file mode 100644 index 0000000000000..3aa0c4d9c0de5 --- /dev/null +++ b/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontAssertPersistentCustomerWelcomeMessageNotPresentActionGroup.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="StorefrontAssertPersistentCustomerWelcomeMessageNotPresentActionGroup" extends="StorefrontAssertPersistentCustomerWelcomeMessageActionGroup"> + <remove keyForRemoval="verifyMessage"/> + <dontSee selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" userInput="Welcome, {{customerFullName}}! Not you?" stepKey="dontSeeWelcomeMessageNotYou"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontCreateCustomerOnRegisterPageDoNotRememberMeActionGroup.xml b/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontCreateCustomerOnRegisterPageDoNotRememberMeActionGroup.xml new file mode 100644 index 0000000000000..9c8f04bf6292a --- /dev/null +++ b/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontCreateCustomerOnRegisterPageDoNotRememberMeActionGroup.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="StorefrontCreateCustomerOnRegisterPageDoNotRememberMeActionGroup" extends="SignUpNewUserFromStorefrontActionGroup"> + <!--- Assume we are on customer registration page. --> + <remove keyForRemoval="amOnStorefrontPage"/> + <remove keyForRemoval="clickOnCreateAccountLink"/> + <uncheckOption selector="{{StorefrontCustomerSignInFormSection.rememberMe}}" before="clickCreateAccountButton" stepKey="unCheckRememberMe"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontCustomerActionGroup.xml b/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontCustomerActionGroup.xml deleted file mode 100644 index 70d5be2dcf23f..0000000000000 --- a/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontCustomerActionGroup.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="CustomerLoginOnStorefrontWithRememberMeChecked" extends="LoginToStorefrontActionGroup"> - <annotations> - <description>EXTENDS: LoginToStorefrontActionGroup. Checks the 'Remember Me' checkbox.</description> - </annotations> - - <checkOption selector="{{StorefrontCustomerSignInFormSection.rememberMe}}" before="clickSignInAccountButton" stepKey="checkRememberMe"/> - </actionGroup> - - <actionGroup name="CustomerLoginOnStorefrontWithRememberMeUnChecked" extends="LoginToStorefrontActionGroup"> - <annotations> - <description>EXTENDS: LoginToStorefrontActionGroup. Uncheck the 'Remember Me' checkbox.</description> - </annotations> - - <uncheckOption selector="{{StorefrontCustomerSignInFormSection.rememberMe}}" before="clickSignInAccountButton" stepKey="unCheckRememberMe"/> - </actionGroup> - - <actionGroup name="StorefrontRegisterCustomerRememberMe" extends="SignUpNewUserFromStorefrontActionGroup"> - <!--- Assume we are on customer registration page. --> - <remove keyForRemoval="amOnStorefrontPage"/> - <remove keyForRemoval="clickOnCreateAccountLink"/> - <checkOption selector="{{StorefrontCustomerSignInFormSection.rememberMe}}" before="clickCreateAccountButton" stepKey="checkRememberMe"/> - </actionGroup> - - <actionGroup name="StorefrontCreateCustomerOnRegisterPageDoNotRememberMe" extends="SignUpNewUserFromStorefrontActionGroup"> - <!--- Assume we are on customer registration page. --> - <remove keyForRemoval="amOnStorefrontPage"/> - <remove keyForRemoval="clickOnCreateAccountLink"/> - <uncheckOption selector="{{StorefrontCustomerSignInFormSection.rememberMe}}" before="clickCreateAccountButton" stepKey="unCheckRememberMe"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontPersistentAssertCustomerWelcomeMessageActionGroup.xml b/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontPersistentAssertCustomerWelcomeMessageActionGroup.xml deleted file mode 100644 index b081f5c3eb0c8..0000000000000 --- a/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontPersistentAssertCustomerWelcomeMessageActionGroup.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontAssertPersistentCustomerWelcomeMessageActionGroup"> - <arguments> - <argument name="customerFullName" type="string" /> - </arguments> - <waitForPageLoad stepKey="waitForPageLoad"/> - <see selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" userInput="Welcome, {{customerFullName}}! Not you?" stepKey="verifyMessage" /> - </actionGroup> - - <actionGroup name="StorefrontAssertPersistentCustomerWelcomeMessageNotPresentActionGroup" extends="StorefrontAssertPersistentCustomerWelcomeMessageActionGroup"> - <remove keyForRemoval="verifyMessage"/> - <dontSee selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" userInput="Welcome, {{customerFullName}}! Not you?" stepKey="dontSeeWelcomeMessageNotYou"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontRegisterCustomerRememberMeActionGroup.xml b/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontRegisterCustomerRememberMeActionGroup.xml new file mode 100644 index 0000000000000..44cdb341b2b10 --- /dev/null +++ b/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontRegisterCustomerRememberMeActionGroup.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="StorefrontRegisterCustomerRememberMeActionGroup" extends="SignUpNewUserFromStorefrontActionGroup"> + <!--- Assume we are on customer registration page. --> + <remove keyForRemoval="amOnStorefrontPage"/> + <remove keyForRemoval="clickOnCreateAccountLink"/> + <checkOption selector="{{StorefrontCustomerSignInFormSection.rememberMe}}" before="clickCreateAccountButton" stepKey="checkRememberMe"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/CheckShoppingCartBehaviorAfterSessionExpiredTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/CheckShoppingCartBehaviorAfterSessionExpiredTest.xml index c66a2979aa7f5..6d35319172823 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Test/CheckShoppingCartBehaviorAfterSessionExpiredTest.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Test/CheckShoppingCartBehaviorAfterSessionExpiredTest.xml @@ -43,10 +43,10 @@ <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> </after> <!-- Add simple product to cart --> - <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart1"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart1"> <argument name="product" value="$$createProduct$$"/> </actionGroup> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <!--Reset cookies and refresh the page--> <resetCookie userInput="PHPSESSID" stepKey="resetCookieForCart"/> <reloadPage stepKey="reloadPage"/> diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/GuestCheckoutWithEnabledPersistentTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/GuestCheckoutWithEnabledPersistentTest.xml index a383fd4505bd6..43390598f7cb3 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Test/GuestCheckoutWithEnabledPersistentTest.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Test/GuestCheckoutWithEnabledPersistentTest.xml @@ -31,7 +31,7 @@ <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> </after> <!-- Add simple product to cart --> - <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart1"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart1"> <argument name="product" value="$$createProduct$$"/> </actionGroup> <!-- Navigate to checkout --> diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml index d508ebaa26885..e81694e044f52 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Test/ShippingQuotePersistedForGuestTest.xml @@ -40,12 +40,12 @@ <actionGroup ref="logout" stepKey="logout"/> </after> <!--Step 1: Login as a Customer with remember me checked--> - <actionGroup ref="CustomerLoginOnStorefrontWithRememberMeChecked" stepKey="loginToStorefrontAccountWithRememberMeChecked"> + <actionGroup ref="CustomerLoginOnStorefrontWithRememberMeCheckedActionGroup" stepKey="loginToStorefrontAccountWithRememberMeChecked"> <argument name="Customer" value="$$createCustomer$$"/> </actionGroup> <!--Step 2: Open the Product Page and add the product to shopping cart--> <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToProductPageAsLoggedUser"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProductToCartAsLoggedUser"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProductToCartAsLoggedUser"> <argument name="productName" value="$$createProduct.name$$"/> </actionGroup> <!--Step 3: Log out, reset persistent cookie and go to homepage--> @@ -56,10 +56,10 @@ <waitForPageLoad stepKey="waitHomePageLoadAfterResetCookie"/> <!--Step 4: Add the product to shopping cart and open cart--> <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="navigateToProductPageAsGuestUser"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="addProductToCartAsGuestUser"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProductToCartAsGuestUser"> <argument name="productName" value="$$createProduct.name$$"/> </actionGroup> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartBeforeChangeShippingAndTaxSection"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartBeforeChangeShippingAndTaxSection"/> <!--Step 5: Open Estimate Shipping and Tax block and fill the sections--> <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingAndTax" /> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> @@ -69,7 +69,7 @@ <!--Step 6: Go to Homepage--> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToHomePageAfterChangingShippingAndTaxSection"/> <!--Step 7: Go to shopping cart and check "Estimate Shipping and Tax" fields values are saved--> - <actionGroup ref="clickViewAndEditCartFromMiniCart" after="goToHomePageAfterChangingShippingAndTaxSection" stepKey="goToShoppingCartAfterChangingShippingAndTaxSection"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" after="goToHomePageAfterChangingShippingAndTaxSection" stepKey="goToShoppingCartAfterChangingShippingAndTaxSection"/> <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingAndTaxAfterChanging" /> <seeOptionIsSelected selector="{{CheckoutCartSummarySection.country}}" userInput="{{US_Address_CA.country}}" stepKey="checkCustomerCountry" /> <seeOptionIsSelected selector="{{CheckoutCartSummarySection.stateProvince}}" userInput="{{US_Address_CA.state}}" stepKey="checkCustomerRegion" /> diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontCorrectWelcomeMessageAfterCustomerIsLoggedOutTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontCorrectWelcomeMessageAfterCustomerIsLoggedOutTest.xml index 61710cbc98082..b41cad61c93a5 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontCorrectWelcomeMessageAfterCustomerIsLoggedOutTest.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontCorrectWelcomeMessageAfterCustomerIsLoggedOutTest.xml @@ -43,7 +43,7 @@ <deleteData createDataKey="createCustomerForPersistent" stepKey="deleteCustomerForPersistent"/> </after> <!--Login as a Customer with remember me unchecked--> - <actionGroup ref="CustomerLoginOnStorefrontWithRememberMeUnChecked" stepKey="loginToStorefrontAccountWithRememberMeUnchecked"> + <actionGroup ref="CustomerLoginOnStorefrontWithRememberMeUnCheckedActionGroup" stepKey="loginToStorefrontAccountWithRememberMeUnchecked"> <argument name="Customer" value="$$createCustomer$$"/> </actionGroup> @@ -60,7 +60,7 @@ stepKey="seeDefaultWelcomeMessage"/> <!--Login as a Customer with remember me checked--> - <actionGroup ref="CustomerLoginOnStorefrontWithRememberMeChecked" stepKey="loginToStorefrontAccountWithRememberMeChecked"> + <actionGroup ref="CustomerLoginOnStorefrontWithRememberMeCheckedActionGroup" stepKey="loginToStorefrontAccountWithRememberMeChecked"> <argument name="Customer" value="$$createCustomerForPersistent$$"/> </actionGroup> <!--Check customer name and last name in welcome message--> diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyShoppingCartPersistenceUnderLongTermCookieTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyShoppingCartPersistenceUnderLongTermCookieTest.xml index e50fc4af83107..35f4fd88f2b09 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyShoppingCartPersistenceUnderLongTermCookieTest.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyShoppingCartPersistenceUnderLongTermCookieTest.xml @@ -54,7 +54,7 @@ <actionGroup ref="StorefrontAssertPersistentRegistrationPageFields" stepKey="assertPersistentRegistrationPageFields"/> <!-- 2. Fill fields for registration, set password and unselect the Remember Me checkbox--> - <actionGroup ref="StorefrontCreateCustomerOnRegisterPageDoNotRememberMe" stepKey="registrationJohnSmithCustomer"> + <actionGroup ref="StorefrontCreateCustomerOnRegisterPageDoNotRememberMeActionGroup" stepKey="registrationJohnSmithCustomer"> <argument name="Customer" value="John_Smith_Customer"/> </actionGroup> <!--Check customer name and last name in welcome message--> @@ -64,10 +64,10 @@ </actionGroup> <!-- 3. Put Simple Product 1 into Shopping Cart --> - <actionGroup ref="AddSimpleProductToCart" stepKey="addSimple1ProductToCartForJohnSmithCustomer"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addSimple1ProductToCartForJohnSmithCustomer"> <argument name="product" value="$$createSimple1$$"/> </actionGroup> - <actionGroup ref="assertOneProductNameInMiniCart" stepKey="checkSimple1InMiniCartForJohnSmithCustomer"> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="checkSimple1InMiniCartForJohnSmithCustomer"> <argument name="productName" value="$$createSimple1.name$$"/> </actionGroup> @@ -79,11 +79,11 @@ <actionGroup ref="StorefrontAssertPersistentCustomerWelcomeMessageNotPresentActionGroup" stepKey="dontSeeWelcomeJohnSmithCustomerNotYouMessage"> <argument name="customerFullName" value="{{John_Smith_Customer.fullname}}"/> </actionGroup> - <actionGroup ref="assertMiniCartEmpty" stepKey="assertMiniCartEmptyAfterJohnSmithSignOut" /> + <actionGroup ref="AssertMiniCartEmptyActionGroup" stepKey="assertMiniCartEmptyAfterJohnSmithSignOut" /> <!-- 5. Click the Create an Account link again and fill fields for registration of another customer, set password and check the Remember Me checkbox --> <amOnPage url="{{StorefrontCustomerCreatePage.url}}" stepKey="amOnCustomerAccountCreatePage"/> - <actionGroup ref="StorefrontRegisterCustomerRememberMe" stepKey="registrationJohnDoeCustomer"> + <actionGroup ref="StorefrontRegisterCustomerRememberMeActionGroup" stepKey="registrationJohnDoeCustomer"> <argument name="Customer" value="Simple_Customer_Without_Address"/> </actionGroup> <!--Check customer name and last name in welcome message--> @@ -92,11 +92,11 @@ <argument name="customerFullName" value="{{Simple_Customer_Without_Address.fullname}}"/> </actionGroup> <!-- 6. Add Simple Product 1 to Shopping Cart --> - <actionGroup ref="AddSimpleProductToCart" stepKey="addSimple1ProductToCartForJohnDoeCustomer"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addSimple1ProductToCartForJohnDoeCustomer"> <argument name="product" value="$$createSimple1$$"/> </actionGroup> <see selector="{{StorefrontMinicartSection.productCount}}" userInput="1" stepKey="miniCartContainsOneProductForJohnDoeCustomer"/> - <actionGroup ref="assertOneProductNameInMiniCart" stepKey="checkSimple1InMiniCartForJohnDoeCustomer"> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="checkSimple1InMiniCartForJohnDoeCustomer"> <argument name="productName" value="$$createSimple1.name$$"/> </actionGroup> @@ -110,22 +110,22 @@ <waitForText selector="{{StorefrontCMSPageSection.mainContent}}" userInput="CMS homepage content goes here." stepKey="waitForLoadContentMessageOnHomePage"/> <waitForElementVisible selector="{{StorefrontMinicartSection.productCount}}" stepKey="waitForCartCounterVisible"/> <see selector="{{StorefrontMinicartSection.productCount}}" userInput="1" stepKey="miniCartContainsOneProductForGuest"/> - <actionGroup ref="assertOneProductNameInMiniCart" stepKey="checkSimple1InMiniCartForGuestCustomer"> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="checkSimple1InMiniCartForGuestCustomer"> <argument name="productName" value="$$createSimple1.name$$"/> </actionGroup> <!-- 8. Go to Shopping Cart and verify Simple Product 1 is present there --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCart" /> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCart" /> <see selector="{{CheckoutCartProductSection.productName}}" userInput="$$createSimple1.name$$" stepKey="checkSimple1InShoppingCart"/> <!-- 9. Add Simple Product 2 to Shopping Cart --> - <actionGroup ref="AddSimpleProductToCart" stepKey="addSimple2ProductToCartForGuest"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addSimple2ProductToCartForGuest"> <argument name="product" value="$$createSimple2$$"/> </actionGroup> - <actionGroup ref="assertOneProductNameInMiniCart" stepKey="checkSimple1InMiniCartForGuestCustomerSecondTime"> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="checkSimple1InMiniCartForGuestCustomerSecondTime"> <argument name="productName" value="$$createSimple1.name$$"/> </actionGroup> - <actionGroup ref="assertOneProductNameInMiniCart" stepKey="checkSimple2InMiniCartForGuestCustomer"> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="checkSimple2InMiniCartForGuestCustomer"> <argument name="productName" value="$$createSimple2.name$$"/> </actionGroup> <see selector="{{StorefrontMinicartSection.productCount}}" userInput="2" stepKey="miniCartContainsTwoProductForGuest"/> @@ -140,10 +140,10 @@ <argument name="Customer" value="Simple_Customer_Without_Address"/> </actionGroup> <see selector="{{StorefrontMinicartSection.productCount}}" userInput="2" stepKey="miniCartContainsTwoProductForJohnDoeCustomer"/> - <actionGroup ref="assertOneProductNameInMiniCart" stepKey="checkSimple1InMiniCartForJohnDoeCustomerSecondTime"> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="checkSimple1InMiniCartForJohnDoeCustomerSecondTime"> <argument name="productName" value="$$createSimple1.name$$"/> </actionGroup> - <actionGroup ref="assertOneProductNameInMiniCart" stepKey="checkSimple2InMiniCartForJohnDoeCustomer"> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="checkSimple2InMiniCartForJohnDoeCustomer"> <argument name="productName" value="$$createSimple2.name$$"/> </actionGroup> @@ -154,6 +154,6 @@ <waitForText selector="{{StorefrontCMSPageSection.mainContent}}" userInput="CMS homepage content goes here." stepKey="waitForLoadMainContentMessageOnHomePage"/> <click selector="{{StorefrontPanelHeaderSection.notYouLink}}" stepKey="clickOnNotYouLink" /> <waitForPageLoad stepKey="waitForCustomerLoginPageLoad"/> - <actionGroup ref="assertMiniCartEmpty" stepKey="assertMiniCartEmptyAfterJohnDoeSignOut" /> + <actionGroup ref="AssertMiniCartEmptyActionGroup" stepKey="assertMiniCartEmptyAfterJohnDoeSignOut" /> </test> </tests> diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyThatInformationAboutViewingComparisonWishlistIsPersistedUnderLongTermCookieTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyThatInformationAboutViewingComparisonWishlistIsPersistedUnderLongTermCookieTest.xml index dc6f87bef0ba8..1b3a996b34e24 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyThatInformationAboutViewingComparisonWishlistIsPersistedUnderLongTermCookieTest.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyThatInformationAboutViewingComparisonWishlistIsPersistedUnderLongTermCookieTest.xml @@ -70,10 +70,10 @@ <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="checkWelcomeMessage"/> <!--Open the details page of Simple Product 1, Simple Product 2 and add to cart, get to the category--> - <actionGroup ref="AddSimpleProductToCart" stepKey="addSimpleProductProductToCart"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addSimpleProductProductToCart"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> - <actionGroup ref="AddSimpleProductToCart" stepKey="addSecondSimpleProductProductToCart"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addSecondSimpleProductProductToCart"> <argument name="product" value="$$createSecondSimpleProduct$$"/> </actionGroup> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.custom_attributes[url_key]$$)}}" stepKey="openCategoryPageAfterAddedProductToCart"/> @@ -95,10 +95,10 @@ </actionGroup> <!--The My Wishlist widget displays Simple Product 1 and Simple Product 2--> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.custom_attributes[url_key]$$)}}" stepKey="openCategoryPageToCheckProductsInWishlistSidebar"/> - <actionGroup ref="StorefrontCustomerCheckProductInWishlistSidebar" stepKey="checkSimpleProductInWishlistSidebar"> + <actionGroup ref="StorefrontCustomerCheckProductInWishlistSidebarActionGroup" stepKey="checkSimpleProductInWishlistSidebar"> <argument name="productVar" value="$$createSimpleProduct$$"/> </actionGroup> - <actionGroup ref="StorefrontCustomerCheckProductInWishlistSidebar" stepKey="checkSecondSimpleProductInWishlistSidebar"> + <actionGroup ref="StorefrontCustomerCheckProductInWishlistSidebarActionGroup" stepKey="checkSecondSimpleProductInWishlistSidebar"> <argument name="productVar" value="$$createSecondSimpleProduct$$"/> </actionGroup> @@ -150,7 +150,7 @@ <actionGroup ref="StorefrontAssertProductInRecentlyViewedWidgetActionGroup" stepKey="checkSimpleProductInRecentlyViewedWidgetAfterLogout"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> - <actionGroup ref="StorefrontCustomerCheckProductInWishlistSidebar" stepKey="checkSimpleProductInWishlistSidebarAfterLogout"> + <actionGroup ref="StorefrontCustomerCheckProductInWishlistSidebarActionGroup" stepKey="checkSimpleProductInWishlistSidebarAfterLogout"> <argument name="productVar" value="$$createSimpleProduct$$"/> </actionGroup> <actionGroup ref="StorefrontAssertProductInRecentlyComparedWidgetActionGroup" stepKey="checkSimpleProductInRecentlyComparedWidgetAfterLogout"> @@ -177,7 +177,7 @@ <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.custom_attributes[url_key]$$)}}" stepKey="openCategoryPageToCheckWidgets"/> <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="checkWelcomeMessageAfterLogin"/> - <actionGroup ref="StorefrontCustomerCheckProductInWishlistSidebar" stepKey="checkSimpleProductNameInWishlistSidebarAfterLogin"> + <actionGroup ref="StorefrontCustomerCheckProductInWishlistSidebarActionGroup" stepKey="checkSimpleProductNameInWishlistSidebarAfterLogin"> <argument name="productVar" value="$$createSimpleProduct$$"/> </actionGroup> <actionGroup ref="StorefrontAssertProductInRecentlyViewedWidgetActionGroup" stepKey="checkSimpleProductInRecentlyViewedWidgetAfterLogin"> diff --git a/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php b/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php new file mode 100644 index 0000000000000..a95f5530bb800 --- /dev/null +++ b/app/code/Magento/Persistent/Test/Unit/CustomerData/PersistentTest.php @@ -0,0 +1,121 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Persistent\Test\Unit\CustomerData; + +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Helper\View; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Persistent\CustomerData\Persistent; +use Magento\Persistent\Helper\Session; +use Magento\Persistent\Model\Session as PersistentSession; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class PersistentTest extends TestCase +{ + /** + * Stub customer id + */ + private const STUB_CUSTOMER_ID = 1; + + /** + * Stub customer name + */ + private const STUB_CUSTOMER_NAME = 'Adam John'; + + /** + * @var Persistent + */ + private $customerData; + + /** + * @var Session|MockObject + */ + private $persistentSessionHelperMock; + + /** + * @var View|MockObject + */ + private $customerViewHelperMock; + + /** + * @var CustomerRepositoryInterface|MockObject + */ + private $customerRepositoryMock; + + /** + * Setup environment for test + */ + protected function setUp() + { + $this->persistentSessionHelperMock = $this->createMock(Session::class); + $this->customerViewHelperMock = $this->createMock(View::class); + $this->customerRepositoryMock = $this->createMock(CustomerRepositoryInterface::class); + + $objectManager = new ObjectManagerHelper($this); + + $this->customerData = $objectManager->getObject( + Persistent::class, + [ + 'persistentSession' => $this->persistentSessionHelperMock, + 'customerViewHelper' => $this->customerViewHelperMock, + 'customerRepository' => $this->customerRepositoryMock + ] + ); + } + + /** + * Test getSectionData() when disable persistent + */ + public function testGetSectionDataWhenDisablePersistent() + { + $this->persistentSessionHelperMock->method('isPersistent')->willReturn(false); + + $this->assertEquals([], $this->customerData->getSectionData()); + } + + /** + * Test getSectionData() when customer doesn't login + */ + public function testGetSectionDataWhenCustomerNotLoggedInReturnsEmptyArray() + { + $this->persistentSessionHelperMock->method('isPersistent')->willReturn(true); + + $persistentSessionMock = $this->createPartialMock(PersistentSession::class, ['getCustomerId']); + $persistentSessionMock->method('getCustomerId')->willReturn(null); + $this->persistentSessionHelperMock->method('getSession')->willReturn($persistentSessionMock); + + $this->assertEquals([], $this->customerData->getSectionData()); + } + + /** + * Test getSectionData() when customer login and enable persistent + */ + public function testGetSectionDataCustomerLoginAndEnablePersistent() + { + $this->persistentSessionHelperMock->method('isPersistent')->willReturn(true); + + $persistentSessionMock = $this->createPartialMock(PersistentSession::class, ['getCustomerId']); + $persistentSessionMock->method('getCustomerId')->willReturn(self::STUB_CUSTOMER_ID); + $this->persistentSessionHelperMock->method('getSession')->willReturn($persistentSessionMock); + + $customerMock = $this->createMock(CustomerInterface::class); + $this->customerRepositoryMock->method('getById')->with(self::STUB_CUSTOMER_ID)->willReturn($customerMock); + $this->customerViewHelperMock->method('getCustomerName')->with($customerMock) + ->willReturn(self::STUB_CUSTOMER_NAME); + + $this->assertEquals( + [ + 'fullname' => self::STUB_CUSTOMER_NAME + ], + $this->customerData->getSectionData() + ); + } +} diff --git a/app/code/Magento/Persistent/Test/Unit/Helper/SessionTest.php b/app/code/Magento/Persistent/Test/Unit/Helper/SessionTest.php new file mode 100644 index 0000000000000..7009ff4d33c0b --- /dev/null +++ b/app/code/Magento/Persistent/Test/Unit/Helper/SessionTest.php @@ -0,0 +1,200 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Persistent\Test\Unit\Helper; + +use Magento\Persistent\Helper\Session as SessionHelper; +use Magento\Framework\App\Helper\Context; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Persistent\Helper\Data as DataHelper; +use Magento\Persistent\Model\SessionFactory; +use Magento\Persistent\Model\Session; + +/** + * Class \Magento\Persistent\Test\Unit\Helper\SessionTest + */ +class SessionTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject|Context + */ + private $context; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|SessionHelper + */ + private $helper; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|DataHelper + */ + private $dataHelper; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|CheckoutSession + */ + private $checkoutSession; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|SessionFactory + */ + private $sessionFactory; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|Session + */ + private $session; + + /** + * Setup environment + */ + protected function setUp() + { + $this->context = $this->getMockBuilder(Context::class) + ->disableOriginalConstructor() + ->getMock(); + $this->dataHelper = $this->getMockBuilder(DataHelper::class) + ->disableOriginalConstructor() + ->getMock(); + $this->checkoutSession = $this->getMockBuilder(CheckoutSession::class) + ->disableOriginalConstructor() + ->getMock(); + $this->scopeConfig = $this->getMockBuilder(ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->sessionFactory = $this->getMockBuilder(SessionFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->session = $this->getMockBuilder(Session::class) + ->disableOriginalConstructor() + ->getMock(); + $this->sessionFactory->expects($this->any())->method('create')->willReturn($this->session); + + $this->helper = $this->getMockBuilder(SessionHelper::class) + ->setMethods(['getSession']) + ->setConstructorArgs( + [ + 'context' => $this->context, + 'persistentData' => $this->dataHelper, + 'checkoutSession' => $this->checkoutSession, + 'sessionFactory' => $this->sessionFactory + ] + ) + ->getMock(); + } + + /** + * Test isPersistent() function + * + * @param int|null $id + * @param boolean $isEnabled + * @param boolean $expected + * @dataProvider isPersistentDataProvider + */ + public function testIsPersistent($id, $isEnabled, $expected) + { + $this->session->expects($this->any())->method('getId') + ->willReturn($id); + $this->helper->expects($this->any())->method('getSession') + ->willReturn($this->session); + $this->dataHelper->expects($this->any())->method('isEnabled') + ->willReturn($isEnabled); + + $this->assertEquals($expected, $this->helper->isPersistent()); + } + + /** + * Data Provider for test isPersistent() + * + * @return array + */ + public function isPersistentDataProvider() + { + return [ + 'session_id_and_enable_persistent' => [ + 1, + true, + true + ], + 'no_session_id_and_enable_persistent' => [ + null, + true, + false + ] + ]; + } + + /** + * Test isRememberMeChecked() function + * + * @param boolean|null $checked + * @param boolean $isEnabled + * @param boolean $isRememberMeEnabled + * @param boolean $isRememberMeCheckedDefault + * @param boolean $expected + * @dataProvider isRememberMeCheckedProvider + */ + public function testIsRememberMeChecked( + $checked, + $isEnabled, + $isRememberMeEnabled, + $isRememberMeCheckedDefault, + $expected + ) { + $this->helper->setRememberMeChecked($checked); + $this->dataHelper->expects($this->any())->method('isEnabled') + ->willReturn($isEnabled); + $this->dataHelper->expects($this->any())->method('isRememberMeEnabled') + ->willReturn($isRememberMeEnabled); + $this->dataHelper->expects($this->any())->method('isRememberMeCheckedDefault') + ->willReturn($isRememberMeCheckedDefault); + + $this->assertEquals($expected, $this->helper->isRememberMeChecked()); + } + + /** + * Data Provider for test isRememberMeChecked() + * + * @return array + */ + public function isRememberMeCheckedProvider() + { + return [ + 'enable_all_config' => [ + null, + true, + true, + true, + true + ], + 'at_least_once_disabled' => [ + null, + false, + true, + true, + false + ], + 'set_remember_me_checked_false' => [ + false, + true, + true, + true, + false + ], + 'set_remember_me_checked_true' => [ + true, + false, + true, + true, + true + ] + ]; + } +} diff --git a/app/code/Magento/Persistent/Test/Unit/Model/QuoteManagerTest.php b/app/code/Magento/Persistent/Test/Unit/Model/QuoteManagerTest.php index e5de8e7d4aade..afabf18079fbc 100644 --- a/app/code/Magento/Persistent/Test/Unit/Model/QuoteManagerTest.php +++ b/app/code/Magento/Persistent/Test/Unit/Model/QuoteManagerTest.php @@ -55,7 +55,9 @@ protected function setUp() { $this->persistentSessionMock = $this->createMock(\Magento\Persistent\Helper\Session::class); $this->sessionMock = - $this->createPartialMock(\Magento\Persistent\Model\Session::class, [ + $this->createPartialMock( + \Magento\Persistent\Model\Session::class, + [ 'setLoadInactive', 'setCustomerData', 'clearQuote', @@ -63,7 +65,8 @@ protected function setUp() 'getQuote', 'removePersistentCookie', '__wakeup', - ]); + ] + ); $this->persistentDataMock = $this->createMock(\Magento\Persistent\Helper\Data::class); $this->checkoutSessionMock = $this->createMock(\Magento\Checkout\Model\Session::class); @@ -71,7 +74,9 @@ protected function setUp() $this->createMock(\Magento\Eav\Model\Entity\Collection\AbstractCollection::class); $this->quoteRepositoryMock = $this->createMock(\Magento\Quote\Api\CartRepositoryInterface::class); - $this->quoteMock = $this->createPartialMock(\Magento\Quote\Model\Quote::class, [ + $this->quoteMock = $this->createPartialMock( + \Magento\Quote\Model\Quote::class, + [ 'getId', 'getIsPersistent', 'getPaymentsCollection', @@ -90,7 +95,8 @@ protected function setUp() 'getIsActive', 'getCustomerId', '__wakeup' - ]); + ] + ); $this->model = new QuoteManager( $this->persistentSessionMock, @@ -258,21 +264,22 @@ public function testConvertCustomerCartToGuest() ->method('setCustomerFirstname')->with(null)->willReturn($this->quoteMock); $this->quoteMock->expects($this->once()) ->method('setCustomerLastname')->with(null)->willReturn($this->quoteMock); - $this->quoteMock->expects($this->once())->method('setCustomerGroupId') - ->with(\Magento\Customer\Model\GroupManagement::NOT_LOGGED_IN_ID) + $this->quoteMock->expects($this->never())->method('setCustomerGroupId') ->willReturn($this->quoteMock); $this->quoteMock->expects($this->once()) ->method('setIsPersistent')->with(false)->willReturn($this->quoteMock); $this->quoteMock->expects($this->exactly(3)) ->method('getAddressesCollection')->willReturn($this->abstractCollectionMock); - $this->abstractCollectionMock->expects($this->exactly(3))->method('walk')->with($this->logicalOr( - $this->equalTo('setCustomerAddressId'), - $this->equalTo($addressArgs), - $this->equalTo('setCustomerId'), - $this->equalTo($customerIdArgs), - $this->equalTo('setEmail'), - $this->equalTo($emailArgs) - )); + $this->abstractCollectionMock->expects($this->exactly(3))->method('walk')->with( + $this->logicalOr( + $this->equalTo('setCustomerAddressId'), + $this->equalTo($addressArgs), + $this->equalTo('setCustomerId'), + $this->equalTo($customerIdArgs), + $this->equalTo('setEmail'), + $this->equalTo($emailArgs) + ) + ); $this->quoteMock->expects($this->once())->method('collectTotals')->willReturn($this->quoteMock); $this->persistentSessionMock->expects($this->once()) ->method('getSession')->willReturn($this->sessionMock); diff --git a/app/code/Magento/Persistent/registration.php b/app/code/Magento/Persistent/registration.php index bc77434391517..4ff1c96a45ae8 100644 --- a/app/code/Magento/Persistent/registration.php +++ b/app/code/Magento/Persistent/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Persistent', __DIR__); diff --git a/app/code/Magento/ProductAlert/Test/Unit/Helper/DataTest.php b/app/code/Magento/ProductAlert/Test/Unit/Helper/DataTest.php new file mode 100644 index 0000000000000..122d785e79599 --- /dev/null +++ b/app/code/Magento/ProductAlert/Test/Unit/Helper/DataTest.php @@ -0,0 +1,162 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ProductAlert\Test\Unit\Helper; + +use PHPUnit\Framework\TestCase; +use Magento\ProductAlert\Helper\Data as HelperData; +use Magento\Framework\App\Helper\Context; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Framework\UrlInterface; +use Magento\Framework\Url\EncoderInterface; +use Magento\Catalog\Model\Product; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\ProductAlert\Model\Observer; +use Magento\Store\Model\ScopeInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\ProductAlert\Block\Email\Price; +use Magento\Framework\Exception\LocalizedException; + +class DataTest extends TestCase +{ + /** + * @var HelperData + */ + private $helper; + + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var Context|\PHPUnit_Framework_MockObject_MockObject + */ + private $contextMock; + + /** + * @var UrlInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $urlBuilderMock; + + /** + * @var EncoderInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $encoderMock; + + /** + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfigMock; + + /** + * @var LayoutInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $layoutMock; + + /** + * Setup environment for testing + */ + protected function setUp() + { + $this->contextMock = $this->createMock(Context::class); + $this->urlBuilderMock = $this->createMock(UrlInterface::class); + $this->encoderMock = $this->createMock(EncoderInterface::class); + $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + $this->layoutMock = $this->createMock(LayoutInterface::class); + $this->contextMock->expects($this->once())->method('getUrlBuilder')->willReturn($this->urlBuilderMock); + $this->contextMock->expects($this->once())->method('getUrlEncoder')->willReturn($this->encoderMock); + $this->contextMock->expects($this->once())->method('getScopeConfig')->willReturn($this->scopeConfigMock); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $productMock = $this->createMock(Product::class); + $productMock->expects($this->any())->method('getId')->willReturn(1); + + $this->helper = $this->objectManagerHelper->getObject( + HelperData::class, + [ + 'context' => $this->contextMock, + 'layout' => $this->layoutMock + ] + ); + $this->helper->setProduct($productMock); + } + + /** + * Test getSaveUrl() function + */ + public function testGetSaveUrl() + { + $currentUrl = 'http://www.example.com/'; + $type = 'stock'; + $uenc = strtr(base64_encode($currentUrl), '+/=', '-_,'); + $expected = 'http://www.example.com/roductalert/add/stock/product_id/1/uenc/' . $uenc; + + $this->urlBuilderMock->expects($this->any())->method('getCurrentUrl')->willReturn($currentUrl); + $this->encoderMock->expects($this->any())->method('encode') + ->with($currentUrl) + ->willReturn($uenc); + $this->urlBuilderMock->expects($this->any())->method('getUrl') + ->with( + 'productalert/add/' . $type, + [ + 'product_id' => 1, + 'uenc' => $uenc + ] + ) + ->willReturn($expected); + + $this->assertEquals($expected, $this->helper->getSaveUrl($type)); + } + + /** + * Test createBlock() with no exception + */ + public function testCreateBlockWithNoException() + { + $priceBlockMock = $this->createMock(Price::class); + $this->layoutMock->expects($this->once())->method('createBlock')->willReturn($priceBlockMock); + + $this->assertEquals($priceBlockMock, $this->helper->createBlock(Price::class)); + } + + /** + * Test createBlock() with exception + */ + public function testCreateBlockWithException() + { + $invalidBlock = $this->createMock(Product::class); + $this->expectException(LocalizedException::class); + + $this->helper->createBlock($invalidBlock); + } + + /** + * Test isStockAlertAllowed() function with Yes settings + */ + public function testIsStockAlertAllowedWithYesSettings() + { + $this->scopeConfigMock->expects($this->any())->method('isSetFlag') + ->with(Observer::XML_PATH_STOCK_ALLOW, ScopeInterface::SCOPE_STORE) + ->willReturn('1'); + + $this->assertEquals('1', $this->helper->isStockAlertAllowed()); + } + + /** + * Test isPriceAlertAllowed() function with Yes settings + */ + public function testIsPriceAlertAllowedWithYesSetting() + { + $this->scopeConfigMock->expects($this->any())->method('isSetFlag') + ->with(Observer::XML_PATH_PRICE_ALLOW, ScopeInterface::SCOPE_STORE) + ->willReturn('1'); + + $this->assertEquals('1', $this->helper->isPriceAlertAllowed()); + } +} diff --git a/app/code/Magento/ProductAlert/registration.php b/app/code/Magento/ProductAlert/registration.php index d1492a6d10db6..f049de4aef26a 100644 --- a/app/code/Magento/ProductAlert/registration.php +++ b/app/code/Magento/ProductAlert/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_ProductAlert', __DIR__); diff --git a/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AddProductVideoActionGroup.xml b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AddProductVideoActionGroup.xml new file mode 100644 index 0000000000000..bf76b7c11acfd --- /dev/null +++ b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AddProductVideoActionGroup.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- Add video in Admin Product page --> + <actionGroup name="AddProductVideoActionGroup"> + <annotations> + <description>Expands the 'Images And Videos' section on the Admin Product creation/edit page. Adds the provided Video to the Product. Clicks on Save.</description> + </annotations> + <arguments> + <argument name="video" defaultValue="mftfTestProductVideo"/> + </arguments> + + <scrollTo selector="{{AdminProductImagesSection.productImagesToggle}}" x="0" y="-100" stepKey="scrollToArea"/> + <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductVideoSection"/> + <waitForElementVisible selector="{{AdminProductImagesSection.addVideoButton}}" stepKey="waitForAddVideoButtonVisible" time="30"/> + <click selector="{{AdminProductImagesSection.addVideoButton}}" stepKey="addVideo"/> + <waitForElementVisible selector=".modal-slide.mage-new-video-dialog.form-inline._show" stepKey="waitForUrlElementVisibleslide" time="30"/> + <waitForElementVisible selector="{{AdminProductNewVideoSection.videoUrlTextField}}" stepKey="waitForUrlElementVisible" time="60"/> + <fillField selector="{{AdminProductNewVideoSection.videoUrlTextField}}" userInput="{{video.videoUrl}}" stepKey="fillFieldVideoUrl"/> + <fillField selector="{{AdminProductNewVideoSection.videoTitleTextField}}" userInput="{{video.videoTitle}}" stepKey="fillFieldVideoTitle"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <waitForElementNotVisible selector="{{AdminProductNewVideoSection.saveButtonDisabled}}" stepKey="waitForSaveButtonVisible" time="30"/> + <click selector="{{AdminProductNewVideoSection.saveButton}}" stepKey="saveVideo"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AdminProductVideoActionGroup.xml b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AdminProductVideoActionGroup.xml deleted file mode 100644 index 67a79e7f37adf..0000000000000 --- a/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AdminProductVideoActionGroup.xml +++ /dev/null @@ -1,77 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!-- Add video in Admin Product page --> - <actionGroup name="addProductVideo"> - <annotations> - <description>Expands the 'Images And Videos' section on the Admin Product creation/edit page. Adds the provided Video to the Product. Clicks on Save.</description> - </annotations> - <arguments> - <argument name="video" defaultValue="mftfTestProductVideo"/> - </arguments> - - <scrollTo selector="{{AdminProductImagesSection.productImagesToggle}}" x="0" y="-100" stepKey="scrollToArea"/> - <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductVideoSection"/> - <waitForElementVisible selector="{{AdminProductImagesSection.addVideoButton}}" stepKey="waitForAddVideoButtonVisible" time="30"/> - <click selector="{{AdminProductImagesSection.addVideoButton}}" stepKey="addVideo"/> - <waitForElementVisible selector=".modal-slide.mage-new-video-dialog.form-inline._show" stepKey="waitForUrlElementVisibleslide" time="30"/> - <waitForElementVisible selector="{{AdminProductNewVideoSection.videoUrlTextField}}" stepKey="waitForUrlElementVisible" time="60"/> - <fillField selector="{{AdminProductNewVideoSection.videoUrlTextField}}" userInput="{{video.videoUrl}}" stepKey="fillFieldVideoUrl"/> - <fillField selector="{{AdminProductNewVideoSection.videoTitleTextField}}" userInput="{{video.videoTitle}}" stepKey="fillFieldVideoTitle"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <waitForElementNotVisible selector="{{AdminProductNewVideoSection.saveButtonDisabled}}" stepKey="waitForSaveButtonVisible" time="30"/> - <click selector="{{AdminProductNewVideoSection.saveButton}}" stepKey="saveVideo"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> - </actionGroup> - - <!-- Remove video in Admin Product page --> - <actionGroup name="removeProductVideo"> - <annotations> - <description>Expands the 'Images And Videos' section on the Admin Product creation/edit page. Clicks on the Remove Video button.</description> - </annotations> - - <scrollTo selector="{{AdminProductImagesSection.productImagesToggle}}" x="0" y="-100" stepKey="scrollToArea"/> - <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductVideoSection"/> - <waitForElementVisible selector="{{AdminProductImagesSection.addVideoButton}}" stepKey="waitForAddVideoButtonVisible" time="30"/> - <click selector="{{AdminProductImagesSection.removeVideoButton}}" stepKey="removeVideo"/> - </actionGroup> - - <!-- Assert product video in Admin Product page --> - <actionGroup name="assertProductVideoAdminProductPage"> - <annotations> - <description>Validates that the provided Video is present on the Admin Product creation/edit page.</description> - </annotations> - <arguments> - <argument name="video" defaultValue="mftfTestProductVideo"/> - </arguments> - - <scrollTo selector="{{AdminProductImagesSection.productImagesToggle}}" x="0" y="-100" stepKey="scrollToArea"/> - <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductVideoSection"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <seeElement selector="{{AdminProductImagesSection.videoTitleText(video.videoShortTitle)}}" stepKey="seeVideoTitle"/> - <seeElementInDOM selector="{{AdminProductImagesSection.videoUrlHiddenField(video.videoUrl)}}" stepKey="seeVideoItem"/> - </actionGroup> - - <!-- Assert product video not in Admin Product page --> - <actionGroup name="assertProductVideoNotInAdminProductPage"> - <annotations> - <description>Validates that the provided Video is NOT present on the Admin Product creation/edit page.</description> - </annotations> - <arguments> - <argument name="video" defaultValue="mftfTestProductVideo"/> - </arguments> - - <scrollTo selector="{{AdminProductImagesSection.productImagesToggle}}" x="0" y="-100" stepKey="scrollToArea"/> - <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductVideoSection"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <dontSeeElement selector="{{AdminProductImagesSection.videoTitleText(video.videoShortTitle)}}" stepKey="seeVideoTitle"/> - <dontSeeElementInDOM selector="{{AdminProductImagesSection.videoUrlHiddenField(video.videoUrl)}}" stepKey="seeVideoItem"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AssertProductVideoAdminProductPageActionGroup.xml b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AssertProductVideoAdminProductPageActionGroup.xml new file mode 100644 index 0000000000000..bda0b9532f2a1 --- /dev/null +++ b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AssertProductVideoAdminProductPageActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertProductVideoAdminProductPageActionGroup"> + <annotations> + <description>Validates that the provided Video is present on the Admin Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="video" defaultValue="mftfTestProductVideo"/> + </arguments> + + <scrollTo selector="{{AdminProductImagesSection.productImagesToggle}}" x="0" y="-100" stepKey="scrollToArea"/> + <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductVideoSection"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <seeElement selector="{{AdminProductImagesSection.videoTitleText(video.videoShortTitle)}}" stepKey="seeVideoTitle"/> + <seeElementInDOM selector="{{AdminProductImagesSection.videoUrlHiddenField(video.videoUrl)}}" stepKey="seeVideoItem"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AssertProductVideoNotInAdminProductPageActionGroup.xml b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AssertProductVideoNotInAdminProductPageActionGroup.xml new file mode 100644 index 0000000000000..3a10c096222b8 --- /dev/null +++ b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AssertProductVideoNotInAdminProductPageActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertProductVideoNotInAdminProductPageActionGroup"> + <annotations> + <description>Validates that the provided Video is NOT present on the Admin Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="video" defaultValue="mftfTestProductVideo"/> + </arguments> + + <scrollTo selector="{{AdminProductImagesSection.productImagesToggle}}" x="0" y="-100" stepKey="scrollToArea"/> + <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductVideoSection"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <dontSeeElement selector="{{AdminProductImagesSection.videoTitleText(video.videoShortTitle)}}" stepKey="seeVideoTitle"/> + <dontSeeElementInDOM selector="{{AdminProductImagesSection.videoUrlHiddenField(video.videoUrl)}}" stepKey="seeVideoItem"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AssertProductVideoNotInStorefrontProductPageActionGroup.xml b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AssertProductVideoNotInStorefrontProductPageActionGroup.xml new file mode 100644 index 0000000000000..22666ad0eddcf --- /dev/null +++ b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AssertProductVideoNotInStorefrontProductPageActionGroup.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="AssertProductVideoNotInStorefrontProductPageActionGroup"> + <annotations> + <description>Validates that the provided Video is NOT present on the Storefront Product page.</description> + </annotations> + <arguments> + <argument name="dataTypeAttribute" defaultValue="'youtube'"/> + </arguments> + + <dontSeeElement selector="{{StorefrontProductInfoMainSection.productVideo(dataTypeAttribute)}}" stepKey="dontSeeProductVideoDataType"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AssertProductVideoStorefrontProductPageActionGroup.xml b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AssertProductVideoStorefrontProductPageActionGroup.xml new file mode 100644 index 0000000000000..c2bb4e016147a --- /dev/null +++ b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/AssertProductVideoStorefrontProductPageActionGroup.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"> + <!--Assert product video in Storefront Product page --> + <actionGroup name="AssertProductVideoStorefrontProductPageActionGroup"> + <annotations> + <description>Validates that the provided Video is present on the Storefront Product page.</description> + </annotations> + <arguments> + <argument name="dataTypeAttribute" defaultValue="'youtube'"/> + </arguments> + + <seeElement selector="{{StorefrontProductInfoMainSection.productVideo(dataTypeAttribute)}}" stepKey="seeProductVideoDataType"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/RemoveProductVideoActionGroup.xml b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/RemoveProductVideoActionGroup.xml new file mode 100644 index 0000000000000..5666d51082543 --- /dev/null +++ b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/RemoveProductVideoActionGroup.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="RemoveProductVideoActionGroup"> + <annotations> + <description>Expands the 'Images And Videos' section on the Admin Product creation/edit page. Clicks on the Remove Video button.</description> + </annotations> + + <scrollTo selector="{{AdminProductImagesSection.productImagesToggle}}" x="0" y="-100" stepKey="scrollToArea"/> + <conditionalClick selector="{{AdminProductImagesSection.productImagesToggle}}" dependentSelector="{{AdminProductImagesSection.imageUploadButton}}" visible="false" stepKey="openProductVideoSection"/> + <waitForElementVisible selector="{{AdminProductImagesSection.addVideoButton}}" stepKey="waitForAddVideoButtonVisible" time="30"/> + <click selector="{{AdminProductImagesSection.removeVideoButton}}" stepKey="removeVideo"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/StorefrontProductVideoActionGroup.xml b/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/StorefrontProductVideoActionGroup.xml deleted file mode 100644 index e2cdac9ec9287..0000000000000 --- a/app/code/Magento/ProductVideo/Test/Mftf/ActionGroup/StorefrontProductVideoActionGroup.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Assert product video in Storefront Product page --> - <actionGroup name="assertProductVideoStorefrontProductPage"> - <annotations> - <description>Validates that the provided Video is present on the Storefront Product page.</description> - </annotations> - <arguments> - <argument name="dataTypeAttribute" defaultValue="'youtube'"/> - </arguments> - - <seeElement selector="{{StorefrontProductInfoMainSection.productVideo(dataTypeAttribute)}}" stepKey="seeProductVideoDataType"/> - </actionGroup> - - <!--Assert product video not in Storefront Product page --> - <actionGroup name="assertProductVideoNotInStorefrontProductPage"> - <annotations> - <description>Validates that the provided Video is NOT present on the Storefront Product page.</description> - </annotations> - <arguments> - <argument name="dataTypeAttribute" defaultValue="'youtube'"/> - </arguments> - - <dontSeeElement selector="{{StorefrontProductInfoMainSection.productVideo(dataTypeAttribute)}}" stepKey="dontSeeProductVideoDataType"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml b/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml index 2b5f87f78d5e5..c2b92e6af452a 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminAddDefaultVideoSimpleProductTest.xml @@ -22,12 +22,12 @@ </after> <!-- Add product video --> - <actionGroup ref="addProductVideo" stepKey="addProductVideo" after="fillMainProductForm"/> + <actionGroup ref="AddProductVideoActionGroup" stepKey="addProductVideo" after="fillMainProductForm"/> <!-- Assert product video in admin product form --> - <actionGroup ref="assertProductVideoAdminProductPage" stepKey="assertProductVideoAdminProductPage" after="saveProductForm"/> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertProductVideoAdminProductPage" after="saveProductForm"/> <!-- Assert product video in storefront product page --> - <actionGroup ref="assertProductVideoStorefrontProductPage" stepKey="assertProductVideoStorefrontProductPage" after="AssertProductInStorefrontProductPage"/> + <actionGroup ref="AssertProductVideoStorefrontProductPageActionGroup" stepKey="assertProductVideoStorefrontProductPage" after="AssertProductInStorefrontProductPage"/> </test> </tests> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml b/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml index d4da0ffa54451..c674f12115334 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminRemoveDefaultVideoSimpleProductTest.xml @@ -22,15 +22,15 @@ </after> <!-- Add product video --> - <actionGroup ref="addProductVideo" stepKey="addProductVideo" after="fillMainProductForm"/> + <actionGroup ref="AddProductVideoActionGroup" stepKey="addProductVideo" after="fillMainProductForm"/> <!-- Remove product video --> - <actionGroup ref="removeProductVideo" stepKey="removeProductVideo" after="saveProductForm"/> + <actionGroup ref="RemoveProductVideoActionGroup" stepKey="removeProductVideo" after="saveProductForm"/> <!-- Assert product video not in admin product form --> - <actionGroup ref="assertProductVideoNotInAdminProductPage" stepKey="assertProductVideoNotInAdminProductPage" after="saveProductFormAfterRemove"/> + <actionGroup ref="AssertProductVideoNotInAdminProductPageActionGroup" stepKey="assertProductVideoNotInAdminProductPage" after="saveProductFormAfterRemove"/> <!-- Assert product video not in storefront product page --> - <actionGroup ref="assertProductVideoNotInStorefrontProductPage" stepKey="assertProductVideoNotInStorefrontProductPage" after="AssertProductInStorefrontProductPage"/> + <actionGroup ref="AssertProductVideoNotInStorefrontProductPageActionGroup" stepKey="assertProductVideoNotInStorefrontProductPage" after="AssertProductInStorefrontProductPage"/> </test> </tests> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminValidateUrlOnGetVideoInformationTest.xml b/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminValidateUrlOnGetVideoInformationTest.xml index 0db15276e8c67..03fb71ac039a7 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminValidateUrlOnGetVideoInformationTest.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Test/AdminValidateUrlOnGetVideoInformationTest.xml @@ -25,7 +25,7 @@ <actionGroup ref="logout" stepKey="logout"/> </after> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToProductIndexPage"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="SimpleProduct"/> </actionGroup> <actionGroup ref="AdminOpenProductVideoModalActionGroup" stepKey="openAddProductVideoModal"/> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml b/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml index 7249a4223503e..13d396b4faf45 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Test/YoutubeVideoWindowOnProductPageTest.xml @@ -35,16 +35,16 @@ <!--Open simple product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> <waitForPageLoad stepKey="wait1"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGrid"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku"> <argument name="product" value="$$createProduct$$"/> </actionGroup> - <actionGroup ref="openProducForEditByClickingRowXColumnYInProductGrid" stepKey="openFirstProductForEdit"/> + <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openFirstProductForEdit"/> <!-- Add product video --> - <actionGroup ref="addProductVideo" stepKey="addProductVideo" after="openFirstProductForEdit"/> + <actionGroup ref="AddProductVideoActionGroup" stepKey="addProductVideo" after="openFirstProductForEdit"/> <!-- Assert product video in admin product form --> - <actionGroup ref="assertProductVideoAdminProductPage" stepKey="assertProductVideoAdminProductPage" after="addProductVideo"/> + <actionGroup ref="AssertProductVideoAdminProductPageActionGroup" stepKey="assertProductVideoAdminProductPage" after="addProductVideo"/> <!-- Save the product --> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveFirstProduct"/> @@ -53,7 +53,7 @@ <!-- Assert product video in storefront product page --> <amOnPage url="$$createProduct.name$$.html" stepKey="goToStorefrontCategoryPage"/> <waitForPageLoad stepKey="waitForStorefrontPageLoaded"/> - <actionGroup ref="assertProductVideoStorefrontProductPage" stepKey="assertProductVideoStorefrontProductPage" after="waitForStorefrontPageLoaded"/> + <actionGroup ref="AssertProductVideoStorefrontProductPageActionGroup" stepKey="assertProductVideoStorefrontProductPage" after="waitForStorefrontPageLoaded"/> <!--Click Play video button--> <click stepKey="clickToPlayVideo" selector="{{StorefrontProductInfoMainSection.clickInVideo}}"/> diff --git a/app/code/Magento/ProductVideo/registration.php b/app/code/Magento/ProductVideo/registration.php index 39f0c452d3611..e49e231283f42 100644 --- a/app/code/Magento/ProductVideo/registration.php +++ b/app/code/Magento/ProductVideo/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_ProductVideo', __DIR__); diff --git a/app/code/Magento/ProductVideo/view/frontend/web/js/load-player.js b/app/code/Magento/ProductVideo/view/frontend/web/js/load-player.js index ede0d2019309d..aead951043448 100644 --- a/app/code/Magento/ProductVideo/view/frontend/web/js/load-player.js +++ b/app/code/Magento/ProductVideo/view/frontend/web/js/load-player.js @@ -344,6 +344,7 @@ define([ .attr('mozallowfullscreen', '') .attr('allowfullscreen', '') .attr('referrerPolicy', 'origin') + .attr('allow', 'autoplay') ); this._player = window.$f(this.element.children(':first')[0]); diff --git a/app/code/Magento/Quote/Api/CartManagementInterface.php b/app/code/Magento/Quote/Api/CartManagementInterface.php index 7aa4bc4c7603a..dc8ab7fedc870 100644 --- a/app/code/Magento/Quote/Api/CartManagementInterface.php +++ b/app/code/Magento/Quote/Api/CartManagementInterface.php @@ -52,6 +52,9 @@ public function getCartForCustomer($customerId); * @param int $customerId The customer ID. * @param int $storeId * @return boolean + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\StateException + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function assignCustomer($cartId, $customerId, $storeId); diff --git a/app/code/Magento/Quote/Model/Quote/Address.php b/app/code/Magento/Quote/Model/Quote/Address.php index 3ecbc69b80785..db8ac0d1fe179 100644 --- a/app/code/Magento/Quote/Model/Quote/Address.php +++ b/app/code/Magento/Quote/Model/Quote/Address.php @@ -425,9 +425,11 @@ protected function _populateBeforeSaveData() */ protected function _isSameAsBilling() { + $quoteSameAsBilling = $this->getSameAsBilling(); + return $this->getAddressType() == \Magento\Quote\Model\Quote\Address::TYPE_SHIPPING && - ($this->_isNotRegisteredCustomer() || - $this->_isDefaultShippingNullOrSameAsBillingAddress()); + ($this->_isNotRegisteredCustomer() || $this->_isDefaultShippingNullOrSameAsBillingAddress()) && + ($quoteSameAsBilling || $quoteSameAsBilling === 0 || $quoteSameAsBilling === null); } /** @@ -471,7 +473,7 @@ protected function _isDefaultShippingNullOrSameAsBillingAddress() /** * Declare address quote model object * - * @param \Magento\Quote\Model\Quote $quote + * @param \Magento\Quote\Model\Quote $quote * @return $this */ public function setQuote(\Magento\Quote\Model\Quote $quote) @@ -691,7 +693,7 @@ public function getItemQty($itemId = 0) */ public function hasItems() { - return sizeof($this->getAllItems()) > 0; + return count($this->getAllItems()) > 0; } /** @@ -1020,6 +1022,7 @@ public function requestShippingRates(\Magento\Quote\Model\Quote\Item\AbstractIte $request->setLimitCarrier($this->getLimitCarrier()); $baseSubtotalInclTax = $this->getBaseSubtotalTotalInclTax(); $request->setBaseSubtotalInclTax($baseSubtotalInclTax); + $request->setBaseSubtotalWithDiscountInclTax($this->getBaseSubtotalWithDiscount() + $this->getBaseTaxAmount()); $result = $this->_rateCollector->create()->collectRates($request)->getResult(); @@ -1225,8 +1228,8 @@ public function setBaseShippingAmount($value, $alreadyExclTax = false) /** * Set total amount value * - * @param string $code - * @param float $amount + * @param string $code + * @param float $amount * @return $this */ public function setTotalAmount($code, $amount) @@ -1243,8 +1246,8 @@ public function setTotalAmount($code, $amount) /** * Set total amount value in base store currency * - * @param string $code - * @param float $amount + * @param string $code + * @param float $amount * @return $this */ public function setBaseTotalAmount($code, $amount) @@ -1261,8 +1264,8 @@ public function setBaseTotalAmount($code, $amount) /** * Add amount total amount value * - * @param string $code - * @param float $amount + * @param string $code + * @param float $amount * @return $this */ public function addTotalAmount($code, $amount) @@ -1276,8 +1279,8 @@ public function addTotalAmount($code, $amount) /** * Add amount total amount value in base store currency * - * @param string $code - * @param float $amount + * @param string $code + * @param float $amount * @return $this */ public function addBaseTotalAmount($code, $amount) diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total/Grand.php b/app/code/Magento/Quote/Model/Quote/Address/Total/Grand.php index 13f41f909d43f..2cefec2c9035a 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/Total/Grand.php +++ b/app/code/Magento/Quote/Model/Quote/Address/Total/Grand.php @@ -3,43 +3,64 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Quote\Model\Quote\Address\Total; -class Grand extends \Magento\Quote\Model\Quote\Address\Total\AbstractTotal +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Pricing\PriceCurrencyInterface as PriceRounder; +use Magento\Quote\Api\Data\ShippingAssignmentInterface as ShippingAssignment; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Address\Total; + +/** + * Collect grand totals. + */ +class Grand extends AbstractTotal { + /** + * @var PriceRounder + */ + private $priceRounder; + + /** + * @param PriceRounder|null $priceRounder + */ + public function __construct(?PriceRounder $priceRounder) + { + $this->priceRounder = $priceRounder?: ObjectManager::getInstance()->get(PriceRounder::class); + } + /** * Collect grand total address amount * - * @param \Magento\Quote\Model\Quote $quote - * @param \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment - * @param \Magento\Quote\Model\Quote\Address\Total $total - * @return $this + * @param Quote $quote + * @param ShippingAssignment $shippingAssignment + * @param Total $total + * @return Grand * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function collect( - \Magento\Quote\Model\Quote $quote, - \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment, - \Magento\Quote\Model\Quote\Address\Total $total - ) { - $grandTotal = $total->getGrandTotal(); - $baseGrandTotal = $total->getBaseGrandTotal(); + public function collect(Quote $quote, ShippingAssignment $shippingAssignment, Total $total): Grand + { $totals = array_sum($total->getAllTotalAmounts()); $baseTotals = array_sum($total->getAllBaseTotalAmounts()); + $grandTotal = $this->priceRounder->roundPrice($total->getGrandTotal() + $totals, 4); + $baseGrandTotal = $this->priceRounder->roundPrice($total->getBaseGrandTotal() + $baseTotals, 4); - $total->setGrandTotal($grandTotal + $totals); - $total->setBaseGrandTotal($baseGrandTotal + $baseTotals); + $total->setGrandTotal($grandTotal); + $total->setBaseGrandTotal($baseGrandTotal); return $this; } /** * Add grand total information to address * - * @param \Magento\Quote\Model\Quote $quote - * @param \Magento\Quote\Model\Quote\Address\Total $total - * @return $this + * @param Quote $quote + * @param Total $total + * @return array * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Quote\Address\Total $total) + public function fetch(Quote $quote, Total $total): array { return [ 'code' => $this->getCode(), diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index 84ef699b6209e..3a81341e2b02a 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -298,22 +298,28 @@ public function assignCustomer($cartId, $customerId, $storeId) ); } try { - $this->quoteRepository->getForCustomer($customerId); - throw new StateException( - __("The customer can't be assigned to the cart because the customer already has an active cart.") - ); + $customerActiveQuote = $this->quoteRepository->getForCustomer($customerId); + + $quote->merge($customerActiveQuote); + $customerActiveQuote->setIsActive(0); + $this->quoteRepository->save($customerActiveQuote); + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { } $quote->setCustomer($customer); $quote->setCustomerIsGuest(0); + $quote->setIsActive(1); + /** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */ $quoteIdMask = $this->quoteIdMaskFactory->create()->load($cartId, 'quote_id'); if ($quoteIdMask->getId()) { $quoteIdMask->delete(); } + $this->quoteRepository->save($quote); + return true; } diff --git a/app/code/Magento/Quote/Model/QuoteRepository.php b/app/code/Magento/Quote/Model/QuoteRepository.php index 30931821ddc7d..ccfd3df5fafa3 100644 --- a/app/code/Magento/Quote/Model/QuoteRepository.php +++ b/app/code/Magento/Quote/Model/QuoteRepository.php @@ -224,7 +224,7 @@ protected function loadQuote($loadMethod, $loadField, $identifier, array $shared { /** @var CartInterface $quote */ $quote = $this->cartFactory->create(); - if ($sharedStoreIds && method_exists($quote, 'setSharedStoreIds')) { + if ($sharedStoreIds && is_callable([$quote, 'setSharedStoreIds'])) { $quote->setSharedStoreIds($sharedStoreIds); } $quote->setStoreId($this->storeManager->getStore()->getId())->$loadMethod($identifier); diff --git a/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php b/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php index 392a815ed963c..443e4fda1bd8c 100644 --- a/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php +++ b/app/code/Magento/Quote/Model/ResourceModel/Quote/Item/Collection.php @@ -10,6 +10,7 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection; use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Model\Quote; use Magento\Quote\Model\Quote\Item as QuoteItem; use Magento\Quote\Model\ResourceModel\Quote\Item as ResourceQuoteItem; @@ -256,8 +257,17 @@ protected function _assignProducts(): self foreach ($this as $item) { /** @var ProductInterface $product */ $product = $productCollection->getItemById($item->getProductId()); + try { + /** @var QuoteItem $item */ + $parentItem = $item->getParentItem(); + $parentProduct = $parentItem ? $parentItem->getProduct() : null; + } catch (NoSuchEntityException $exception) { + $parentItem = null; + $parentProduct = null; + $this->_logger->error($exception); + } $qtyOptions = []; - if ($product && $this->isValidProduct($product)) { + if ($this->isValidProduct($product) && (!$parentItem || $this->isValidProduct($parentProduct))) { $product->setCustomOptions([]); $optionProductIds = $this->getOptionProductIds($item, $product, $productCollection); foreach ($optionProductIds as $optionProductId) { @@ -276,7 +286,7 @@ protected function _assignProducts(): self } } if ($this->recollectQuote && $this->_quote) { - $this->_quote->collectTotals(); + $this->_quote->setTotalsCollectedFlag(false); } \Magento\Framework\Profiler::stop('QUOTE:' . __METHOD__); @@ -327,7 +337,7 @@ private function getOptionProductIds( * @param ProductInterface $product * @return bool */ - private function isValidProduct(ProductInterface $product): bool + private function isValidProduct(?ProductInterface $product): bool { $result = ($product && (int)$product->getStatus() !== ProductStatus::STATUS_DISABLED); diff --git a/app/code/Magento/Quote/Observer/SubmitObserver.php b/app/code/Magento/Quote/Observer/SubmitObserver.php index 0d6613a691390..db0ba7fb77937 100644 --- a/app/code/Magento/Quote/Observer/SubmitObserver.php +++ b/app/code/Magento/Quote/Observer/SubmitObserver.php @@ -14,7 +14,7 @@ use Psr\Log\LoggerInterface; /** - * Class SubmitObserver + * Class responsive for sending order and invoice emails when it's created through storefront. */ class SubmitObserver implements ObserverInterface { @@ -49,7 +49,7 @@ public function __construct( } /** - * Sends emails to customer. + * Send order and invoice email. * * @param Observer $observer * diff --git a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml index f7a4fda4f67d8..6e31214b0dddf 100644 --- a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml +++ b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml @@ -86,11 +86,11 @@ <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductListing"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetGridToDefaultKeywordSearch"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> </after> <!-- Step 1: Add simple product to shopping cart --> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.name$$)}}" stepKey="amOnSimpleProductPage"/> - <actionGroup ref="AddSimpleProductToCart" stepKey="cartAddSimpleProductToCart"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="cartAddSimpleProductToCart"> <argument name="product" value="$$createSimpleProduct$$"/> <argument name="productCount" value="1"/> </actionGroup> @@ -103,7 +103,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!-- Find the first simple product that we just created using the product grid and go to its page--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> - <actionGroup ref="filterProductGridBySku" stepKey="findCreatedProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct"> <argument name="product" value="$$createConfigChildProduct1$$"/> </actionGroup> <waitForPageLoad stepKey="waitForFiltersToBeApplied"/> @@ -111,7 +111,7 @@ <waitForPageLoad stepKey="waitForProductPageLoad"/> <!-- Disabled child configurable product --> <click selector="{{AdminProductFormSection.enableProductAttributeLabel}}" stepKey="clickDisableProduct"/> - <actionGroup ref="saveProductForm" stepKey="clickSaveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveProduct"/> <!-- Disabled simple product from grid --> <actionGroup ref="ChangeStatusProductUsingProductGridActionGroup" stepKey="disabledProductFromGrid"> <argument name="product" value="$$createSimpleProduct$$"/> @@ -126,7 +126,7 @@ <dontSeeElement selector="{{StorefrontMiniCartSection.quantity}}" stepKey="dontSeeCartItem"/> <!-- Add simple product to shopping cart --> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct2.name$$)}}" stepKey="amOnSimpleProductPage2"/> - <actionGroup ref="AddSimpleProductToCart" stepKey="cartAddSimpleProductToCart2"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="cartAddSimpleProductToCart2"> <argument name="product" value="$$createSimpleProduct2$$"/> <argument name="productCount" value="1"/> </actionGroup> @@ -135,7 +135,7 @@ <openNewTab stepKey="openNewTab2"/> <!-- Find the first simple product that we just created using the product grid and go to its page --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage2"/> - <actionGroup ref="filterProductGridBySku" stepKey="findCreatedProduct2"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="findCreatedProduct2"> <argument name="product" value="$$createSimpleProduct2$$"/> </actionGroup> <click selector="{{AdminProductGridSection.firstRow}}" stepKey="clickOnProductPage2"/> diff --git a/app/code/Magento/Quote/Test/Unit/Model/ChangeQuoteControlTest.php b/app/code/Magento/Quote/Test/Unit/Model/ChangeQuoteControlTest.php new file mode 100644 index 0000000000000..dc62c57c84941 --- /dev/null +++ b/app/code/Magento/Quote/Test/Unit/Model/ChangeQuoteControlTest.php @@ -0,0 +1,144 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Quote\Test\Unit\Model; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Authorization\Model\UserContextInterface; +use Magento\Quote\Model\ChangeQuoteControl; +use Magento\Quote\Api\Data\CartInterface; + +/** + * Unit test for \Magento\Quote\Model\ChangeQuoteControl + * + * Class \Magento\Quote\Test\Unit\Model\ChangeQuoteControlTest + */ +class ChangeQuoteControlTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + protected $objectManager; + + /** + * @var \Magento\Quote\Model\ChangeQuoteControl + */ + protected $model; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $userContextMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $quoteMock; + + protected function setUp() + { + $this->objectManager = new ObjectManager($this); + $this->userContextMock = $this->createMock(UserContextInterface::class); + + $this->model = $this->objectManager->getObject( + ChangeQuoteControl::class, + [ + 'userContext' => $this->userContextMock + ] + ); + + $this->quoteMock = $this->getMockForAbstractClass( + CartInterface::class, + [], + '', + false, + true, + true, + ['getCustomerId'] + ); + } + + /** + * Test if the quote is belonged to customer + */ + public function testIsAllowedIfTheQuoteIsBelongedToCustomer() + { + $quoteCustomerId = 1; + $this->quoteMock->expects($this->any())->method('getCustomerId') + ->will($this->returnValue($quoteCustomerId)); + $this->userContextMock->expects($this->any())->method('getUserType') + ->will($this->returnValue(UserContextInterface::USER_TYPE_CUSTOMER)); + $this->userContextMock->expects($this->any())->method('getUserId') + ->will($this->returnValue($quoteCustomerId)); + + $this->assertEquals(true, $this->model->isAllowed($this->quoteMock)); + } + + /** + * Test if the quote is not belonged to customer + */ + public function testIsAllowedIfTheQuoteIsNotBelongedToCustomer() + { + $currentCustomerId = 1; + $quoteCustomerId = 2; + + $this->quoteMock->expects($this->any())->method('getCustomerId') + ->will($this->returnValue($quoteCustomerId)); + $this->userContextMock->expects($this->any())->method('getUserType') + ->will($this->returnValue(UserContextInterface::USER_TYPE_CUSTOMER)); + $this->userContextMock->expects($this->any())->method('getUserId') + ->will($this->returnValue($currentCustomerId)); + + $this->assertEquals(false, $this->model->isAllowed($this->quoteMock)); + } + + /** + * Test if the quote is belonged to guest and the context is guest + */ + public function testIsAllowedIfQuoteIsBelongedToGuestAndContextIsGuest() + { + $quoteCustomerId = null; + $this->quoteMock->expects($this->any())->method('getCustomerId') + ->will($this->returnValue($quoteCustomerId)); + $this->userContextMock->expects($this->any())->method('getUserType') + ->will($this->returnValue(UserContextInterface::USER_TYPE_GUEST)); + $this->assertEquals(true, $this->model->isAllowed($this->quoteMock)); + } + + /** + * Test if the quote is belonged to customer and the context is guest + */ + public function testIsAllowedIfQuoteIsBelongedToCustomerAndContextIsGuest() + { + $quoteCustomerId = 1; + $this->quoteMock->expects($this->any())->method('getCustomerId') + ->will($this->returnValue($quoteCustomerId)); + $this->userContextMock->expects($this->any())->method('getUserType') + ->will($this->returnValue(UserContextInterface::USER_TYPE_GUEST)); + $this->assertEquals(false, $this->model->isAllowed($this->quoteMock)); + } + + /** + * Test if the context is admin + */ + public function testIsAllowedIfContextIsAdmin() + { + $this->userContextMock->expects($this->any())->method('getUserType') + ->will($this->returnValue(UserContextInterface::USER_TYPE_ADMIN)); + $this->assertEquals(true, $this->model->isAllowed($this->quoteMock)); + } + + /** + * Test if the context is integration + */ + public function testIsAllowedIfContextIsIntegration() + { + $this->userContextMock->expects($this->any())->method('getUserType') + ->will($this->returnValue(UserContextInterface::USER_TYPE_INTEGRATION)); + $this->assertEquals(true, $this->model->isAllowed($this->quoteMock)); + } +} diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/GrandTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/GrandTest.php index 34c51f294c05f..6771583b5bbb0 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/GrandTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/GrandTest.php @@ -6,18 +6,43 @@ namespace Magento\Quote\Test\Unit\Model\Quote\Address\Total; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Quote\Model\Quote\Address\Total\Grand; +use Magento\Framework\Pricing\PriceCurrencyInterface as PriceRounder; +use PHPUnit_Framework_MockObject_MockObject as ObjectMock; +use PHPUnit\Framework\TestCase; -class GrandTest extends \PHPUnit\Framework\TestCase +/** + * Grand totals collector test. + */ +class GrandTest extends TestCase { /** - * @var \Magento\Quote\Model\Quote\Address\Total\Grand + * @var PriceRounder|ObjectMock + */ + private $priceRounder; + + /** + * @var Grand */ - protected $model; + private $model; + /** + * @inheritDoc + */ protected function setUp() { - $objectManager = new ObjectManager($this); - $this->model = $objectManager->getObject(\Magento\Quote\Model\Quote\Address\Total\Grand::class); + $this->priceRounder = $this->getMockBuilder(PriceRounder::class) + ->disableOriginalConstructor() + ->setMethods(['roundPrice']) + ->getMockForAbstractClass(); + + $helper = new ObjectManager($this); + $this->model = $helper->getObject( + Grand::class, + [ + 'priceRounder' => $this->priceRounder, + ] + ); } public function testCollect() @@ -27,14 +52,20 @@ public function testCollect() $grandTotal = 6.4; // 1 + 2 + 3.4 $grandTotalBase = 15.7; // 4 + 5 + 6.7 - $totalMock = $this->createPartialMock(\Magento\Quote\Model\Quote\Address\Total::class, [ + $this->priceRounder->expects($this->at(0))->method('roundPrice')->willReturn($grandTotal + 2); + $this->priceRounder->expects($this->at(1))->method('roundPrice')->willReturn($grandTotalBase + 2); + + $totalMock = $this->createPartialMock( + \Magento\Quote\Model\Quote\Address\Total::class, + [ 'getAllTotalAmounts', 'getAllBaseTotalAmounts', 'setGrandTotal', 'setBaseGrandTotal', 'getGrandTotal', 'getBaseGrandTotal' - ]); + ] + ); $totalMock->expects($this->once())->method('getGrandTotal')->willReturn(2); $totalMock->expects($this->once())->method('getBaseGrandTotal')->willReturn(2); $totalMock->expects($this->once())->method('getAllTotalAmounts')->willReturn($totals); diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php index cd2afc39733f2..2c61c192ead62 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php @@ -7,11 +7,13 @@ namespace Magento\Quote\Test\Unit\Model; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\Customer; use Magento\Framework\App\RequestInterface; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\HTTP\PhpEnvironment\RemoteAddress; use Magento\Quote\Model\CustomerManagement; +use Magento\Quote\Model\Quote; use Magento\Quote\Model\QuoteIdMaskFactory; use Magento\Sales\Api\Data\OrderAddressInterface; @@ -199,7 +201,7 @@ protected function setUp() ); $this->quoteMock = $this->createPartialMock( - \Magento\Quote\Model\Quote::class, + Quote::class, [ 'assignCustomer', 'collectTotals', @@ -275,7 +277,7 @@ public function testCreateEmptyCartAnonymous() $storeId = 345; $quoteId = 2311; - $quoteMock = $this->createMock(\Magento\Quote\Model\Quote::class); + $quoteMock = $this->createMock(Quote::class); $quoteAddress = $this->createPartialMock( \Magento\Quote\Model\Quote\Address::class, ['setCollectShippingRates'] @@ -306,14 +308,14 @@ public function testCreateEmptyCartForCustomer() $quoteId = 2311; $userId = 567; - $quoteMock = $this->createMock(\Magento\Quote\Model\Quote::class); + $quoteMock = $this->createMock(Quote::class); $this->quoteRepositoryMock ->expects($this->once()) ->method('getActiveForCustomer') ->with($userId) - ->willThrowException(new NoSuchEntityException()); - $customer = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) + ->willThrowException(new \Magento\Framework\Exception\NoSuchEntityException()); + $customer = $this->getMockBuilder(CustomerInterface::class) ->setMethods(['getDefaultBilling'])->disableOriginalConstructor()->getMockForAbstractClass(); $quoteAddress = $this->createPartialMock( \Magento\Quote\Model\Quote\Address::class, @@ -342,14 +344,14 @@ public function testCreateEmptyCartForCustomerReturnExistsQuote() $storeId = 345; $userId = 567; - $quoteMock = $this->createMock(\Magento\Quote\Model\Quote::class); + $quoteMock = $this->createMock(Quote::class); $this->quoteRepositoryMock ->expects($this->once()) ->method('getActiveForCustomer') ->with($userId)->willReturn($quoteMock); - $customer = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) + $customer = $this->getMockBuilder(CustomerInterface::class) ->setMethods(['getDefaultBilling'])->disableOriginalConstructor()->getMockForAbstractClass(); $quoteAddress = $this->createPartialMock( \Magento\Quote\Model\Quote\Address::class, @@ -379,8 +381,8 @@ public function testAssignCustomerFromAnotherStore() $customerId = 455; $storeId = 5; - $quoteMock = $this->createMock(\Magento\Quote\Model\Quote::class); - $customerMock = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); + $quoteMock = $this->createMock(Quote::class); + $customerMock = $this->createMock(CustomerInterface::class); $this->quoteRepositoryMock ->expects($this->once()) @@ -395,7 +397,7 @@ public function testAssignCustomerFromAnotherStore() ->willReturn($customerMock); $customerModelMock = $this->createPartialMock( - \Magento\Customer\Model\Customer::class, + Customer::class, ['load', 'getSharedStoreIds'] ); $this->customerFactoryMock->expects($this->once())->method('create')->willReturn($customerModelMock); @@ -424,10 +426,10 @@ public function testAssignCustomerToNonanonymousCart() $storeId = 5; $quoteMock = $this->createPartialMock( - \Magento\Quote\Model\Quote::class, + Quote::class, ['getCustomerId', 'setCustomer', 'setCustomerIsGuest'] ); - $customerMock = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); + $customerMock = $this->createMock(CustomerInterface::class); $this->quoteRepositoryMock ->expects($this->once()) @@ -442,7 +444,7 @@ public function testAssignCustomerToNonanonymousCart() ->willReturn($customerMock); $customerModelMock = $this->createPartialMock( - \Magento\Customer\Model\Customer::class, + Customer::class, ['load', 'getSharedStoreIds'] ); $this->customerFactoryMock->expects($this->once())->method('create')->willReturn($customerModelMock); @@ -463,7 +465,7 @@ public function testAssignCustomerToNonanonymousCart() } /** - * @expectedException \Magento\Framework\Exception\StateException + * @expectedException \Magento\Framework\Exception\NoSuchEntityException */ public function testAssignCustomerNoSuchCustomer() { @@ -472,10 +474,51 @@ public function testAssignCustomerNoSuchCustomer() $storeId = 5; $quoteMock = $this->createPartialMock( - \Magento\Quote\Model\Quote::class, + Quote::class, ['getCustomerId', 'setCustomer', 'setCustomerIsGuest'] ); - $customerMock = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); + + $this->quoteRepositoryMock + ->expects($this->once()) + ->method('getActive') + ->with($cartId) + ->willReturn($quoteMock); + + $this->customerRepositoryMock + ->expects($this->once()) + ->method('getById') + ->with($customerId) + ->willThrowException(new \Magento\Framework\Exception\NoSuchEntityException()); + + $this->expectExceptionMessage( + "No such entity." + ); + + $this->model->assignCustomer($cartId, $customerId, $storeId); + } + + public function testAssignCustomerWithActiveCart() + { + $cartId = 220; + $customerId = 455; + $storeId = 5; + + $this->getPropertyValue($this->model, 'quoteIdMaskFactory') + ->expects($this->once()) + ->method('create') + ->willReturn($this->quoteIdMock); + + $quoteMock = $this->createPartialMock( + Quote::class, + ['getCustomerId', 'setCustomer', 'setCustomerIsGuest', 'setIsActive', 'getIsActive', 'merge'] + ); + + $activeQuoteMock = $this->createPartialMock( + Quote::class, + ['getCustomerId', 'setCustomer', 'setCustomerIsGuest', 'setIsActive', 'getIsActive', 'merge'] + ); + + $customerMock = $this->createMock(CustomerInterface::class); $this->quoteRepositoryMock ->expects($this->once()) @@ -490,7 +533,7 @@ public function testAssignCustomerNoSuchCustomer() ->willReturn($customerMock); $customerModelMock = $this->createPartialMock( - \Magento\Customer\Model\Customer::class, + Customer::class, ['load', 'getSharedStoreIds'] ); $this->customerFactoryMock->expects($this->once())->method('create')->willReturn($customerModelMock); @@ -506,17 +549,26 @@ public function testAssignCustomerNoSuchCustomer() ->willReturn([$storeId, 'some store value']); $quoteMock->expects($this->once())->method('getCustomerId')->willReturn(null); - $this->quoteRepositoryMock ->expects($this->once()) ->method('getForCustomer') - ->with($customerId); + ->with($customerId) + ->willReturn($activeQuoteMock); - $this->model->assignCustomer($cartId, $customerId, $storeId); + $quoteMock->expects($this->once())->method('merge')->with($activeQuoteMock)->willReturnSelf(); + $activeQuoteMock->expects($this->once())->method('setIsActive')->with(0); + $this->quoteRepositoryMock->expects($this->atLeastOnce())->method('save')->with($activeQuoteMock); - $this->expectExceptionMessage( - "The customer can't be assigned to the cart because the customer already has an active cart." - ); + $quoteMock->expects($this->once())->method('setCustomer')->with($customerMock); + $quoteMock->expects($this->once())->method('setCustomerIsGuest')->with(0); + $quoteMock->expects($this->once())->method('setIsActive')->with(1); + + $this->quoteIdMock->expects($this->once())->method('load')->with($cartId, 'quote_id')->willReturnSelf(); + $this->quoteIdMock->expects($this->once())->method('getId')->willReturn(10); + $this->quoteIdMock->expects($this->once())->method('delete'); + $this->quoteRepositoryMock->expects($this->atLeastOnce())->method('save')->with($quoteMock); + + $this->model->assignCustomer($cartId, $customerId, $storeId); } public function testAssignCustomer() @@ -529,15 +581,13 @@ public function testAssignCustomer() ->expects($this->once()) ->method('create') ->willReturn($this->quoteIdMock); - $this->quoteIdMock->expects($this->once())->method('load')->with($cartId, 'quote_id')->willReturnSelf(); - $this->quoteIdMock->expects($this->once())->method('getId')->willReturn(10); - $this->quoteIdMock->expects($this->once())->method('delete'); + $quoteMock = $this->createPartialMock( - \Magento\Quote\Model\Quote::class, - ['getCustomerId', 'setCustomer', 'setCustomerIsGuest'] + Quote::class, + ['getCustomerId', 'setCustomer', 'setCustomerIsGuest', 'setIsActive', 'getIsActive', 'merge'] ); - $customerMock = $this->createMock(\Magento\Customer\Api\Data\CustomerInterface::class); + $customerMock = $this->createMock(CustomerInterface::class); $this->quoteRepositoryMock ->expects($this->once()) ->method('getActive') @@ -551,10 +601,12 @@ public function testAssignCustomer() ->willReturn($customerMock); $customerModelMock = $this->createPartialMock( - \Magento\Customer\Model\Customer::class, + Customer::class, ['load', 'getSharedStoreIds'] ); + $this->customerFactoryMock->expects($this->once())->method('create')->willReturn($customerModelMock); + $customerModelMock ->expects($this->once()) ->method('load') @@ -572,11 +624,17 @@ public function testAssignCustomer() ->expects($this->once()) ->method('getForCustomer') ->with($customerId) - ->willThrowException(new NoSuchEntityException()); + ->willThrowException(new \Magento\Framework\Exception\NoSuchEntityException()); + + $quoteMock->expects($this->never())->method('merge'); $quoteMock->expects($this->once())->method('setCustomer')->with($customerMock); $quoteMock->expects($this->once())->method('setCustomerIsGuest')->with(0); + $quoteMock->expects($this->once())->method('setIsActive')->with(1); + $this->quoteIdMock->expects($this->once())->method('load')->with($cartId, 'quote_id')->willReturnSelf(); + $this->quoteIdMock->expects($this->once())->method('getId')->willReturn(10); + $this->quoteIdMock->expects($this->once())->method('delete'); $this->quoteRepositoryMock->expects($this->once())->method('save')->with($quoteMock); $this->model->assignCustomer($cartId, $customerId, $storeId); @@ -881,7 +939,7 @@ protected function getQuote( \Magento\Quote\Model\Quote\Address $shippingAddress = null ) { $quote = $this->createPartialMock( - \Magento\Quote\Model\Quote::class, + Quote::class, [ 'setIsActive', 'getCustomerEmail', @@ -928,7 +986,7 @@ protected function getQuote( ->willReturn($payment); $customer = $this->createPartialMock( - \Magento\Customer\Model\Customer::class, + Customer::class, ['getDefaultBilling', 'getId'] ); $quote->expects($this->any())->method('getCustomerId')->willReturn($customerId); @@ -1016,12 +1074,12 @@ protected function prepareOrderFactory( } /** - * @throws NoSuchEntityException + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testGetCartForCustomer() { $customerId = 100; - $cartMock = $this->createMock(\Magento\Quote\Model\Quote::class); + $cartMock = $this->createMock(Quote::class); $this->quoteRepositoryMock->expects($this->once()) ->method('getActiveForCustomer') ->with($customerId) diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php index 095e1760df86f..9c28a06fe83eb 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php @@ -9,6 +9,7 @@ use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; use Magento\Framework\Api\SortOrder; use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface; +use PHPUnit\Framework\MockObject\Matcher\InvokedCount as InvokedCountMatch; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Quote\Api\Data\CartInterface; @@ -284,7 +285,14 @@ public function testGetWithSharedStoreIds() $this->assertEquals($this->quoteMock, $this->model->get($cartId, $sharedStoreIds)); } - public function testGetForCustomer() + /** + * Test getForCustomer method + * + * @param InvokedCountMatch $invokeTimes + * @param array $sharedStoreIds + * @dataProvider getForCustomerDataProvider + */ + public function testGetForCustomer(InvokedCountMatch $invokeTimes, array $sharedStoreIds) { $cartId = 17; $customerId = 23; @@ -298,7 +306,7 @@ public function testGetForCustomer() $this->storeMock->expects(static::once()) ->method('getId') ->willReturn(1); - $this->quoteMock->expects(static::never()) + $this->quoteMock->expects($invokeTimes) ->method('setSharedStoreIds'); $this->quoteMock->expects(static::once()) ->method('loadByCustomer') @@ -312,8 +320,27 @@ public function testGetForCustomer() ->method('load') ->with($this->quoteMock); + static::assertEquals($this->quoteMock, $this->model->getForCustomer($customerId, $sharedStoreIds)); static::assertEquals($this->quoteMock, $this->model->getForCustomer($customerId)); - static::assertEquals($this->quoteMock, $this->model->getForCustomer($customerId)); + } + + /** + * Checking how many times we invoke setSharedStoreIds() in protected method loadQuote() + * + * @return array + */ + public function getForCustomerDataProvider() + { + return [ + [ + 'invoke_number_times' => static::never(), + 'shared_store_ids' => [] + ], + [ + 'invoke_number_times' => static::once(), + 'shared_store_ids' => [1] + ] + ]; } /** diff --git a/app/code/Magento/Quote/etc/db_schema.xml b/app/code/Magento/Quote/etc/db_schema.xml index d41591c619cde..44a5f275b4d9f 100644 --- a/app/code/Magento/Quote/etc/db_schema.xml +++ b/app/code/Magento/Quote/etc/db_schema.xml @@ -56,7 +56,7 @@ <column xsi:type="varchar" name="customer_lastname" nullable="true" length="255" comment="Customer Lastname"/> <column xsi:type="varchar" name="customer_suffix" nullable="true" length="40" comment="Customer Suffix"/> <column xsi:type="datetime" name="customer_dob" on_update="false" nullable="true" comment="Customer Dob"/> - <column xsi:type="varchar" name="customer_note" nullable="true" length="255" comment="Customer Note"/> + <column xsi:type="text" name="customer_note" nullable="true" comment="Customer Note"/> <column xsi:type="smallint" name="customer_note_notify" padding="5" unsigned="true" nullable="true" identity="false" default="1" comment="Customer Note Notify"/> <column xsi:type="smallint" name="customer_is_guest" padding="5" unsigned="true" nullable="true" diff --git a/app/code/Magento/Quote/registration.php b/app/code/Magento/Quote/registration.php index f74a3ac5ff9e5..da3425918cf0f 100644 --- a/app/code/Magento/Quote/registration.php +++ b/app/code/Magento/Quote/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Quote', __DIR__); diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php index 005cf3a10ca80..91c77a1a3ecc5 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AddProductsToCart.php @@ -8,7 +8,7 @@ namespace Magento\QuoteGraphQl\Model\Cart; use Magento\Framework\GraphQl\Exception\GraphQlInputException; -use Magento\Framework\Message\AbstractMessage; +use Magento\Framework\Message\MessageInterface; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Model\Quote; @@ -55,29 +55,15 @@ public function execute(Quote $cart, array $cartItems): void } if ($cart->getData('has_error')) { - throw new GraphQlInputException( - __('Shopping cart error: %message', ['message' => $this->getCartErrors($cart)]) - ); + $e = new GraphQlInputException(__('Shopping cart errors')); + $errors = $cart->getErrors(); + foreach ($errors as $error) { + /** @var MessageInterface $error */ + $e->addError(new GraphQlInputException(__($error->getText()))); + } + throw $e; } $this->cartRepository->save($cart); } - - /** - * Collecting cart errors - * - * @param Quote $cart - * @return string - */ - private function getCartErrors(Quote $cart): string - { - $errorMessages = []; - - /** @var AbstractMessage $error */ - foreach ($cart->getErrors() as $error) { - $errorMessages[] = $error->getText(); - } - - return implode(PHP_EOL, $errorMessages); - } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php index 11719db2d1b8f..83c1d03f132db 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php @@ -72,7 +72,12 @@ public function execute(Quote $cart, array $cartItemData): void } if (is_string($result)) { - throw new GraphQlInputException(__($result)); + $e = new GraphQlInputException(__('Cannot add product to cart')); + $errors = array_unique(explode("\n", $result)); + foreach ($errors as $error) { + $e->addError(new GraphQlInputException(__($error))); + } + throw $e; } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php index 5c773d44e6a1d..c87101156327e 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/Address/SaveQuoteAddressToCustomerAddressBook.php @@ -12,6 +12,7 @@ use Magento\Customer\Api\Data\AddressInterfaceFactory; use Magento\Customer\Api\Data\RegionInterface; use Magento\Customer\Api\Data\RegionInterfaceFactory; +use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Quote\Model\Quote\Address as QuoteAddress; @@ -89,8 +90,15 @@ public function execute(QuoteAddress $quoteAddress, int $customerId): void $customerAddress->setRegion($region); $this->addressRepository->save($customerAddress); - } catch (LocalizedException $e) { - throw new GraphQlInputException(__($e->getMessage()), $e); + } catch (InputException $inputException) { + $graphQlInputException = new GraphQlInputException(__($inputException->getMessage())); + $errors = $inputException->getErrors(); + foreach ($errors as $error) { + $graphQlInputException->addError(new GraphQlInputException(__($error->getMessage()))); + } + throw $graphQlInputException; + } catch (LocalizedException $exception) { + throw new GraphQlInputException(__($exception->getMessage()), $exception); } } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AssignBillingAddressToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AssignBillingAddressToCart.php index dd6478b4873c6..22bd0abb1a159 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/AssignBillingAddressToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AssignBillingAddressToCart.php @@ -7,7 +7,7 @@ namespace Magento\QuoteGraphQl\Model\Cart; -use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; @@ -39,20 +39,20 @@ public function __construct( * * @param CartInterface $cart * @param AddressInterface $billingAddress - * @param bool $useForShipping + * @param bool $sameAsShipping * @throws GraphQlInputException * @throws GraphQlNoSuchEntityException */ public function execute( CartInterface $cart, AddressInterface $billingAddress, - bool $useForShipping + bool $sameAsShipping ): void { try { - $this->billingAddressManagement->assign($cart->getId(), $billingAddress, $useForShipping); + $this->billingAddressManagement->assign($cart->getId(), $billingAddress, $sameAsShipping); } catch (NoSuchEntityException $e) { throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e); - } catch (LocalizedException $e) { + } catch (InputException $e) { throw new GraphQlInputException(__($e->getMessage()), $e); } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AssignShippingAddressToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AssignShippingAddressToCart.php index 527999b245a4c..4dbcfad31e84c 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/AssignShippingAddressToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AssignShippingAddressToCart.php @@ -7,7 +7,7 @@ namespace Magento\QuoteGraphQl\Model\Cart; -use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; @@ -50,7 +50,7 @@ public function execute( $this->shippingAddressManagement->assign($cart->getId(), $shippingAddress); } catch (NoSuchEntityException $e) { throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e); - } catch (LocalizedException $e) { + } catch (InputException $e) { throw new GraphQlInputException(__($e->getMessage()), $e); } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestDataProviderInterface.php b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestDataProviderInterface.php index cd61b52db1beb..77925d0940cf4 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestDataProviderInterface.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/BuyRequest/BuyRequestDataProviderInterface.php @@ -13,10 +13,10 @@ interface BuyRequestDataProviderInterface { /** - * Build buy request for adding product to cart + * Provide buy request data from add to cart item request * * @param array $cartItemData - * @return DataObject + * @return array */ public function execute(array $cartItemData): array; } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/DiscountAggregator.php b/app/code/Magento/QuoteGraphQl/Model/Cart/DiscountAggregator.php deleted file mode 100644 index a620b4b2610cf..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/DiscountAggregator.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\Cart; - -use Magento\Quote\Model\Quote; - -/** - * Aggregate cart level discounts - * - * @package Magento\QuoteGraphQl\Model\Cart - */ -class DiscountAggregator -{ - /** - * Aggregate Discount per rule - * - * @param Quote $quote - * @return array - */ - public function aggregateDiscountPerRule( - Quote $quote - ) { - $items = $quote->getItems(); - $discountPerRule = []; - foreach ($items as $item) { - $discountBreakdown = $item->getExtensionAttributes()->getDiscounts(); - if ($discountBreakdown) { - foreach ($discountBreakdown as $key => $value) { - /* @var \Magento\SalesRule\Model\Rule\Action\Discount\Data $discount */ - $discount = $value['discount']; - $rule = $value['rule']; - if (isset($discountPerRule[$key])) { - $discountPerRule[$key]['discount'] += $discount->getAmount(); - } else { - $discountPerRule[$key]['discount'] = $discount->getAmount(); - } - $discountPerRule[$key]['rule'] = $rule; - } - } - } - return $discountPerRule; - } -} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractQuoteAddressData.php b/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractQuoteAddressData.php index 27dd1959cb5d7..468ef4b8f879c 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractQuoteAddressData.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractQuoteAddressData.php @@ -61,21 +61,25 @@ public function execute(QuoteAddress $address): array return $addressData; } - $addressItemsData = []; foreach ($address->getAllItems() as $addressItem) { if ($addressItem instanceof \Magento\Quote\Model\Quote\Item) { $itemId = $addressItem->getItemId(); } else { $itemId = $addressItem->getQuoteItemId(); } - - $addressItemsData[] = [ + $productData = $addressItem->getProduct()->getData(); + $productData['model'] = $addressItem->getProduct(); + $addressData['cart_items'][] = [ 'cart_item_id' => $itemId, 'quantity' => $addressItem->getQty() ]; + $addressData['cart_items_v2'][] = [ + 'id' => $itemId, + 'quantity' => $addressItem->getQty(), + 'product' => $productData, + 'model' => $addressItem, + ]; } - $addressData['cart_items'] = $addressItemsData; - return $addressData; } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php b/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php index afc88f026ed62..9cb3d9173ac59 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php @@ -15,6 +15,9 @@ use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Quote\Model\Quote\Address as QuoteAddress; use Magento\Quote\Model\Quote\AddressFactory as BaseQuoteAddressFactory; +use Magento\Directory\Model\ResourceModel\Region\CollectionFactory as RegionCollectionFactory; +use Magento\Directory\Helper\Data as CountryHelper; +use Magento\Directory\Model\AllowedCountries; /** * Create QuoteAddress @@ -36,36 +39,77 @@ class QuoteAddressFactory */ private $addressHelper; + /** + * @var RegionCollectionFactory + */ + private $regionCollectionFactory; + + /** + * @var CountryHelper + */ + private $countryHelper; + + /** + * @var AllowedCountries + */ + private $allowedCountries; + /** * @param BaseQuoteAddressFactory $quoteAddressFactory * @param GetCustomerAddress $getCustomerAddress * @param AddressHelper $addressHelper + * @param RegionCollectionFactory $regionCollectionFactory + * @param CountryHelper $countryHelper + * @param AllowedCountries $allowedCountries */ public function __construct( BaseQuoteAddressFactory $quoteAddressFactory, GetCustomerAddress $getCustomerAddress, - AddressHelper $addressHelper + AddressHelper $addressHelper, + RegionCollectionFactory $regionCollectionFactory, + CountryHelper $countryHelper, + AllowedCountries $allowedCountries ) { $this->quoteAddressFactory = $quoteAddressFactory; $this->getCustomerAddress = $getCustomerAddress; $this->addressHelper = $addressHelper; + $this->regionCollectionFactory = $regionCollectionFactory; + $this->countryHelper = $countryHelper; + $this->allowedCountries = $allowedCountries; } /** * Create QuoteAddress based on input data * * @param array $addressInput + * * @return QuoteAddress * @throws GraphQlInputException */ public function createBasedOnInputData(array $addressInput): QuoteAddress { $addressInput['country_id'] = ''; - if ($addressInput['country_code']) { + if (isset($addressInput['country_code']) && $addressInput['country_code']) { $addressInput['country_code'] = strtoupper($addressInput['country_code']); $addressInput['country_id'] = $addressInput['country_code']; } + $allowedCountries = $this->allowedCountries->getAllowedCountries(); + if (!in_array($addressInput['country_code'], $allowedCountries, true)) { + throw new GraphQlInputException(__('Country is not available')); + } + $isRegionRequired = $this->countryHelper->isRegionRequired($addressInput['country_code']); + if ($isRegionRequired && !empty($addressInput['region'])) { + $regionCollection = $this->regionCollectionFactory + ->create() + ->addRegionCodeFilter($addressInput['region']) + ->addCountryFilter($addressInput['country_code']); + if ($regionCollection->getSize() === 0) { + throw new GraphQlInputException( + __('Region is not available for the selected country') + ); + } + } $maxAllowedLineCount = $this->addressHelper->getStreetLines(); if (is_array($addressInput['street']) && count($addressInput['street']) > $maxAllowedLineCount) { throw new GraphQlInputException( diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index f3b96a1454fb8..20a3677ef1feb 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -65,8 +65,11 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b { $customerAddressId = $billingAddressInput['customer_address_id'] ?? null; $addressInput = $billingAddressInput['address'] ?? null; - $useForShipping = isset($billingAddressInput['use_for_shipping']) + // Need to keep this for BC of `use_for_shipping` field + $sameAsShipping = isset($billingAddressInput['use_for_shipping']) ? (bool)$billingAddressInput['use_for_shipping'] : false; + $sameAsShipping = isset($billingAddressInput['same_as_shipping']) + ? (bool)$billingAddressInput['same_as_shipping'] : $sameAsShipping; if (null === $customerAddressId && null === $addressInput) { throw new GraphQlInputException( @@ -81,15 +84,15 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b } $addresses = $cart->getAllShippingAddresses(); - if ($useForShipping && count($addresses) > 1) { + if ($sameAsShipping && count($addresses) > 1) { throw new GraphQlInputException( - __('Using the "use_for_shipping" option with multishipping is not possible.') + __('Using the "same_as_shipping" option with multishipping is not possible.') ); } $billingAddress = $this->createBillingAddress($context, $customerAddressId, $addressInput); - $this->assignBillingAddressToCart->execute($cart, $billingAddress, $useForShipping); + $this->assignBillingAddressToCart->execute($cart, $billingAddress, $sameAsShipping); } /** @@ -129,6 +132,15 @@ private function createBillingAddress( (int)$context->getUserId() ); } + $errors = $billingAddress->validate(); + + if (true !== $errors) { + $e = new GraphQlInputException(__('Billing address errors')); + foreach ($errors as $error) { + $e->addError(new GraphQlInputException($error)); + } + throw $e; + } return $billingAddress; } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php index 6b1296eaf3752..e058913dde1d3 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCart.php @@ -10,6 +10,7 @@ use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\GraphQl\Model\Query\ContextInterface; use Magento\Quote\Api\Data\CartInterface; +use Magento\Quote\Model\Quote\Address; /** * Set single shipping address for a specified shopping cart @@ -52,6 +53,15 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s $shippingAddress = $this->getShippingAddress->execute($context, $shippingAddressInput); + $errors = $shippingAddress->validate(); + + if (true !== $errors) { + $e = new GraphQlInputException(__('Shipping address errors')); + foreach ($errors as $error) { + $e->addError(new GraphQlInputException($error)); + } + throw $e; + } $this->assignShippingAddressToCart->execute($cart, $shippingAddress); } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php index b66327ac1dbba..f0d97780845e8 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php @@ -93,12 +93,11 @@ private function getDiscountValues($cartItem, $currencyCode) foreach ($itemDiscounts as $value) { $discount = []; $amount = []; - /* @var \Magento\SalesRule\Model\Rule\Action\Discount\Data $discountData */ - $discountData = $value['discount']; - /* @var \Magento\SalesRule\Model\Rule $rule */ - $rule = $value['rule']; - $discount['label'] = $rule->getStoreLabel($cartItem->getQuote()->getStore()) ?: __('Discount'); - $amount['value'] = $discountData->getAmount(); + /* @var \Magento\SalesRule\Api\Data\DiscountDataInterface $discountData */ + $discountData = $value->getDiscountData(); + $discountAmount = $discountData->getAmount(); + $discount['label'] = $value->getRuleLabel() ?: __('Discount'); + $amount['value'] = $discountAmount; $amount['currency'] = $currencyCode; $discount['amount'] = $amount; $discountValues[] = $discount; diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CustomerCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CustomerCart.php new file mode 100644 index 0000000000000..0be95eccc39e5 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CustomerCart.php @@ -0,0 +1,101 @@ +<?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\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\QuoteGraphQl\Model\Cart\CreateEmptyCartForCustomer; +use Magento\GraphQl\Model\Query\ContextInterface; +use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; +use Magento\Quote\Api\CartManagementInterface; +use Magento\Quote\Model\QuoteIdMaskFactory; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote\QuoteIdMask as QuoteIdMaskResourceModel; + +/** + * Get cart for the customer + */ +class CustomerCart implements ResolverInterface +{ + /** + * @var CreateEmptyCartForCustomer + */ + private $createEmptyCartForCustomer; + + /** + * @var CartManagementInterface + */ + private $cartManagement; + + /** + * @var QuoteIdMaskFactory + */ + private $quoteIdMaskFactory; + + /** + * @var QuoteIdMaskResourceModel + */ + private $quoteIdMaskResourceModel; + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedQuoteId; + + /** + * @param CreateEmptyCartForCustomer $createEmptyCartForCustomer + * @param CartManagementInterface $cartManagement + * @param QuoteIdMaskFactory $quoteIdMaskFactory + * @param QuoteIdMaskResourceModel $quoteIdMaskResourceModel + * @param QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedQuoteId + */ + public function __construct( + CreateEmptyCartForCustomer $createEmptyCartForCustomer, + CartManagementInterface $cartManagement, + QuoteIdMaskFactory $quoteIdMaskFactory, + QuoteIdMaskResourceModel $quoteIdMaskResourceModel, + QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedQuoteId + ) { + $this->createEmptyCartForCustomer = $createEmptyCartForCustomer; + $this->cartManagement = $cartManagement; + $this->quoteIdMaskFactory = $quoteIdMaskFactory; + $this->quoteIdMaskResourceModel = $quoteIdMaskResourceModel; + $this->quoteIdToMaskedQuoteId = $quoteIdToMaskedQuoteId; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + $currentUserId = $context->getUserId(); + + /** @var ContextInterface $context */ + if (false === $context->getExtensionAttributes()->getIsCustomer()) { + throw new GraphQlAuthorizationException(__('The request is allowed for logged in customer')); + } + try { + $cart = $this->cartManagement->getCartForCustomer($currentUserId); + } catch (NoSuchEntityException $e) { + $this->createEmptyCartForCustomer->execute($currentUserId, null); + $cart = $this->cartManagement->getCartForCustomer($currentUserId); + } + + $maskedId = $this->quoteIdToMaskedQuoteId->execute((int) $cart->getId()); + if (empty($maskedId)) { + $quoteIdMask = $this->quoteIdMaskFactory->create(); + $quoteIdMask->setQuoteId((int) $cart->getId()); + $this->quoteIdMaskResourceModel->save($quoteIdMask); + } + + return [ + 'model' => $cart + ]; + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/Discounts.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/Discounts.php index 2c3f6d0e69f4a..703015fd7ddb1 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/Discounts.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/Discounts.php @@ -12,27 +12,12 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Quote\Model\Quote; -use Magento\QuoteGraphQl\Model\Cart\DiscountAggregator; /** * @inheritdoc */ class Discounts implements ResolverInterface { - /** - * @var DiscountAggregator - */ - private $discountAggregator; - - /** - * @param DiscountAggregator|null $discountAggregator - */ - public function __construct( - DiscountAggregator $discountAggregator - ) { - $this->discountAggregator = $discountAggregator; - } - /** * @inheritdoc */ @@ -55,15 +40,16 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value private function getDiscountValues(Quote $quote) { $discountValues=[]; - $totalDiscounts = $this->discountAggregator->aggregateDiscountPerRule($quote); - if ($totalDiscounts) { + $address = $quote->getShippingAddress(); + $totalDiscounts = $address->getExtensionAttributes()->getDiscounts(); + if ($totalDiscounts && is_array($totalDiscounts)) { foreach ($totalDiscounts as $value) { $discount = []; $amount = []; - /* @var \Magento\SalesRule\Model\Rule $rule*/ - $rule = $value['rule']; - $discount['label'] = $rule->getStoreLabel($quote->getStore()) ?: __('Discount'); - $amount['value'] = $value['discount']; + $discount['label'] = $value->getRuleLabel() ?: __('Discount'); + /* @var \Magento\SalesRule\Api\Data\DiscountDataInterface $discountData */ + $discountData = $value->getDiscountData(); + $amount['value'] = $discountData->getAmount(); $amount['currency'] = $quote->getQuoteCurrencyCode(); $discount['amount'] = $amount; $discountValues[] = $discount; diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/MaskedCartId.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/MaskedCartId.php new file mode 100644 index 0000000000000..755f79569f09a --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/MaskedCartId.php @@ -0,0 +1,69 @@ +<?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\Exception\NoSuchEntityException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; + +/** + * Get cart id from the cart + */ +class MaskedCartId implements ResolverInterface +{ + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedQuoteId; + + /** + * @param QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedQuoteId + */ + public function __construct( + QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedQuoteId + ) { + $this->quoteIdToMaskedQuoteId = $quoteIdToMaskedQuoteId; + } + + /** + * @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 Quote $cart */ + $cart = $value['model']; + $cartId = (int) $cart->getId(); + $maskedCartId = $this->getQuoteMaskId($cartId); + return $maskedCartId; + } + + /** + * Get masked id for cart + * + * @param int $quoteId + * @return string + * @throws GraphQlNoSuchEntityException + */ + private function getQuoteMaskId(int $quoteId): string + { + try { + $maskedId = $this->quoteIdToMaskedQuoteId->execute($quoteId); + } catch (NoSuchEntityException $exception) { + throw new GraphQlNoSuchEntityException(__('Current user does not have an active cart.')); + } + return $maskedId; + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/MergeCarts.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/MergeCarts.php new file mode 100644 index 0000000000000..d77d19df55603 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/MergeCarts.php @@ -0,0 +1,80 @@ +<?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\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\QuoteGraphQl\Model\Cart\GetCartForUser; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\GraphQl\Model\Query\ContextInterface; +use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; + +/** + * Merge Carts Resolver + */ +class MergeCarts implements ResolverInterface +{ + /** + * @var GetCartForUser + */ + private $getCartForUser; + + /** + * @var CartRepositoryInterface + */ + private $cartRepository; + + /** + * @param GetCartForUser $getCartForUser + * @param CartRepositoryInterface $cartRepository + */ + public function __construct( + GetCartForUser $getCartForUser, + CartRepositoryInterface $cartRepository + ) { + $this->getCartForUser = $getCartForUser; + $this->cartRepository = $cartRepository; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (empty($args['source_cart_id'])) { + throw new GraphQlInputException(__('Required parameter "source_cart_id" is missing')); + } + + if (empty($args['destination_cart_id'])) { + throw new GraphQlInputException(__('Required parameter "destination_cart_id" is missing')); + } + + /** @var ContextInterface $context */ + if (false === $context->getExtensionAttributes()->getIsCustomer()) { + throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); + } + + $guestMaskedCartId = $args['source_cart_id']; + $customerMaskedCartId = $args['destination_cart_id']; + + $currentUserId = $context->getUserId(); + $storeId = (int)$context->getExtensionAttributes()->getStore()->getId(); + // passing customerId as null enforces source cart should always be a guestcart + $guestCart = $this->getCartForUser->execute($guestMaskedCartId, null, $storeId); + $customerCart = $this->getCartForUser->execute($customerMaskedCartId, $currentUserId, $storeId); + $customerCart->merge($guestCart); + $guestCart->setIsActive(false); + $this->cartRepository->save($customerCart); + $this->cartRepository->save($guestCart); + return [ + 'model' => $customerCart, + ]; + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php index 241237613b94e..fa90f08e4b553 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/UpdateCartItems.php @@ -116,6 +116,11 @@ private function processCartItems(Quote $cart, array $items): void $itemId = (int)$item['cart_item_id']; $customizableOptions = $item['customizable_options'] ?? []; + $cartItem = $cart->getItemById($itemId); + if ($cartItem && $cartItem->getParentItemId()) { + throw new GraphQlInputException(__('Child items may not be updated.')); + } + if (count($customizableOptions) === 0 && !isset($item['quantity'])) { throw new GraphQlInputException(__('Required parameter "quantity" for "cart_items" is missing.')); } diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 0f818984ebda4..d334d56c85aac 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -3,6 +3,7 @@ type Query { cart(cart_id: String!): Cart @resolver (class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Cart") @doc(description:"Returns information about shopping cart") @cache(cacheable: false) + customerCart: Cart! @resolver (class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CustomerCart") @doc(description:"Returns information about the customer shopping cart") @cache(cacheable: false) } type Mutation { @@ -19,6 +20,7 @@ type Mutation { setPaymentMethodOnCart(input: SetPaymentMethodOnCartInput): SetPaymentMethodOnCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\SetPaymentMethodOnCart") setGuestEmailOnCart(input: SetGuestEmailOnCartInput): SetGuestEmailOnCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\SetGuestEmailOnCart") setPaymentMethodAndPlaceOrder(input: SetPaymentMethodAndPlaceOrderInput): PlaceOrderOutput @deprecated(reason: "Should use setPaymentMethodOnCart and placeOrder mutations in single request.") @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetPaymentAndPlaceOrder") + mergeCarts(source_cart_id: String!, destination_cart_id: String!): Cart! @doc(description:"Merges the source cart into the destination cart") @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\MergeCarts") placeOrder(input: PlaceOrderInput): PlaceOrderOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\PlaceOrder") } @@ -96,7 +98,8 @@ input SetBillingAddressOnCartInput { input BillingAddressInput { customer_address_id: Int address: CartAddressInput - use_for_shipping: Boolean + use_for_shipping: Boolean @doc(description: "Deprecated: use `same_as_shipping` field instead") + same_as_shipping: Boolean @doc(description: "Set billing address same as shipping") } input CartAddressInput { @@ -191,6 +194,7 @@ type PlaceOrderOutput { } type Cart { + id: ID! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\MaskedCartId") @doc(description: "The ID of the cart.") items: [CartItemInterface] @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartItems") applied_coupon: AppliedCoupon @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\AppliedCoupon") @doc(description:"An array of coupons that have been applied to the cart") @deprecated(reason: "Use applied_coupons instead ") applied_coupons: [AppliedCoupon] @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\AppliedCoupons") @doc(description:"An array of `AppliedCoupon` objects. Each object contains the `code` text attribute, which specifies the coupon code") @@ -219,18 +223,19 @@ interface CartAddressInterface @typeResolver(class: "\\Magento\\QuoteGraphQl\\Mo type ShippingCartAddress implements CartAddressInterface { available_shipping_methods: [AvailableShippingMethod] @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingAddress\\AvailableShippingMethods") selected_shipping_method: SelectedShippingMethod @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingAddress\\SelectedShippingMethod") - items_weight: Float - cart_items: [CartItemQuantity] customer_notes: String + items_weight: Float @deprecated(reason: "This information shoud not be exposed on frontend") + cart_items: [CartItemQuantity] @deprecated(reason: "`cart_items_v2` should be used instead") + cart_items_v2: [CartItemInterface] } type BillingCartAddress implements CartAddressInterface { customer_notes: String @deprecated (reason: "The field is used only in shipping address") } -type CartItemQuantity { - cart_item_id: Int! - quantity: Float! +type CartItemQuantity @doc(description:"Deprecated: `cart_items` field of `ShippingCartAddress` returns now `CartItemInterface` instead of `CartItemQuantity`") { + cart_item_id: Int! @deprecated(reason: "`cart_items` field of `ShippingCartAddress` returns now `CartItemInterface` instead of `CartItemQuantity`") + quantity: Float! @deprecated(reason: "`cart_items` field of `ShippingCartAddress` returns now `CartItemInterface` instead of `CartItemQuantity`") } type CartAddressRegion { diff --git a/app/code/Magento/RelatedProductGraphQl/Model/DataProvider/RelatedProductDataProvider.php b/app/code/Magento/RelatedProductGraphQl/Model/DataProvider/RelatedProductDataProvider.php index 173c0a94312ee..e5084d4c9f9b6 100644 --- a/app/code/Magento/RelatedProductGraphQl/Model/DataProvider/RelatedProductDataProvider.php +++ b/app/code/Magento/RelatedProductGraphQl/Model/DataProvider/RelatedProductDataProvider.php @@ -7,9 +7,12 @@ namespace Magento\RelatedProductGraphQl\Model\DataProvider; +use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Link; use Magento\Catalog\Model\Product\LinkFactory; +use Magento\Framework\EntityManager\HydratorPool; +use Magento\Framework\EntityManager\MetadataPool; /** * Related Products Data Provider @@ -21,13 +24,31 @@ class RelatedProductDataProvider */ private $linkFactory; + /** + * @var MetadataPool + */ + private $metadataPool; + + /** + * @var HydratorPool + */ + private $hydratorPool; + /** * @param LinkFactory $linkFactory + * @param MetadataPool|null $metadataPool + * @param HydratorPool|null $hydratorPool */ public function __construct( - LinkFactory $linkFactory + LinkFactory $linkFactory, + ?MetadataPool $metadataPool = null, + ?HydratorPool $hydratorPool = null ) { $this->linkFactory = $linkFactory; + $this->metadataPool = $metadataPool + ?? \Magento\Framework\App\ObjectManager::getInstance()->get(MetadataPool::class); + $this->hydratorPool = $hydratorPool + ?? \Magento\Framework\App\ObjectManager::getInstance()->get(HydratorPool::class); } /** @@ -62,9 +83,7 @@ public function getData(Product $product, array $fields, int $linkType): array private function getRelatedProducts(Product $product, array $fields, int $linkType): array { /** @var Link $link */ - $link = $this->linkFactory->create([ 'data' => [ - 'link_type_id' => $linkType, - ]]); + $link = $this->linkFactory->create(['data' => ['link_type_id' => $linkType]]); $collection = $link->getProductCollection(); $collection->setIsStrongMode(); @@ -75,4 +94,42 @@ private function getRelatedProducts(Product $product, array $fields, int $linkTy return $collection->getItems(); } + + /** + * Get related product IDs for given products. + * + * @param \Magento\Catalog\Api\Data\ProductInterface[] $products + * @param int $linkType + * @return string[][] keys - IDs, values - list of linked product IDs. + */ + public function getRelations(array $products, int $linkType): array + { + //Links use real IDs for root products, we need to get them + $actualIdField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); + $hydrator = $this->hydratorPool->getHydrator(ProductInterface::class); + /** @var ProductInterface[] $productsByActualIds */ + $productsByActualIds = []; + foreach ($products as $product) { + $productsByActualIds[$hydrator->extract($product)[$actualIdField]] = $product; + } + //Load all links + /** @var Link $link */ + $link = $this->linkFactory->create(['data' => ['link_type_id' => $linkType]]); + $collection = $link->getLinkCollection(); + $collection->addFieldToFilter('product_id', ['in' => array_keys($productsByActualIds)]); + $collection->addLinkTypeIdFilter(); + + //Prepare map + $map = []; + /** @var Link $item */ + foreach ($collection as $item) { + $productId = $productsByActualIds[$item->getProductId()]->getId(); + if (!array_key_exists($productId, $map)) { + $map[$productId] = []; + } + $map[$productId][] = $item->getLinkedProductId(); + } + + return $map; + } } diff --git a/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/AbstractLikedProducts.php b/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/AbstractLikedProducts.php new file mode 100644 index 0000000000000..7ad2e5dde2985 --- /dev/null +++ b/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/AbstractLikedProducts.php @@ -0,0 +1,169 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\RelatedProductGraphQl\Model\Resolver\Batch; + +use Magento\CatalogGraphQl\Model\Resolver\Product\ProductFieldsSelector; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\Resolver\BatchResolverInterface; +use Magento\Framework\GraphQl\Query\Resolver\BatchResponse; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; +use Magento\RelatedProductGraphQl\Model\DataProvider\RelatedProductDataProvider; +use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product as ProductDataProvider; +use Magento\Framework\Api\SearchCriteriaBuilder; + +/** + * Resolve linked product lists. + */ +abstract class AbstractLikedProducts implements BatchResolverInterface +{ + /** + * @var ProductFieldsSelector + */ + private $productFieldsSelector; + + /** + * @var RelatedProductDataProvider + */ + private $relatedProductDataProvider; + + /** + * @var ProductDataProvider + */ + private $productDataProvider; + + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * @param ProductFieldsSelector $productFieldsSelector + * @param RelatedProductDataProvider $relatedProductDataProvider + * @param ProductDataProvider $productDataProvider + * @param SearchCriteriaBuilder $searchCriteriaBuilder + */ + public function __construct( + ProductFieldsSelector $productFieldsSelector, + RelatedProductDataProvider $relatedProductDataProvider, + ProductDataProvider $productDataProvider, + SearchCriteriaBuilder $searchCriteriaBuilder + ) { + $this->productFieldsSelector = $productFieldsSelector; + $this->relatedProductDataProvider = $relatedProductDataProvider; + $this->productDataProvider = $productDataProvider; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + } + + /** + * Node type. + * + * @return string + */ + abstract protected function getNode(): string; + + /** + * Type of linked products to be resolved. + * + * @return int + */ + abstract protected function getLinkType(): int; + + /** + * Find related products. + * + * @param \Magento\Catalog\Api\Data\ProductInterface[] $products + * @param string[] $loadAttributes + * @param int $linkType + * @return \Magento\Catalog\Api\Data\ProductInterface[][] + */ + private function findRelations(array $products, array $loadAttributes, int $linkType): array + { + //Loading relations + $relations = $this->relatedProductDataProvider->getRelations($products, $linkType); + if (!$relations) { + return []; + } + $relatedIds = array_values($relations); + $relatedIds = array_unique(array_merge(...$relatedIds)); + //Loading products data. + $this->searchCriteriaBuilder->addFilter('entity_id', $relatedIds, 'in'); + $relatedSearchResult = $this->productDataProvider->getList( + $this->searchCriteriaBuilder->create(), + $loadAttributes, + false, + true + ); + //Filling related products map. + /** @var \Magento\Catalog\Api\Data\ProductInterface[] $relatedProducts */ + $relatedProducts = []; + /** @var \Magento\Catalog\Api\Data\ProductInterface $item */ + foreach ($relatedSearchResult->getItems() as $item) { + $relatedProducts[$item->getId()] = $item; + } + + //Matching products with related products. + $relationsData = []; + foreach ($relations as $productId => $relatedIds) { + $relationsData[$productId] = array_map( + function ($id) use ($relatedProducts) { + return $relatedProducts[$id]; + }, + $relatedIds + ); + } + + return $relationsData; + } + + /** + * @inheritDoc + */ + public function resolve(ContextInterface $context, Field $field, array $requests): BatchResponse + { + /** @var \Magento\Catalog\Api\Data\ProductInterface[] $products */ + $products = []; + $fields = []; + /** @var \Magento\Framework\GraphQl\Query\Resolver\BatchRequestItemInterface $request */ + foreach ($requests as $request) { + //Gathering fields and relations to load. + if (empty($request->getValue()['model'])) { + throw new LocalizedException(__('"model" value should be specified')); + } + $products[] = $request->getValue()['model']; + $fields[] = $this->productFieldsSelector->getProductFieldsFromInfo($request->getInfo(), $this->getNode()); + } + $fields = array_unique(array_merge(...$fields)); + + //Finding relations. + $related = $this->findRelations($products, $fields, $this->getLinkType()); + + //Matching requests with responses. + $response = new BatchResponse(); + /** @var \Magento\Framework\GraphQl\Query\Resolver\BatchRequestItemInterface $request */ + foreach ($requests as $request) { + /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ + $product = $request->getValue()['model']; + $result = []; + if (array_key_exists($product->getId(), $related)) { + $result = array_map( + function ($relatedProduct) { + $data = $relatedProduct->getData(); + $data['model'] = $relatedProduct; + + return $data; + }, + $related[$product->getId()] + ); + } + $response->addResponse($request, $result); + } + + return $response; + } +} diff --git a/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/CrossSellProducts.php b/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/CrossSellProducts.php new file mode 100644 index 0000000000000..d636d980597c6 --- /dev/null +++ b/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/CrossSellProducts.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\RelatedProductGraphQl\Model\Resolver\Batch; + +use Magento\Catalog\Model\Product\Link; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\Resolver\BatchResponse; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; + +/** + * CrossSell Products Resolver + */ +class CrossSellProducts extends AbstractLikedProducts +{ + /** + * @inheritDoc + */ + protected function getNode(): string + { + return 'crosssell_products'; + } + + /** + * @inheritDoc + */ + protected function getLinkType(): int + { + return Link::LINK_TYPE_CROSSSELL; + } +} diff --git a/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/RelatedProducts.php b/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/RelatedProducts.php new file mode 100644 index 0000000000000..cefa4db912328 --- /dev/null +++ b/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/RelatedProducts.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\RelatedProductGraphQl\Model\Resolver\Batch; + +use Magento\Catalog\Model\Product\Link; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\Resolver\BatchResponse; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; + +/** + * Related Products Resolver + */ +class RelatedProducts extends AbstractLikedProducts +{ + /** + * @inheritDoc + */ + protected function getNode(): string + { + return 'related_products'; + } + + /** + * @inheritDoc + */ + protected function getLinkType(): int + { + return Link::LINK_TYPE_RELATED; + } +} diff --git a/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/UpSellProducts.php b/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/UpSellProducts.php new file mode 100644 index 0000000000000..42807772a2282 --- /dev/null +++ b/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/UpSellProducts.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\RelatedProductGraphQl\Model\Resolver\Batch; + +use Magento\Catalog\Model\Product\Link; + +/** + * UpSell Products Resolver + */ +class UpSellProducts extends AbstractLikedProducts +{ + /** + * @inheritDoc + */ + protected function getNode(): string + { + return 'upsell_products'; + } + + /** + * @inheritDoc + */ + protected function getLinkType(): int + { + return Link::LINK_TYPE_UPSELL; + } +} diff --git a/app/code/Magento/RelatedProductGraphQl/etc/schema.graphqls b/app/code/Magento/RelatedProductGraphQl/etc/schema.graphqls index 81c51f5035ea6..849f8fb679806 100644 --- a/app/code/Magento/RelatedProductGraphQl/etc/schema.graphqls +++ b/app/code/Magento/RelatedProductGraphQl/etc/schema.graphqls @@ -2,7 +2,7 @@ # See COPYING.txt for license details. interface ProductInterface { - related_products: [ProductInterface] @doc(description: "Related Products") @resolver(class: "Magento\\RelatedProductGraphQl\\Model\\Resolver\\RelatedProducts") - upsell_products: [ProductInterface] @doc(description: "Upsell Products") @resolver(class: "Magento\\RelatedProductGraphQl\\Model\\Resolver\\UpSellProducts") - crosssell_products: [ProductInterface] @doc(description: "Crosssell Products") @resolver(class: "Magento\\RelatedProductGraphQl\\Model\\Resolver\\CrossSellProducts") + related_products: [ProductInterface] @doc(description: "Related Products") @resolver(class: "Magento\\RelatedProductGraphQl\\Model\\Resolver\\Batch\\RelatedProducts") + upsell_products: [ProductInterface] @doc(description: "Upsell Products") @resolver(class: "Magento\\RelatedProductGraphQl\\Model\\Resolver\\Batch\\UpSellProducts") + crosssell_products: [ProductInterface] @doc(description: "Crosssell Products") @resolver(class: "Magento\\RelatedProductGraphQl\\Model\\Resolver\\Batch\\CrossSellProducts") } diff --git a/app/code/Magento/ReleaseNotification/Test/Unit/Model/Condition/CanViewNotificationTest.php b/app/code/Magento/ReleaseNotification/Test/Unit/Model/Condition/CanViewNotificationTest.php index a7f619863af56..813c5f28bf4d9 100644 --- a/app/code/Magento/ReleaseNotification/Test/Unit/Model/Condition/CanViewNotificationTest.php +++ b/app/code/Magento/ReleaseNotification/Test/Unit/Model/Condition/CanViewNotificationTest.php @@ -12,7 +12,6 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Backend\Model\Auth\Session; use Magento\Framework\App\CacheInterface; -use Magento\User\Model\User; /** * Class CanViewNotificationTest @@ -37,11 +36,6 @@ class CanViewNotificationTest extends \PHPUnit\Framework\TestCase /** @var $cacheStorageMock \PHPUnit_Framework_MockObject_MockObject|CacheInterface */ private $cacheStorageMock; - /** - * @var User|\PHPUnit_Framework_MockObject_MockObject - */ - private $userMock; - public function setUp() { $this->cacheStorageMock = $this->getMockBuilder(CacheInterface::class) @@ -50,6 +44,7 @@ public function setUp() ->getMock(); $this->sessionMock = $this->getMockBuilder(Session::class) ->disableOriginalConstructor() + ->setMethods(['getUser', 'getId']) ->getMock(); $this->viewerLoggerMock = $this->getMockBuilder(Logger::class) ->disableOriginalConstructor() @@ -57,7 +52,6 @@ public function setUp() $this->productMetadataMock = $this->getMockBuilder(ProductMetadataInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->userMock = $this->createMock(User::class); $objectManager = new ObjectManager($this); $this->canViewNotification = $objectManager->getObject( CanViewNotification::class, @@ -74,8 +68,8 @@ public function testIsVisibleLoadDataFromCache() { $this->sessionMock->expects($this->once()) ->method('getUser') - ->willReturn($this->userMock); - $this->userMock->expects($this->once()) + ->willReturn($this->sessionMock); + $this->sessionMock->expects($this->once()) ->method('getId') ->willReturn(1); $this->cacheStorageMock->expects($this->once()) @@ -99,8 +93,8 @@ public function testIsVisible($expected, $version, $lastViewVersion) ->willReturn(false); $this->sessionMock->expects($this->once()) ->method('getUser') - ->willReturn($this->userMock); - $this->userMock->expects($this->once()) + ->willReturn($this->sessionMock); + $this->sessionMock->expects($this->once()) ->method('getId') ->willReturn(1); $this->productMetadataMock->expects($this->once()) diff --git a/app/code/Magento/Reports/Controller/Adminhtml/Report/AbstractReport.php b/app/code/Magento/Reports/Controller/Adminhtml/Report/AbstractReport.php index 2fbff13a5b644..d5d8d32744e49 100644 --- a/app/code/Magento/Reports/Controller/Adminhtml/Report/AbstractReport.php +++ b/app/code/Magento/Reports/Controller/Adminhtml/Report/AbstractReport.php @@ -18,6 +18,7 @@ /** * Reports api controller * + * phpcs:disable Magento2.Classes.AbstractApi * @api * @since 100.0.2 * @SuppressWarnings(PHPMD.AllPurposeAction) @@ -140,7 +141,7 @@ protected function _showLastExecutionTime($flagCode, $refreshCode) $flag = $this->_objectManager->create(\Magento\Reports\Model\Flag::class) ->setReportFlagCode($flagCode) ->loadSelf(); - $updatedAt = 'undefined'; + $updatedAt = __('Never'); if ($flag->hasData()) { $updatedAt = $this->timezone->formatDate( $flag->getLastUpdate(), diff --git a/app/code/Magento/Reports/Setup/Patch/Data/ReportDisableNotification.php b/app/code/Magento/Reports/Setup/Patch/Data/ReportDisableNotification.php new file mode 100644 index 0000000000000..e34d7f1144341 --- /dev/null +++ b/app/code/Magento/Reports/Setup/Patch/Data/ReportDisableNotification.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Reports\Setup\Patch\Data; + +use Magento\Framework\Notification\NotifierInterface; + +/** + * Report Disable Notification + */ +class ReportDisableNotification implements \Magento\Framework\Setup\Patch\DataPatchInterface +{ + /** + * @var NotifierInterface + */ + private $notifier; + + /** + * @param NotifierInterface $notifier + */ + public function __construct( + NotifierInterface $notifier + ) { + $this->notifier = $notifier; + } + + /** + * @inheritdoc + */ + public function apply() + { + $message = <<<"MESSAGE" +To improve performance, collecting statistics for the Magento Report module is disabled by default. +You can enable it in System Config. +MESSAGE; + $this->notifier->addNotice(__('Disable Notice'), __($message)); + } + + /** + * @inheritdoc + */ + public function getAliases() + { + return []; + } + + /** + * @inheritdoc + */ + public static function getDependencies() + { + return []; + } +} diff --git a/app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportActionGroup.xml b/app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportActionGroup.xml index 29bbe91afbd11..1250c7b0ac370 100644 --- a/app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportActionGroup.xml +++ b/app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportActionGroup.xml @@ -16,29 +16,11 @@ <argument name="orderFromDate" type="string"/> <argument name="orderToDate" type="string"/> </arguments> - + <click selector="{{OrderReportMainSection.here}}" stepKey="clickOnHere"/> <fillField selector="{{OrderReportFilterSection.dateFrom}}" userInput="{{orderFromDate}}" stepKey="fillFromDate"/> <fillField selector="{{OrderReportFilterSection.dateTo}}" userInput="{{orderToDate}}" stepKey="fillToDate"/> <selectOption selector="{{OrderReportFilterSection.orderStatus}}" userInput="Any" stepKey="selectAnyOption"/> <click selector="{{OrderReportMainSection.showReport}}" stepKey="showReport"/> </actionGroup> - - <actionGroup name="GenerateOrderReportForNotCancelActionGroup"> - <annotations> - <description>Clicks on 'here' to refresh the grid data. Enters the provided Order From/To Dates and provided Order Status. Clicks on 'Show Report'.</description> - </annotations> - <arguments> - <argument name="orderFromDate" type="string"/> - <argument name="orderToDate" type="string"/> - <argument name="statuses" type="string"/> - </arguments> - - <click selector="{{OrderReportMainSection.here}}" stepKey="clickOnHere"/> - <fillField selector="{{OrderReportFilterSection.dateFrom}}" userInput="{{orderFromDate}}" stepKey="fillFromDate"/> - <fillField selector="{{OrderReportFilterSection.dateTo}}" userInput="{{orderToDate}}" stepKey="fillToDate"/> - <selectOption selector="{{OrderReportFilterSection.orderStatus}}" userInput="Specified" stepKey="selectSpecifiedOption"/> - <selectOption selector="{{OrderReportFilterSection.orderStatusSpecified}}" parameterArray="{{statuses}}" stepKey="selectSpecifiedOptionStatus"/> - <click selector="{{OrderReportMainSection.showReport}}" stepKey="showReport"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportForNotCancelActionGroup.xml b/app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportForNotCancelActionGroup.xml new file mode 100644 index 0000000000000..32c82308c8f99 --- /dev/null +++ b/app/code/Magento/Reports/Test/Mftf/ActionGroup/GenerateOrderReportForNotCancelActionGroup.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="GenerateOrderReportForNotCancelActionGroup"> + <annotations> + <description>Clicks on 'here' to refresh the grid data. Enters the provided Order From/To Dates and provided Order Status. Clicks on 'Show Report'.</description> + </annotations> + <arguments> + <argument name="orderFromDate" type="string"/> + <argument name="orderToDate" type="string"/> + <argument name="statuses" type="string"/> + </arguments> + + <click selector="{{OrderReportMainSection.here}}" stepKey="clickOnHere"/> + <fillField selector="{{OrderReportFilterSection.dateFrom}}" userInput="{{orderFromDate}}" stepKey="fillFromDate"/> + <fillField selector="{{OrderReportFilterSection.dateTo}}" userInput="{{orderToDate}}" stepKey="fillToDate"/> + <selectOption selector="{{OrderReportFilterSection.orderStatus}}" userInput="Specified" stepKey="selectSpecifiedOption"/> + <selectOption selector="{{OrderReportFilterSection.orderStatusSpecified}}" parameterArray="{{statuses}}" stepKey="selectSpecifiedOptionStatus"/> + <click selector="{{OrderReportMainSection.showReport}}" stepKey="showReport"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml index 0412e945362e4..324b56757c516 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml @@ -62,7 +62,7 @@ </actionGroup> <!-- Cancel order --> - <actionGroup ref="cancelPendingOrder" stepKey="cancelOrder"/> + <actionGroup ref="CancelPendingOrderActionGroup" stepKey="cancelOrder"/> <!-- Generate Order report for statuses --> <amOnPage url="{{OrdersReportPage.url}}" stepKey="goToOrdersReportPage1"/> diff --git a/app/code/Magento/Reports/etc/config.xml b/app/code/Magento/Reports/etc/config.xml index ffd2299eb6884..5e4bf56cff50c 100644 --- a/app/code/Magento/Reports/etc/config.xml +++ b/app/code/Magento/Reports/etc/config.xml @@ -20,7 +20,7 @@ <mtd_start>1</mtd_start> </dashboard> <options> - <enabled>1</enabled> + <enabled>0</enabled> <product_view_enabled>1</product_view_enabled> <product_send_enabled>1</product_send_enabled> <product_compare_enabled>1</product_compare_enabled> diff --git a/app/code/Magento/Reports/i18n/en_US.csv b/app/code/Magento/Reports/i18n/en_US.csv index 3225f2fc41409..169d3cc2b74b4 100644 --- a/app/code/Magento/Reports/i18n/en_US.csv +++ b/app/code/Magento/Reports/i18n/en_US.csv @@ -224,3 +224,5 @@ Action,Action Report,Report Description,Description undefined,undefined +Never,Never + diff --git a/app/code/Magento/Reports/registration.php b/app/code/Magento/Reports/registration.php index f903b78961992..6ab949d92db73 100644 --- a/app/code/Magento/Reports/registration.php +++ b/app/code/Magento/Reports/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Reports', __DIR__); diff --git a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_statistics_index.xml b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_statistics_index.xml index 649dc7ceeb065..5b841e3523649 100644 --- a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_statistics_index.xml +++ b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_statistics_index.xml @@ -71,7 +71,7 @@ <argument name="sortable" xsi:type="string">0</argument> <argument name="id" xsi:type="string">updated_at</argument> <argument name="index" xsi:type="string">updated_at</argument> - <argument name="default" xsi:type="string" translate="true">undefined</argument> + <argument name="default" xsi:type="string" translate="true">Never</argument> <argument name="column_css_class" xsi:type="string">col-period</argument> <argument name="header_css_class" xsi:type="string">col-period</argument> </arguments> diff --git a/app/code/Magento/RequireJs/registration.php b/app/code/Magento/RequireJs/registration.php index 8e2104a874ad4..4920ac99bbe96 100644 --- a/app/code/Magento/RequireJs/registration.php +++ b/app/code/Magento/RequireJs/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_RequireJs', __DIR__); diff --git a/app/code/Magento/Review/Block/Adminhtml/Grid.php b/app/code/Magento/Review/Block/Adminhtml/Grid.php index 02477bb89610f..e20cb7554e094 100644 --- a/app/code/Magento/Review/Block/Adminhtml/Grid.php +++ b/app/code/Magento/Review/Block/Adminhtml/Grid.php @@ -90,7 +90,7 @@ public function __construct( protected function _construct() { parent::_construct(); - $this->setId('reviwGrid'); + $this->setId('reviewGrid'); $this->setDefaultSort('created_at'); } diff --git a/app/code/Magento/Review/Block/Adminhtml/Rating/Edit/Tab/Form.php b/app/code/Magento/Review/Block/Adminhtml/Rating/Edit/Tab/Form.php index e4b4da23ac629..a8a39b3326edd 100644 --- a/app/code/Magento/Review/Block/Adminhtml/Rating/Edit/Tab/Form.php +++ b/app/code/Magento/Review/Block/Adminhtml/Rating/Edit/Tab/Form.php @@ -111,13 +111,16 @@ protected function addRatingFieldset() ] ); - foreach ($this->systemStore->getStoreCollection() as $store) { - $this->getFieldset('rating_form')->addField( - 'rating_code_' . $store->getId(), - 'text', - ['label' => $store->getName(), 'name' => 'rating_codes[' . $store->getId() . ']'] - ); + if (!$this->_storeManager->isSingleStoreMode()) { + foreach ($this->systemStore->getStoreCollection() as $store) { + $this->getFieldset('rating_form')->addField( + 'rating_code_' . $store->getId(), + 'text', + ['label' => $store->getName(), 'name' => 'rating_codes[' . $store->getId() . ']'] + ); + } } + $this->setRatingData(); } diff --git a/app/code/Magento/Review/Controller/Adminhtml/Product/Delete.php b/app/code/Magento/Review/Controller/Adminhtml/Product/Delete.php index 1b9c9eaa22be7..14a3271e7196d 100644 --- a/app/code/Magento/Review/Controller/Adminhtml/Product/Delete.php +++ b/app/code/Magento/Review/Controller/Adminhtml/Product/Delete.php @@ -33,7 +33,7 @@ public function execute() try { $this->getModel()->aggregate()->delete(); - $this->messageManager->addSuccess(__('The review has been deleted.')); + $this->messageManager->addSuccessMessage(__('The review has been deleted.')); if ($this->getRequest()->getParam('ret') == 'pending') { $resultRedirect->setPath('review/*/pending'); } else { @@ -41,9 +41,9 @@ public function execute() } return $resultRedirect; } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('Something went wrong deleting this review.')); + $this->messageManager->addExceptionMessage($e, __('Something went wrong deleting this review.')); } return $resultRedirect->setPath('review/*/edit/', ['id' => $reviewId]); diff --git a/app/code/Magento/Review/Controller/Adminhtml/Product/MassDelete.php b/app/code/Magento/Review/Controller/Adminhtml/Product/MassDelete.php index 95f9ca3aa79d2..44b267dc5aa7c 100644 --- a/app/code/Magento/Review/Controller/Adminhtml/Product/MassDelete.php +++ b/app/code/Magento/Review/Controller/Adminhtml/Product/MassDelete.php @@ -59,19 +59,19 @@ public function execute() { $reviewsIds = $this->getRequest()->getParam('reviews'); if (!is_array($reviewsIds)) { - $this->messageManager->addError(__('Please select review(s).')); + $this->messageManager->addErrorMessage(__('Please select review(s).')); } else { try { foreach ($this->getCollection() as $model) { $model->delete(); } - $this->messageManager->addSuccess( + $this->messageManager->addSuccessMessage( __('A total of %1 record(s) have been deleted.', count($reviewsIds)) ); } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('Something went wrong while deleting these records.')); + $this->messageManager->addExceptionMessage($e, __('Something went wrong while deleting these records.')); } } /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ diff --git a/app/code/Magento/Review/Controller/Adminhtml/Product/MassUpdateStatus.php b/app/code/Magento/Review/Controller/Adminhtml/Product/MassUpdateStatus.php index 9e93fb8fce63e..ff4acfb964898 100644 --- a/app/code/Magento/Review/Controller/Adminhtml/Product/MassUpdateStatus.php +++ b/app/code/Magento/Review/Controller/Adminhtml/Product/MassUpdateStatus.php @@ -59,20 +59,20 @@ public function execute() { $reviewsIds = $this->getRequest()->getParam('reviews'); if (!is_array($reviewsIds)) { - $this->messageManager->addError(__('Please select review(s).')); + $this->messageManager->addErrorMessage(__('Please select review(s).')); } else { try { $status = $this->getRequest()->getParam('status'); foreach ($this->getCollection() as $model) { $model->setStatusId($status)->save()->aggregate(); } - $this->messageManager->addSuccess( + $this->messageManager->addSuccessMessage( __('A total of %1 record(s) have been updated.', count($reviewsIds)) ); } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException( + $this->messageManager->addExceptionMessage( $e, __('Something went wrong while updating these review(s).') ); diff --git a/app/code/Magento/Review/Controller/Adminhtml/Product/MassVisibleIn.php b/app/code/Magento/Review/Controller/Adminhtml/Product/MassVisibleIn.php index eca37d3fe24da..1f82d4846ede3 100644 --- a/app/code/Magento/Review/Controller/Adminhtml/Product/MassVisibleIn.php +++ b/app/code/Magento/Review/Controller/Adminhtml/Product/MassVisibleIn.php @@ -5,20 +5,27 @@ */ namespace Magento\Review\Controller\Adminhtml\Product; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Review\Controller\Adminhtml\Product as ProductController; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Controller\ResultFactory; -class MassVisibleIn extends ProductController +/** + * Class MassVisibleIn + */ +class MassVisibleIn extends ProductController implements HttpPostActionInterface { + /** + * Execute action + * * @return \Magento\Backend\Model\View\Result\Redirect */ public function execute() { $reviewsIds = $this->getRequest()->getParam('reviews'); if (!is_array($reviewsIds)) { - $this->messageManager->addError(__('Please select review(s).')); + $this->messageManager->addErrorMessage(__('Please select review(s).')); } else { try { $stores = $this->getRequest()->getParam('stores'); @@ -27,13 +34,13 @@ public function execute() $model->setSelectStores($stores); $model->save(); } - $this->messageManager->addSuccess( + $this->messageManager->addSuccessMessage( __('A total of %1 record(s) have been updated.', count($reviewsIds)) ); } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException( + $this->messageManager->addExceptionMessage( $e, __('Something went wrong while updating these review(s).') ); diff --git a/app/code/Magento/Review/Controller/Adminhtml/Product/Post.php b/app/code/Magento/Review/Controller/Adminhtml/Product/Post.php index b42dd3b3063f6..96e6cc48fb496 100644 --- a/app/code/Magento/Review/Controller/Adminhtml/Product/Post.php +++ b/app/code/Magento/Review/Controller/Adminhtml/Product/Post.php @@ -56,7 +56,7 @@ public function execute() $review->aggregate(); - $this->messageManager->addSuccess(__('You saved the review.')); + $this->messageManager->addSuccessMessage(__('You saved the review.')); if ($this->getRequest()->getParam('ret') == 'pending') { $resultRedirect->setPath('review/*/pending'); } else { @@ -64,9 +64,9 @@ public function execute() } return $resultRedirect; } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('Something went wrong while saving this review.')); + $this->messageManager->addExceptionMessage($e, __('Something went wrong while saving this review.')); } } $resultRedirect->setPath('review/*/'); diff --git a/app/code/Magento/Review/Controller/Adminhtml/Product/Save.php b/app/code/Magento/Review/Controller/Adminhtml/Product/Save.php index 5b8ad106987e5..a7a0c96b7e48f 100644 --- a/app/code/Magento/Review/Controller/Adminhtml/Product/Save.php +++ b/app/code/Magento/Review/Controller/Adminhtml/Product/Save.php @@ -34,7 +34,7 @@ public function execute() if (($data = $this->getRequest()->getPostValue()) && ($reviewId = $this->getRequest()->getParam('id'))) { $review = $this->getModel(); if (!$review->getId()) { - $this->messageManager->addError(__('The review was removed by another user or does not exist.')); + $this->messageManager->addErrorMessage(__('The review was removed by another user or does not exist.')); } else { try { $review->addData($data)->save(); @@ -63,11 +63,11 @@ public function execute() $review->aggregate(); - $this->messageManager->addSuccess(__('You saved the review.')); + $this->messageManager->addSuccessMessage(__('You saved the review.')); } catch (LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addException($e, __('Something went wrong while saving this review.')); + $this->messageManager->addExceptionMessage($e, __('Something went wrong while saving this review.')); } } diff --git a/app/code/Magento/Review/Controller/Adminhtml/Rating/Delete.php b/app/code/Magento/Review/Controller/Adminhtml/Rating/Delete.php index b25db6e498fe0..03a73d431221f 100644 --- a/app/code/Magento/Review/Controller/Adminhtml/Rating/Delete.php +++ b/app/code/Magento/Review/Controller/Adminhtml/Rating/Delete.php @@ -9,9 +9,14 @@ use Magento\Review\Controller\Adminhtml\Rating as RatingController; use Magento\Framework\Controller\ResultFactory; +/** + * Class Delete + */ class Delete extends RatingController implements HttpPostActionInterface { /** + * Delete action + * * @return \Magento\Backend\Model\View\Result\Redirect */ public function execute() @@ -23,9 +28,9 @@ public function execute() /** @var \Magento\Review\Model\Rating $model */ $model = $this->_objectManager->create(\Magento\Review\Model\Rating::class); $model->load($this->getRequest()->getParam('id'))->delete(); - $this->messageManager->addSuccess(__('You deleted the rating.')); + $this->messageManager->addSuccessMessage(__('You deleted the rating.')); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $resultRedirect->setPath('review/rating/edit', ['id' => $this->getRequest()->getParam('id')]); return $resultRedirect; } diff --git a/app/code/Magento/Review/Controller/Adminhtml/Rating/Save.php b/app/code/Magento/Review/Controller/Adminhtml/Rating/Save.php index 5dd464f7eb611..ebb4b2a01e286 100644 --- a/app/code/Magento/Review/Controller/Adminhtml/Rating/Save.php +++ b/app/code/Magento/Review/Controller/Adminhtml/Rating/Save.php @@ -9,6 +9,9 @@ use Magento\Review\Controller\Adminhtml\Rating as RatingController; use Magento\Framework\Controller\ResultFactory; +/** + * Class Save + */ class Save extends RatingController implements HttpPostActionInterface { /** @@ -58,10 +61,10 @@ public function execute() } } - $this->messageManager->addSuccess(__('You saved the rating.')); + $this->messageManager->addSuccessMessage(__('You saved the rating.')); $this->_objectManager->get(\Magento\Backend\Model\Session::class)->setRatingData(false); } catch (\Exception $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $this->_objectManager->get(\Magento\Backend\Model\Session::class) ->setRatingData($this->getRequest()->getPostValue()); $resultRedirect->setPath('review/rating/edit', ['id' => $this->getRequest()->getParam('id')]); diff --git a/app/code/Magento/Review/Controller/Product/Post.php b/app/code/Magento/Review/Controller/Product/Post.php index 32838eb6acbbb..2928fdce16c9b 100644 --- a/app/code/Magento/Review/Controller/Product/Post.php +++ b/app/code/Magento/Review/Controller/Product/Post.php @@ -10,6 +10,9 @@ use Magento\Framework\Controller\ResultFactory; use Magento\Review\Model\Review; +/** + * Class Post + */ class Post extends ProductController implements HttpPostActionInterface { /** @@ -63,19 +66,19 @@ public function execute() } $review->aggregate(); - $this->messageManager->addSuccess(__('You submitted your review for moderation.')); + $this->messageManager->addSuccessMessage(__('You submitted your review for moderation.')); } catch (\Exception $e) { $this->reviewSession->setFormData($data); - $this->messageManager->addError(__('We can\'t post your review right now.')); + $this->messageManager->addErrorMessage(__('We can\'t post your review right now.')); } } else { $this->reviewSession->setFormData($data); if (is_array($validate)) { foreach ($validate as $errorMessage) { - $this->messageManager->addError($errorMessage); + $this->messageManager->addErrorMessage($errorMessage); } } else { - $this->messageManager->addError(__('We can\'t post your review right now.')); + $this->messageManager->addErrorMessage(__('We can\'t post your review right now.')); } } } diff --git a/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminAssertStoreViewRatingTitleWhenSingleStoreModeIsNoActionGroup.xml b/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminAssertStoreViewRatingTitleWhenSingleStoreModeIsNoActionGroup.xml new file mode 100644 index 0000000000000..05fad32dabe51 --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminAssertStoreViewRatingTitleWhenSingleStoreModeIsNoActionGroup.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="AdminAssertStoreViewRatingTitleWhenSingleStoreModeIsNoActionGroup"> + <annotations> + <description>If Single Store Mode is disabled, default store view title label should be displayed.</description> + </annotations> + <seeElement selector="{{AdminEditAndNewRatingSection.defaultStoreViewTitleLabel}}" stepKey="seeLabel"/> + <seeElement selector="{{AdminEditAndNewRatingSection.defaultStoreViewTitleInput}}" stepKey="seeInput"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminAssertStoreViewRatingTitleWhenSingleStoreModeIsYesActionGroup.xml b/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminAssertStoreViewRatingTitleWhenSingleStoreModeIsYesActionGroup.xml new file mode 100644 index 0000000000000..6e5586bfa1252 --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminAssertStoreViewRatingTitleWhenSingleStoreModeIsYesActionGroup.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="AdminAssertStoreViewRatingTitleWhenSingleStoreModeIsYesActionGroup"> + <annotations> + <description>If Single Store Mode is enabled, default store view title label should not be displayed.</description> + </annotations> + <dontSeeElement selector="{{AdminEditAndNewRatingSection.defaultStoreViewTitleLabel}}" stepKey="dontSeeLabel"/> + <dontSeeElement selector="{{AdminEditAndNewRatingSection.defaultStoreViewTitleInput}}" stepKey="dontSeeInput"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminNavigateToNewRatingFormActionGroup.xml b/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminNavigateToNewRatingFormActionGroup.xml new file mode 100644 index 0000000000000..3659405c52b69 --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminNavigateToNewRatingFormActionGroup.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="AdminNavigateToNewRatingFormActionGroup"> + <annotations> + <description>Open New Rating Form</description> + </annotations> + <amOnPage url="{{AdminNewRatingPage.url}}" stepKey="amOnUrlNewRatingPage"/> + <waitForPageLoad stepKey="waitForNewRatingPage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminSaveReviewActionGroup.xml b/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminSaveReviewActionGroup.xml index 62c93764ab61d..1937905ae2849 100644 --- a/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminSaveReviewActionGroup.xml +++ b/app/code/Magento/Review/Test/Mftf/ActionGroup/AdminSaveReviewActionGroup.xml @@ -9,7 +9,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminSaveReviewActionGroup"> <click selector="{{AdminEditReviewSection.saveReview}}" stepKey="saveReview"/> - <waitForElementVisible selector="{{AdminMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="You saved the review." stepKey="seeSuccessMessage"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the review." stepKey="seeSuccessMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Review/Test/Mftf/Page/AdminNewRatingPage.xml b/app/code/Magento/Review/Test/Mftf/Page/AdminNewRatingPage.xml new file mode 100644 index 0000000000000..8dfc2182e228c --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/Page/AdminNewRatingPage.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="AdminNewRatingPage" url="review/rating/new/" area="admin" module="Review"> + <section name="AdminEditAndNewRatingSection"/> + </page> +</pages> diff --git a/app/code/Magento/Review/Test/Mftf/Section/AdminEditAndNewRatingSection.xml b/app/code/Magento/Review/Test/Mftf/Section/AdminEditAndNewRatingSection.xml new file mode 100644 index 0000000000000..59dd3d2004790 --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/Section/AdminEditAndNewRatingSection.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="AdminEditAndNewRatingSection"> + <element name="defaultStoreViewTitleLabel" type="text" selector=".field-rating_code_1 label"/> + <element name="defaultStoreViewTitleInput" type="input" selector=".field-rating_code_1 input"/> + </section> +</sections> diff --git a/app/code/Magento/Review/Test/Mftf/Section/AdminReviewGridSection.xml b/app/code/Magento/Review/Test/Mftf/Section/AdminReviewGridSection.xml index 2c5588cf2645b..981de91f357cb 100644 --- a/app/code/Magento/Review/Test/Mftf/Section/AdminReviewGridSection.xml +++ b/app/code/Magento/Review/Test/Mftf/Section/AdminReviewGridSection.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="AdminReviewGridSection"> - <element name="nickname" type="input" selector="#reviwGrid_filter_nickname"/> - <element name="status" type="select" selector="#reviwGrid_filter_status"/> + <element name="nickname" type="input" selector="#reviewGrid_filter_nickname"/> + <element name="status" type="select" selector="#reviewGrid_filter_status"/> <element name="firstRow" type="block" selector=".data-grid tbody tr:nth-of-type(1)"/> - <element name="massActions" type="button" selector="#reviwGrid_massaction-mass-select"/> - <element name="massActionsSelect" type="button" selector="#reviwGrid_massaction-select"/> + <element name="massActions" type="button" selector="#reviewGrid_massaction-mass-select"/> + <element name="massActionsSelect" type="button" selector="#reviewGrid_massaction-select"/> <element name="submit" type="button" selector=".admin__grid-massaction-form .action-default.scalable"/> <element name="acceptModal" type="button" selector=".modal-popup.confirm button.action-accept"/> </section> diff --git a/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeNoTest.xml b/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeNoTest.xml new file mode 100644 index 0000000000000..77789dd172bdd --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeNoTest.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminVerifyNewRatingFormSingleStoreModeNoTest"> + <annotations> + <features value="Review"/> + <stories value="Rating Form"/> + <title value="Verify New Rating Form if single store mode is No"/> + <description value="New Rating Form should have Default store view field if single store mode is No"/> + <severity value="MAJOR"/> + <testCaseId value="MC-21818"/> + <group value="review"/> + </annotations> + <before> + <magentoCLI command="config:set general/single_store_mode/enabled 0" stepKey="enabledSingleStoreMode"/> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <actionGroup ref="AdminNavigateToNewRatingFormActionGroup" stepKey="navigateToNewRatingPage" /> + <actionGroup ref="AdminAssertStoreViewRatingTitleWhenSingleStoreModeIsNoActionGroup" stepKey="verifyForm" /> + </test> +</tests> diff --git a/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeYesTest.xml b/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeYesTest.xml new file mode 100644 index 0000000000000..e5368e9192c98 --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/Test/AdminVerifyNewRatingFormSingleStoreModeYesTest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminVerifyNewRatingFormSingleStoreModeYesTest"> + <annotations> + <features value="Review"/> + <stories value="Rating Form"/> + <title value="Verify New Rating Form if single store mode is Yes"/> + <description value="New Rating Form should not have Default store view field if single store mode is Yes"/> + <severity value="MAJOR"/> + <testCaseId value="MC-21818"/> + <group value="review"/> + </annotations> + <before> + <magentoCLI command="config:set general/single_store_mode/enabled 1" stepKey="enabledSingleStoreMode"/> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + </before> + <after> + <magentoCLI command="config:set general/single_store_mode/enabled 0" stepKey="enabledSingleStoreMode"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <actionGroup ref="AdminNavigateToNewRatingFormActionGroup" stepKey="navigateToNewRatingPage" /> + <actionGroup ref="AdminAssertStoreViewRatingTitleWhenSingleStoreModeIsYesActionGroup" stepKey="verifyForm" /> + </test> +</tests> diff --git a/app/code/Magento/Review/Test/Mftf/Test/StorefrontNoJavascriptErrorOnAddYourReviewClickTest.xml b/app/code/Magento/Review/Test/Mftf/Test/StorefrontNoJavascriptErrorOnAddYourReviewClickTest.xml new file mode 100644 index 0000000000000..99e418a950c69 --- /dev/null +++ b/app/code/Magento/Review/Test/Mftf/Test/StorefrontNoJavascriptErrorOnAddYourReviewClickTest.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontNoJavascriptErrorOnAddYourReviewClickTest"> + <annotations> + <features value="Review"/> + <title value="Storefront no javascript error on 'Add Your Review' click test"/> + <description value="Verify no javascript error occurs when customer clicks 'Add Your Review' link"/> + <severity value="MAJOR"/> + <group value="review"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="textProductAttribute" stepKey="createProductAttribute"/> + <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> + <amOnPage url="{{AdminProductAttributeSetEditPage.url}}/$$createAttributeSet.attribute_set_id$$/" + stepKey="onAttributeSetEdit"/> + <actionGroup ref="AssignAttributeToGroupActionGroup" stepKey="assignAttributeToGroup"> + <argument name="group" value="Product Details"/> + <argument name="attribute" value="$$createProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="SaveAttributeSetActionGroup" stepKey="SaveAttributeSet"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProductWithCustomAttributeSet" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + <requiredEntity createDataKey="createAttributeSet"/> + </createData> + </before> + <after> + <deleteData createDataKey="createProduct" stepKey="deleteProductCustom"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createAttributeSet" stepKey="deleteAttributeSet"/> + <deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <amOnPage url="{{StorefrontProductPage.url($$createProduct.name$$)}}" stepKey="goToProductPage"/> + <dontSeeElement selector="{{StorefrontProductInfoDetailsSection.productNameForReview}}" stepKey="dontSeeReviewTab"/> + <click selector="{{StorefrontProductInfoMainSection.addReviewLink}}" stepKey="clickAddReview"/> + <dontSeeJsError stepKey="dontSeeJsError"/> + <seeElement selector="{{StorefrontProductInfoDetailsSection.productNameForReview}}" stepKey="seeReviewTab"/> + </test> +</tests> diff --git a/app/code/Magento/Review/Test/Unit/Controller/Product/PostTest.php b/app/code/Magento/Review/Test/Unit/Controller/Product/PostTest.php index 1526e80f8190a..e5fd52bf8cf97 100644 --- a/app/code/Magento/Review/Test/Unit/Controller/Product/PostTest.php +++ b/app/code/Magento/Review/Test/Unit/Controller/Product/PostTest.php @@ -299,7 +299,7 @@ public function testExecute() ->willReturnSelf(); $this->review->expects($this->once())->method('aggregate') ->willReturnSelf(); - $this->messageManager->expects($this->once())->method('addSuccess') + $this->messageManager->expects($this->once())->method('addSuccessMessage') ->with(__('You submitted your review for moderation.')) ->willReturnSelf(); $this->reviewSession->expects($this->once())->method('getRedirectUrl') diff --git a/app/code/Magento/Review/Test/Unit/Helper/DataTest.php b/app/code/Magento/Review/Test/Unit/Helper/DataTest.php new file mode 100644 index 0000000000000..7473018c0eaa2 --- /dev/null +++ b/app/code/Magento/Review/Test/Unit/Helper/DataTest.php @@ -0,0 +1,163 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Review\Test\Unit\Helper; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Review\Helper\Data as HelperData; +use Magento\Framework\Escaper; +use Magento\Framework\Filter\FilterManager; +use Magento\Framework\App\Helper\Context; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface; + +/** + * Class \Magento\Review\Test\Unit\Helper\DataTest + */ +class DataTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManagerHelper + */ + private $objectManager; + + /** + * @var HelperData + */ + private $helper; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|Escaper + */ + private $escaper; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|FilterManager + */ + private $filter; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|Context + */ + private $context; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|ScopeConfigInterface + */ + private $scopeConfig; + + /** + * Setup environment + */ + protected function setUp() + { + $this->context = $this->getMockBuilder(Context::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->scopeConfig = $this->getMockBuilder(ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->filter = $this->getMockBuilder(FilterManager::class) + ->disableOriginalConstructor() + ->setMethods(['truncate']) + ->getMock(); + + $this->escaper = $this->getMockBuilder(Escaper::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->context->expects($this->once()) + ->method('getScopeConfig') + ->willReturn($this->scopeConfig); + + $this->objectManager = new ObjectManagerHelper($this); + $this->helper = $this->objectManager->getObject( + HelperData::class, + [ + 'context' => $this->context, + 'escaper' => $this->escaper, + 'filter' => $this->filter + ] + ); + } + + /** + * Test getDetail() function + */ + public function testGetDetail() + { + $origDetail = "This\nis\na\nstring"; + $expected = "This<br />"."\n"."is<br />"."\n"."a<br />"."\n"."string"; + + $this->filter->expects($this->any())->method('truncate') + ->with($origDetail, ['length' => 50]) + ->willReturn($origDetail); + + $this->assertEquals($expected, $this->helper->getDetail($origDetail)); + } + + /** + * Test getDetailHtml() function + */ + public function getDetailHtml() + { + $origDetail = "<span>This\nis\na\nstring</span>"; + $origDetailEscapeHtml = "This\nis\na\nstring"; + $expected = "This<br />"."\n"."is<br />"."\n"."a<br />"."\n"."string"; + + $this->escaper->expects($this->any())->method('escapeHtml') + ->with($origDetail) + ->willReturn($origDetailEscapeHtml); + + $this->filter->expects($this->any())->method('truncate') + ->with($origDetailEscapeHtml, ['length' => 50]) + ->willReturn($origDetailEscapeHtml); + + $this->assertEquals($expected, $this->helper->getDetail($origDetail)); + } + + /** + * Test getIsGuestAllowToWrite() function + */ + public function testGetIsGuestAllowToWrite() + { + $this->scopeConfig->expects($this->any())->method('isSetFlag') + ->with('catalog/review/allow_guest', ScopeInterface::SCOPE_STORE) + ->willReturn('1'); + + $this->assertEquals(true, $this->helper->getIsGuestAllowToWrite()); + } + + /** + * Test getReviewStatuses() function + */ + public function testGetReviewStatuses() + { + $expected = [ + 1 => __('Approved'), + 2 => __('Pending'), + 3 => __('Not Approved') + ]; + $this->assertEquals($expected, $this->helper->getReviewStatuses()); + } + + /** + * Test getReviewStatusesOptionArray() function + */ + public function testGetReviewStatusesOptionArray() + { + $expected = [ + ['value' => 1, 'label' => __('Approved')], + ['value' => 2, 'label' => __('Pending')], + ['value' => 3, 'label' => __('Not Approved')] + ]; + $this->assertEquals($expected, $this->helper->getReviewStatusesOptionArray()); + } +} diff --git a/app/code/Magento/Review/registration.php b/app/code/Magento/Review/registration.php index 3325f6194118f..0588c9a1a7620 100644 --- a/app/code/Magento/Review/registration.php +++ b/app/code/Magento/Review/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Review', __DIR__); diff --git a/app/code/Magento/Review/view/frontend/templates/form.phtml b/app/code/Magento/Review/view/frontend/templates/form.phtml index f1340945043e9..6b00bf681c1e3 100644 --- a/app/code/Magento/Review/view/frontend/templates/form.phtml +++ b/app/code/Magento/Review/view/frontend/templates/form.phtml @@ -5,28 +5,29 @@ */ /** @var \Magento\Review\Block\Form $block */ +//phpcs:disable Generic.Files.LineLength ?> <div class="block review-add"> <div class="block-title"><strong><?= $block->escapeHtml(__('Write Your Own Review')) ?></strong></div> <div class="block-content"> -<?php if ($block->getAllowWriteReviewFlag()) : ?> +<?php if ($block->getAllowWriteReviewFlag()):?> <form action="<?= $block->escapeUrl($block->getAction()) ?>" class="review-form" method="post" id="review-form" data-role="product-review-form" data-bind="scope: 'review-form'"> <?= $block->getBlockHtml('formkey') ?> <?= $block->getChildHtml('form_fields_before') ?> <fieldset class="fieldset review-fieldset" data-hasrequired="<?= $block->escapeHtmlAttr(__('* Required Fields')) ?>"> <legend class="legend review-legend"><span><?= $block->escapeHtml(__("You're reviewing:")) ?></span><strong><?= $block->escapeHtml($block->getProductInfo()->getName()) ?></strong></legend><br /> - <?php if ($block->getRatings() && $block->getRatings()->getSize()) : ?> + <?php if ($block->getRatings() && $block->getRatings()->getSize()): ?> <span id="input-message-box"></span> <fieldset class="field required review-field-ratings"> <legend class="label"><span><?= $block->escapeHtml(__('Your Rating')) ?></span></legend><br/> <div class="control"> <div class="nested" id="product-review-table"> - <?php foreach ($block->getRatings() as $_rating) : ?> + <?php foreach ($block->getRatings() as $_rating): ?> <div class="field choice review-field-rating"> <label class="label" id="<?= $block->escapeHtml($_rating->getRatingCode()) ?>_rating_label"><span><?= $block->escapeHtml($_rating->getRatingCode()) ?></span></label> <div class="control review-control-vote"> <?php $options = $_rating->getOptions();?> - <?php $iterator = 1; foreach ($options as $_option) : ?> + <?php $iterator = 1; foreach ($options as $_option): ?> <input type="radio" name="ratings[<?= $block->escapeHtmlAttr($_rating->getId()) ?>]" @@ -84,11 +85,12 @@ }, "#review-form": { "Magento_Review/js/error-placement": {}, - "Magento_Review/js/validate-review": {} + "Magento_Review/js/validate-review": {}, + "Magento_Review/js/submit-review": {} } } </script> -<?php else : ?> +<?php else: ?> <div class="message info notlogged" id="review-form"> <div> <?= $block->escapeHtml(__('Only registered users can write reviews. Please <a href="%1">Sign in</a> or <a href="%2">create an account</a>', $block->getLoginLink(), $block->getRegisterUrl()), ['a']) ?> diff --git a/app/code/Magento/Review/view/frontend/web/js/submit-review.js b/app/code/Magento/Review/view/frontend/web/js/submit-review.js new file mode 100644 index 0000000000000..6399ce22ffe88 --- /dev/null +++ b/app/code/Magento/Review/view/frontend/web/js/submit-review.js @@ -0,0 +1,18 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery' +], function ($) { + 'use strict'; + + return function (config, element) { + $(element).on('submit', function () { + if ($(this).valid()) { + $(this).find('.submit').attr('disabled', true); + } + }); + }; +}); diff --git a/app/code/Magento/Robots/registration.php b/app/code/Magento/Robots/registration.php index 36d898abfa9fb..343344d3910ff 100644 --- a/app/code/Magento/Robots/registration.php +++ b/app/code/Magento/Robots/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Robots', __DIR__); diff --git a/app/code/Magento/Rss/registration.php b/app/code/Magento/Rss/registration.php index 4ab2ddf494665..dc8ba0d5d88ba 100644 --- a/app/code/Magento/Rss/registration.php +++ b/app/code/Magento/Rss/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Rss', __DIR__); diff --git a/app/code/Magento/Rule/Model/AbstractModel.php b/app/code/Magento/Rule/Model/AbstractModel.php index 72b3528532be5..58c18093c3bab 100644 --- a/app/code/Magento/Rule/Model/AbstractModel.php +++ b/app/code/Magento/Rule/Model/AbstractModel.php @@ -12,6 +12,7 @@ * Abstract Rule entity data model * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * phpcs:disable Magento2.Classes.AbstractApi * @api * @since 100.0.2 */ @@ -342,7 +343,7 @@ protected function _convertFlatToRecursive(array $data) foreach ($value as $id => $data) { $path = explode('--', $id); $node = & $arr; - for ($i = 0, $l = sizeof($path); $i < $l; $i++) { + for ($i = 0, $l = count($path); $i < $l; $i++) { if (!isset($node[$key][$path[$i]])) { $node[$key][$path[$i]] = []; } @@ -483,6 +484,8 @@ public function getWebsiteIds() } /** + * Get extension factory + * * @return \Magento\Framework\Api\ExtensionAttributesFactory * @deprecated 100.1.0 */ @@ -493,6 +496,8 @@ private function getExtensionFactory() } /** + * Get custom attribute factory + * * @return \Magento\Framework\Api\AttributeValueFactory * @deprecated 100.1.0 */ diff --git a/app/code/Magento/Rule/Model/Action/Collection.php b/app/code/Magento/Rule/Model/Action/Collection.php index 3fd1d59df3315..33e385e264f72 100644 --- a/app/code/Magento/Rule/Model/Action/Collection.php +++ b/app/code/Magento/Rule/Model/Action/Collection.php @@ -3,9 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Rule\Model\Action; /** + * Collections + * * @api * @since 100.0.2 */ @@ -61,6 +64,8 @@ public function asArray(array $arrAttributes = []) } /** + * Load array + * * @param array $arr * @return $this */ @@ -80,6 +85,8 @@ public function loadArray(array $arr) } /** + * Add actions + * * @param ActionInterface $action * @return $this */ @@ -91,7 +98,7 @@ public function addAction(ActionInterface $action) $actions[] = $action; if (!$action->getId()) { - $action->setId($this->getId() . '.' . sizeof($actions)); + $action->setId($this->getId() . '.' . count($actions)); } $this->setActions($actions); @@ -99,6 +106,8 @@ public function addAction(ActionInterface $action) } /** + * As html + * * @return string */ public function asHtml() @@ -111,6 +120,8 @@ public function asHtml() } /** + * Return new child element + * * @return $this */ public function getNewChildElement() @@ -129,6 +140,8 @@ public function getNewChildElement() } /** + * Return as html recursive + * * @return string */ public function asHtmlRecursive() @@ -142,6 +155,8 @@ public function asHtmlRecursive() } /** + * Add string + * * @param string $format * @return string * @SuppressWarnings(PHPMD.UnusedFormalParameter) @@ -153,6 +168,8 @@ public function asString($format = '') } /** + * Return string as recursive + * * @param int $level * @return string */ @@ -166,6 +183,8 @@ public function asStringRecursive($level = 0) } /** + * Process + * * @return $this */ public function process() diff --git a/app/code/Magento/Rule/Model/Condition/AbstractCondition.php b/app/code/Magento/Rule/Model/Condition/AbstractCondition.php index d58af06da94cf..67fc3590ac501 100644 --- a/app/code/Magento/Rule/Model/Condition/AbstractCondition.php +++ b/app/code/Magento/Rule/Model/Condition/AbstractCondition.php @@ -3,7 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); namespace Magento\Rule\Model\Condition; @@ -387,7 +386,7 @@ public function getValueSelectOptions() public function getValueParsed() { if (!$this->hasValueParsed()) { - $value = $this->getData('value'); + $value = $this->getValue(); if (is_array($value) && count($value) === 1) { $value = reset($value); } @@ -766,7 +765,7 @@ public function asStringRecursive($level = 0) /** * Validate product attribute value for condition * - * @param object|array|int|string|float|bool $validatedValue product attribute value + * @param object|array|int|string|float|bool|null $validatedValue product attribute value * @return bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) @@ -831,6 +830,7 @@ public function validateAttribute($validatedValue) case '{}': case '!{}': if (is_scalar($validatedValue) && is_array($value)) { + $validatedValue = (string)$validatedValue; foreach ($value as $item) { if (stripos($validatedValue, (string)$item) !== false) { $result = true; @@ -878,14 +878,15 @@ public function validateAttribute($validatedValue) /** * Case and type insensitive comparison of values * - * @param string|int|float $validatedValue - * @param string|int|float $value + * @param string|int|float|null $validatedValue + * @param string|int|float|null $value * @param bool $strict * @return bool */ protected function _compareValues($validatedValue, $value, $strict = true) { - if ($strict && is_numeric($validatedValue) && is_numeric($value)) { + if (null === $value || null === $validatedValue || + $strict && is_numeric($validatedValue) && is_numeric($value)) { return $validatedValue == $value; } diff --git a/app/code/Magento/Rule/Model/Condition/Combine.php b/app/code/Magento/Rule/Model/Condition/Combine.php index 48873aec66295..a8a8e5fb0f843 100644 --- a/app/code/Magento/Rule/Model/Condition/Combine.php +++ b/app/code/Magento/Rule/Model/Condition/Combine.php @@ -6,6 +6,8 @@ namespace Magento\Rule\Model\Condition; /** + * Combine + * * @api * @since 100.0.2 */ @@ -22,6 +24,8 @@ class Combine extends AbstractCondition protected $_logger; /** + * Construct + * * @param Context $context * @param array $data */ @@ -54,6 +58,8 @@ public function __construct(Context $context, array $data = []) /* start aggregator methods */ /** + * Load aggregation options + * * @return $this */ public function loadAggregatorOptions() @@ -63,6 +69,8 @@ public function loadAggregatorOptions() } /** + * Return agregator selected options + * * @return array */ public function getAggregatorSelectOptions() @@ -75,6 +83,8 @@ public function getAggregatorSelectOptions() } /** + * Get Agregator name + * * @return string */ public function getAggregatorName() @@ -83,6 +93,8 @@ public function getAggregatorName() } /** + * Return agregator element + * * @return object */ public function getAggregatorElement() @@ -112,6 +124,8 @@ public function getAggregatorElement() /* end aggregator methods */ /** + * Load value options + * * @return $this */ public function loadValueOptions() @@ -121,6 +135,8 @@ public function loadValueOptions() } /** + * Adds condition + * * @param object $condition * @return $this */ @@ -134,7 +150,7 @@ public function addCondition($condition) $conditions[] = $condition; if (!$condition->getId()) { - $condition->setId($this->getId() . '--' . sizeof($conditions)); + $condition->setId($this->getId() . '--' . count($conditions)); } $this->setData($this->getPrefix(), $conditions); @@ -142,6 +158,8 @@ public function addCondition($condition) } /** + * Return value element type + * * @return string */ public function getValueElementType() @@ -181,6 +199,8 @@ public function asArray(array $arrAttributes = []) } /** + * As xml + * * @param string $containerKey * @param string $itemKey * @return string @@ -202,6 +222,8 @@ public function asXml($containerKey = 'conditions', $itemKey = 'condition') } /** + * Load array + * * @param array $arr * @param string $key * @return $this @@ -230,6 +252,8 @@ public function loadArray($arr, $key = 'conditions') } /** + * Load xml + * * @param array|string $xml * @return $this */ @@ -247,6 +271,8 @@ public function loadXml($xml) } /** + * As html + * * @return string */ public function asHtml() @@ -263,6 +289,8 @@ public function asHtml() } /** + * Get new child element + * * @return $this */ public function getNewChildElement() @@ -282,6 +310,8 @@ public function getNewChildElement() } /** + * As html recursive + * * @return string */ public function asHtmlRecursive() @@ -300,6 +330,8 @@ public function asHtmlRecursive() } /** + * As string + * * @param string $format * @return string * @SuppressWarnings(PHPMD.UnusedFormalParameter) @@ -311,6 +343,8 @@ public function asString($format = '') } /** + * As string recursive + * * @param int $level * @return string */ @@ -324,6 +358,8 @@ public function asStringRecursive($level = 0) } /** + * Validate + * * @param \Magento\Framework\Model\AbstractModel $model * @return bool */ @@ -374,6 +410,8 @@ protected function _isValid($entity) } /** + * Set js From object + * * @param \Magento\Framework\Data\Form $form * @return $this */ diff --git a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php index f44b788354103..a0812aeaa9c5e 100644 --- a/app/code/Magento/Rule/Model/Condition/Sql/Builder.php +++ b/app/code/Magento/Rule/Model/Condition/Sql/Builder.php @@ -270,25 +270,22 @@ private function buildConditions(AbstractCollection $collection, Combine $combin $conditions = ''; $attributeField = ''; foreach ($combine->getConditions() as $condition) { - if ($condition->getData('attribute') === \Magento\Catalog\Api\Data\ProductInterface::SKU) { + if ($condition->getData('attribute') === \Magento\Catalog\Api\Data\ProductInterface::SKU + && $condition->getData('operator') === '()' + ) { $conditions = $condition->getData('value'); - $attributeField = $condition->getMappedSqlField(); + $attributeField = $this->_connection->quoteIdentifier($condition->getMappedSqlField()); } } if (!empty($conditions) && !empty($attributeField)) { - $conditions = explode(',', $conditions); - foreach ($conditions as &$condition) { - $condition = trim($condition); - } - $conditions = implode(', ', $conditions); + $conditions = $this->_connection->quote( + array_map('trim', explode(',', $conditions)) + ); + $collection->getSelect()->reset(Select::ORDER); $collection->getSelect()->order( - $this->_connection->quoteInto( - "FIELD(?, ?)", - [ - $attributeField, - $conditions - ] + $this->_expressionFactory->create( + ['expression' => "FIELD($attributeField, $conditions)"] ) ); } diff --git a/app/code/Magento/Rule/Test/Unit/Model/Condition/AbstractConditionTest.php b/app/code/Magento/Rule/Test/Unit/Model/Condition/AbstractConditionTest.php index 0ba41af04a1b3..52653197e3981 100644 --- a/app/code/Magento/Rule/Test/Unit/Model/Condition/AbstractConditionTest.php +++ b/app/code/Magento/Rule/Test/Unit/Model/Condition/AbstractConditionTest.php @@ -55,14 +55,14 @@ public function validateAttributeDataProvider() ['0', '==', 1, false], ['1', '==', 1, true], ['x', '==', 'x', true], - ['x', '==', '0', false], + ['x', '==', 0, false], [1, '!=', 1, false], [0, '!=', 1, true], ['0', '!=', 1, true], ['1', '!=', 1, false], ['x', '!=', 'x', false], - ['x', '!=', '0', true], + ['x', '!=', 0, true], [1, '==', [1], true], [1, '!=', [1], false], @@ -164,15 +164,15 @@ public function validateAttributeArrayInputTypeDataProvider() [[1, 2, 3], '{}', '1', true, 'grid'], [[1, 2, 3], '{}', '8', false, 'grid'], - [[1, 2, 3], '{}', '5', false, 'grid'], + [[1, 2, 3], '{}', 5, false, 'grid'], [[1, 2, 3], '{}', [2, 3, 4], true, 'grid'], [[1, 2, 3], '{}', [4], false, 'grid'], [[3], '{}', [], false, 'grid'], [1, '{}', 1, false, 'grid'], [1, '!{}', [1, 2, 3], false, 'grid'], [[1], '{}', null, false, 'grid'], - ['null', '{}', 'null', true, 'input'], - ['null', '!{}', 'null', false, 'input'], + [null, '{}', null, true, 'input'], + [null, '!{}', null, false, 'input'], [null, '{}', [1], false, 'input'], [[1, 2, 3], '()', 1, true, 'select'], diff --git a/app/code/Magento/Rule/registration.php b/app/code/Magento/Rule/registration.php index 00c72c5c0a9ba..21bce7ba05833 100644 --- a/app/code/Magento/Rule/registration.php +++ b/app/code/Magento/Rule/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Rule', __DIR__); diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Address/Form.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Address/Form.php index 1efa149b390ef..06f2edba878ae 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Address/Form.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Address/Form.php @@ -108,6 +108,7 @@ protected function _prepareForm() { parent::_prepareForm(); $this->_form->setId('edit_form'); + $this->_form->setClass('admin__fieldset'); $this->_form->setMethod('post'); $this->_form->setAction( $this->getUrl('sales/*/addressSave', ['address_id' => $this->_getAddress()->getId()]) diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php index 3fe943c1b194c..bcdeb4e7d67de 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php @@ -14,6 +14,7 @@ /** * Order create address form + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Address extends \Magento\Sales\Block\Adminhtml\Order\Create\Form\AbstractForm @@ -216,15 +217,12 @@ public function getAddressCollectionJson() * Prepare Form and add elements to form * * @return $this - * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ protected function _prepareForm() { - $storeId = $this->getCreateOrderModel() - ->getSession() - ->getStoreId(); + $storeId = $this->getAddressStoreId(); $this->_storeManager->setCurrentStore($storeId); $fieldset = $this->_form->addFieldset('main', ['no_container' => true]); diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Giftmessage.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Giftmessage.php index 4a335805f8a1e..b314ee24c3e27 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Giftmessage.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Giftmessage.php @@ -97,7 +97,7 @@ public function getItems() } } - if (sizeof($items)) { + if (count($items)) { return $items; } diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/AbstractSidebar.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/AbstractSidebar.php index 06c6a9eb0652b..737ca446bb8e9 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/AbstractSidebar.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/AbstractSidebar.php @@ -3,6 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Sales\Block\Adminhtml\Order\Create\Sidebar; use Magento\Framework\Pricing\PriceCurrencyInterface; diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/Cart.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/Cart.php index f2200e1c1a108..a927b7177294a 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/Cart.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/Cart.php @@ -9,6 +9,7 @@ use Magento\Catalog\Model\Product; use Magento\Catalog\Pricing\Price\FinalPrice; +use Magento\Store\Model\ScopeInterface; /** * Adminhtml sales order create sidebar cart block @@ -146,4 +147,30 @@ private function getCartItemCustomPrice(Product $product): ?float return null; } + + /** + * @inheritdoc + */ + public function getItemCount() + { + $count = $this->getData('item_count'); + if ($count === null) { + $useQty = $this->_scopeConfig->getValue( + 'checkout/cart_link/use_qty', + ScopeInterface::SCOPE_STORE + ); + $allItems = $this->getItems(); + if ($useQty) { + $count = 0; + foreach ($allItems as $item) { + $count += $item->getQty(); + } + } else { + $count = count($allItems); + } + $this->setData('item_count', $count); + } + + return $count; + } } diff --git a/app/code/Magento/Sales/Block/Adminhtml/Totals.php b/app/code/Magento/Sales/Block/Adminhtml/Totals.php index 8172a3c0db4ad..68843952035c8 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Totals.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Totals.php @@ -5,7 +5,7 @@ */ namespace Magento\Sales\Block\Adminhtml; -use Magento\Sales\Model\Order; +use Magento\Framework\DataObject; /** * Adminhtml sales totals block @@ -57,60 +57,65 @@ public function formatValue($total) protected function _initTotals() { $this->_totals = []; - $this->_totals['subtotal'] = new \Magento\Framework\DataObject( + $order = $this->getSource(); + + $this->_totals['subtotal'] = new DataObject( [ 'code' => 'subtotal', - 'value' => $this->getSource()->getSubtotal(), - 'base_value' => $this->getSource()->getBaseSubtotal(), + 'value' => $order->getSubtotal(), + 'base_value' => $order->getBaseSubtotal(), 'label' => __('Subtotal'), ] ); /** - * Add shipping + * Add discount */ - if (!$this->getSource()->getIsVirtual() && ((double)$this->getSource()->getShippingAmount() || - $this->getSource()->getShippingDescription()) - ) { - $shippingLabel = __('Shipping & Handling'); - if ($this->isFreeShipping($this->getOrder()) && $this->getSource()->getDiscountDescription()) { - $shippingLabel .= sprintf(' (%s)', $this->getSource()->getDiscountDescription()); + if ((double)$order->getDiscountAmount() != 0) { + if ($order->getDiscountDescription()) { + $discountLabel = __('Discount (%1)', $order->getDiscountDescription()); + } else { + $discountLabel = __('Discount'); } - $this->_totals['shipping'] = new \Magento\Framework\DataObject( + $this->_totals['discount'] = new DataObject( [ - 'code' => 'shipping', - 'value' => $this->getSource()->getShippingAmount(), - 'base_value' => $this->getSource()->getBaseShippingAmount(), - 'label' => $shippingLabel, + 'code' => 'discount', + 'value' => $order->getDiscountAmount(), + 'base_value' => $order->getBaseDiscountAmount(), + 'label' => $discountLabel, ] ); } /** - * Add discount + * Add shipping */ - if ((double)$this->getSource()->getDiscountAmount() != 0) { - if ($this->getSource()->getDiscountDescription()) { - $discountLabel = __('Discount (%1)', $this->getSource()->getDiscountDescription()); - } else { - $discountLabel = __('Discount'); + if (!$order->getIsVirtual() + && ((double)$order->getShippingAmount() + || $order->getShippingDescription()) + ) { + $shippingLabel = __('Shipping & Handling'); + + if ($order->getCouponCode() && !isset($this->_totals['discount'])) { + $shippingLabel .= " ({$order->getCouponCode()})"; } - $this->_totals['discount'] = new \Magento\Framework\DataObject( + + $this->_totals['shipping'] = new DataObject( [ - 'code' => 'discount', - 'value' => $this->getSource()->getDiscountAmount(), - 'base_value' => $this->getSource()->getBaseDiscountAmount(), - 'label' => $discountLabel, + 'code' => 'shipping', + 'value' => $order->getShippingAmount(), + 'base_value' => $order->getBaseShippingAmount(), + 'label' => $shippingLabel, ] ); } - $this->_totals['grand_total'] = new \Magento\Framework\DataObject( + $this->_totals['grand_total'] = new DataObject( [ 'code' => 'grand_total', 'strong' => true, - 'value' => $this->getSource()->getGrandTotal(), - 'base_value' => $this->getSource()->getBaseGrandTotal(), + 'value' => $order->getGrandTotal(), + 'base_value' => $order->getBaseGrandTotal(), 'label' => __('Grand Total'), 'area' => 'footer', ] @@ -118,23 +123,4 @@ protected function _initTotals() return $this; } - - /** - * Availability of free shipping in at least one order item - * - * @param Order $order - * @return bool - */ - private function isFreeShipping(Order $order): bool - { - $isFreeShipping = false; - foreach ($order->getItems() as $orderItem) { - if ($orderItem->getFreeShipping() == '1') { - $isFreeShipping = true; - break; - } - } - - return $isFreeShipping; - } } diff --git a/app/code/Magento/Sales/Block/Order/History.php b/app/code/Magento/Sales/Block/Order/History.php index c06a5d8b24c1e..09300424212fe 100644 --- a/app/code/Magento/Sales/Block/Order/History.php +++ b/app/code/Magento/Sales/Block/Order/History.php @@ -188,4 +188,14 @@ public function getBackUrl() { return $this->getUrl('customer/account/'); } + + /** + * Get message for no orders. + * + * @return \Magento\Framework\Phrase + */ + public function getEmptyOrdersMessage() + { + return __('You have placed no orders.'); + } } diff --git a/app/code/Magento/Sales/Block/Order/Item/Renderer/DefaultRenderer.php b/app/code/Magento/Sales/Block/Order/Item/Renderer/DefaultRenderer.php index 83e66bbbce7cc..2e119d0bf887a 100644 --- a/app/code/Magento/Sales/Block/Order/Item/Renderer/DefaultRenderer.php +++ b/app/code/Magento/Sales/Block/Order/Item/Renderer/DefaultRenderer.php @@ -182,7 +182,7 @@ public function getFormatedOptionValue($optionValue) if ($this->string->strlen($optionValue) > 55) { $result['value'] = $result['value'] - . ' <a href="#" class="dots tooltip toggle" onclick="return false">...</a>'; + . ' ...'; $optionValue = nl2br($optionValue); $result = array_merge($result, ['full_view' => $optionValue]); } diff --git a/app/code/Magento/Sales/Block/Order/Totals.php b/app/code/Magento/Sales/Block/Order/Totals.php index 3720db76b5778..80ce5e2e689c6 100644 --- a/app/code/Magento/Sales/Block/Order/Totals.php +++ b/app/code/Magento/Sales/Block/Order/Totals.php @@ -8,6 +8,8 @@ use Magento\Sales\Model\Order; /** + * Order totals. + * * @api * @since 100.0.2 */ @@ -85,6 +87,8 @@ public function getOrder() } /** + * Sets order. + * * @param Order $order * @return $this */ @@ -118,20 +122,6 @@ protected function _initTotals() ['code' => 'subtotal', 'value' => $source->getSubtotal(), 'label' => __('Subtotal')] ); - /** - * Add shipping - */ - if (!$source->getIsVirtual() && ((double)$source->getShippingAmount() || $source->getShippingDescription())) { - $this->_totals['shipping'] = new \Magento\Framework\DataObject( - [ - 'code' => 'shipping', - 'field' => 'shipping_amount', - 'value' => $this->getSource()->getShippingAmount(), - 'label' => __('Shipping & Handling'), - ] - ); - } - /** * Add discount */ @@ -151,6 +141,25 @@ protected function _initTotals() ); } + /** + * Add shipping + */ + if (!$source->getIsVirtual() && ((double)$source->getShippingAmount() || $source->getShippingDescription())) { + $label = __('Shipping & Handling'); + if ($this->getSource()->getCouponCode() && !isset($this->_totals['discount'])) { + $label = __('Shipping & Handling (%1)', $this->getSource()->getCouponCode()); + } + + $this->_totals['shipping'] = new \Magento\Framework\DataObject( + [ + 'code' => 'shipping', + 'field' => 'shipping_amount', + 'value' => $this->getSource()->getShippingAmount(), + 'label' => $label, + ] + ); + } + $this->_totals['grand_total'] = new \Magento\Framework\DataObject( [ 'code' => 'grand_total', @@ -286,7 +295,6 @@ public function removeTotal($code) * $totalCode => $totalSortOrder * ) * - * * @param array $order * @return $this * @SuppressWarnings(PHPMD.UnusedFormalParameter) @@ -303,7 +311,7 @@ function ($code1, $code2) use ($order) { } /** - * get totals array for visualization + * Get totals array for visualization * * @param array|null $area * @return array diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/View.php b/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/View.php index 300b7ee37f2ef..6e7c2e5ce5609 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/View.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Invoice/AbstractInvoice/View.php @@ -1,17 +1,22 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Sales\Controller\Adminhtml\Invoice\AbstractInvoice; use Magento\Backend\App\Action\Context; +use Magento\Backend\Model\View\Result\ForwardFactory; use Magento\Framework\App\ObjectManager; use Magento\Framework\Registry; use Magento\Sales\Api\InvoiceRepositoryInterface; -use Magento\Sales\Model\Order\InvoiceRepository; +/** + * Class View + */ abstract class View extends \Magento\Backend\App\Action { /** @@ -27,7 +32,7 @@ abstract class View extends \Magento\Backend\App\Action protected $registry; /** - * @var \Magento\Backend\Model\View\Result\ForwardFactory + * @var ForwardFactory */ protected $resultForwardFactory; @@ -39,16 +44,20 @@ abstract class View extends \Magento\Backend\App\Action /** * @param Context $context * @param Registry $registry - * @param \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory + * @param ForwardFactory $resultForwardFactory + * @param InvoiceRepositoryInterface $invoiceRepository */ public function __construct( Context $context, Registry $registry, - \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory + ForwardFactory $resultForwardFactory, + InvoiceRepositoryInterface $invoiceRepository = null ) { - $this->registry = $registry; parent::__construct($context); + $this->registry = $registry; $this->resultForwardFactory = $resultForwardFactory; + $this->invoiceRepository = $invoiceRepository ?: + ObjectManager::getInstance()->get(InvoiceRepositoryInterface::class); } /** @@ -70,13 +79,14 @@ public function execute() } /** + * Get invoice using invoice Id from request params + * * @return \Magento\Sales\Model\Order\Invoice|bool */ protected function getInvoice() { try { - $invoice = $this->getInvoiceRepository() - ->get($this->getRequest()->getParam('invoice_id')); + $invoice = $this->invoiceRepository->get($this->getRequest()->getParam('invoice_id')); $this->registry->register('current_invoice', $invoice); } catch (\Exception $e) { $this->messageManager->addErrorMessage(__('Invoice capturing error')); @@ -85,19 +95,4 @@ protected function getInvoice() return $invoice; } - - /** - * @return InvoiceRepository - * - * @deprecated 100.1.0 - */ - private function getInvoiceRepository() - { - if ($this->invoiceRepository === null) { - $this->invoiceRepository = ObjectManager::getInstance() - ->get(InvoiceRepositoryInterface::class); - } - - return $this->invoiceRepository; - } } diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/AddComment.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/AddComment.php index 515c0753542a0..23dcae3a858cc 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/AddComment.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/AddComment.php @@ -1,23 +1,30 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Controller\Adminhtml\Order\Invoice; use Magento\Backend\App\Action\Context; +use Magento\Backend\Model\View\Result\ForwardFactory; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\LocalizedException; +use Magento\Sales\Api\InvoiceRepositoryInterface; use Magento\Sales\Model\Order\Email\Sender\InvoiceCommentSender; -use Magento\Sales\Model\Order\Invoice; -use Magento\Backend\App\Action; use Magento\Framework\Registry; use Magento\Framework\Controller\Result\JsonFactory; use Magento\Framework\View\Result\PageFactory; use Magento\Framework\Controller\Result\RawFactory; -class AddComment extends \Magento\Sales\Controller\Adminhtml\Invoice\AbstractInvoice\View +/** + * Class AddComment + */ +class AddComment extends \Magento\Sales\Controller\Adminhtml\Invoice\AbstractInvoice\View implements + HttpPostActionInterface { /** * @var InvoiceCommentSender @@ -39,29 +46,38 @@ class AddComment extends \Magento\Sales\Controller\Adminhtml\Invoice\AbstractInv */ protected $resultRawFactory; + /** + * @var InvoiceRepositoryInterface + */ + protected $invoiceRepository; + /** * @param Context $context * @param Registry $registry - * @param \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory + * @param ForwardFactory $resultForwardFactory * @param InvoiceCommentSender $invoiceCommentSender * @param JsonFactory $resultJsonFactory * @param PageFactory $resultPageFactory * @param RawFactory $resultRawFactory + * @param InvoiceRepositoryInterface $invoiceRepository */ public function __construct( Context $context, Registry $registry, - \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory, + ForwardFactory $resultForwardFactory, InvoiceCommentSender $invoiceCommentSender, JsonFactory $resultJsonFactory, PageFactory $resultPageFactory, - RawFactory $resultRawFactory + RawFactory $resultRawFactory, + InvoiceRepositoryInterface $invoiceRepository = null ) { $this->invoiceCommentSender = $invoiceCommentSender; $this->resultJsonFactory = $resultJsonFactory; $this->resultPageFactory = $resultPageFactory; $this->resultRawFactory = $resultRawFactory; - parent::__construct($context, $registry, $resultForwardFactory); + $this->invoiceRepository = $invoiceRepository ?: + ObjectManager::getInstance()->get(InvoiceRepositoryInterface::class); + parent::__construct($context, $registry, $resultForwardFactory, $invoiceRepository); } /** @@ -90,7 +106,7 @@ public function execute() ); $this->invoiceCommentSender->send($invoice, !empty($data['is_customer_notified']), $data['comment']); - $invoice->save(); + $this->invoiceRepository->save($invoice); /** @var \Magento\Backend\Model\View\Result\Page $resultPage */ $resultPage = $this->resultPageFactory->create(); diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Save.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Save.php index b4fa6fed6cdf5..f66ca37a47655 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Save.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Invoice/Save.php @@ -16,6 +16,7 @@ use Magento\Sales\Model\Order\ShipmentFactory; use Magento\Sales\Model\Order\Invoice; use Magento\Sales\Model\Service\InvoiceService; +use Magento\Sales\Helper\Data as SalesData; /** * Save invoice controller. @@ -56,6 +57,11 @@ class Save extends \Magento\Backend\App\Action implements HttpPostActionInterfac */ private $invoiceService; + /** + * @var SalesData + */ + private $salesData; + /** * @param Action\Context $context * @param Registry $registry @@ -63,6 +69,7 @@ class Save extends \Magento\Backend\App\Action implements HttpPostActionInterfac * @param ShipmentSender $shipmentSender * @param ShipmentFactory $shipmentFactory * @param InvoiceService $invoiceService + * @param SalesData $salesData */ public function __construct( Action\Context $context, @@ -70,7 +77,8 @@ public function __construct( InvoiceSender $invoiceSender, ShipmentSender $shipmentSender, ShipmentFactory $shipmentFactory, - InvoiceService $invoiceService + InvoiceService $invoiceService, + SalesData $salesData = null ) { $this->registry = $registry; $this->invoiceSender = $invoiceSender; @@ -78,6 +86,7 @@ public function __construct( $this->shipmentFactory = $shipmentFactory; $this->invoiceService = $invoiceService; parent::__construct($context); + $this->salesData = $salesData ?? $this->_objectManager->get(SalesData::class); } /** @@ -204,7 +213,7 @@ public function execute() // send invoice/shipment emails try { - if (!empty($data['send_email'])) { + if (!empty($data['send_email']) || $this->salesData->canSendNewInvoiceEmail()) { $this->invoiceSender->send($invoice); } } catch (\Exception $e) { @@ -213,7 +222,7 @@ public function execute() } if ($shipment) { try { - if (!empty($data['send_email'])) { + if (!empty($data['send_email']) || $this->salesData->canSendNewShipmentEmail()) { $this->shipmentSender->send($shipment); } } catch (\Exception $e) { diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php b/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php index d4c2e7b2d6854..95dace13d832f 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo/Total/Tax.php @@ -41,7 +41,7 @@ public function collect(\Magento\Sales\Model\Order\Creditmemo $creditmemo) $baseOrderItemTax = (double)$orderItem->getBaseTaxInvoiced(); $orderItemQty = (double)$orderItem->getQtyInvoiced(); - if ($orderItemTax && $orderItemQty) { + if ($orderItemQty) { /** * Check item tax amount */ diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoCommentSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoCommentSender.php index 09360d0685cf3..930791532539f 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoCommentSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoCommentSender.php @@ -73,6 +73,10 @@ public function send(Creditmemo $creditmemo, $notify = true, $comment = '') 'store' => $order->getStore(), 'formattedShippingAddress' => $this->getFormattedShippingAddress($order), 'formattedBillingAddress' => $this->getFormattedBillingAddress($order), + 'order_data' => [ + 'customer_name' => $order->getCustomerName(), + 'frontend_status_label' => $order->getFrontendStatusLabel() + ] ]; $transportObject = new DataObject($transport); diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoSender.php index 3cbd063641366..e6d528fb93a34 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/CreditmemoSender.php @@ -115,6 +115,12 @@ public function send(Creditmemo $creditmemo, $forceSyncMode = false) 'store' => $order->getStore(), 'formattedShippingAddress' => $this->getFormattedShippingAddress($order), 'formattedBillingAddress' => $this->getFormattedBillingAddress($order), + 'order_data' => [ + 'customer_name' => $order->getCustomerName(), + 'is_not_virtual' => $order->getIsNotVirtual(), + 'email_customer_note' => $order->getEmailCustomerNote(), + 'frontend_status_label' => $order->getFrontendStatusLabel() + ] ]; $transportObject = new DataObject($transport); diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceCommentSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceCommentSender.php index 32855f78c1571..9441f0e842925 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceCommentSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceCommentSender.php @@ -73,6 +73,10 @@ public function send(Invoice $invoice, $notify = true, $comment = '') 'store' => $order->getStore(), 'formattedShippingAddress' => $this->getFormattedShippingAddress($order), 'formattedBillingAddress' => $this->getFormattedBillingAddress($order), + 'order_data' => [ + 'customer_name' => $order->getCustomerName(), + 'frontend_status_label' => $order->getFrontendStatusLabel() + ] ]; $transportObject = new DataObject($transport); diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceSender.php index 3ac5342de74a6..79133af6d6fb8 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/InvoiceSender.php @@ -114,7 +114,13 @@ public function send(Invoice $invoice, $forceSyncMode = false) 'payment_html' => $this->getPaymentHtml($order), 'store' => $order->getStore(), 'formattedShippingAddress' => $this->getFormattedShippingAddress($order), - 'formattedBillingAddress' => $this->getFormattedBillingAddress($order) + 'formattedBillingAddress' => $this->getFormattedBillingAddress($order), + 'order_data' => [ + 'customer_name' => $order->getCustomerName(), + 'is_not_virtual' => $order->getIsNotVirtual(), + 'email_customer_note' => $order->getEmailCustomerNote(), + 'frontend_status_label' => $order->getFrontendStatusLabel() + ] ]; $transportObject = new DataObject($transport); diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/OrderCommentSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/OrderCommentSender.php index e162e01bd7555..4d37fc1b7769a 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/OrderCommentSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/OrderCommentSender.php @@ -70,6 +70,10 @@ public function send(Order $order, $notify = true, $comment = '') 'store' => $order->getStore(), 'formattedShippingAddress' => $this->getFormattedShippingAddress($order), 'formattedBillingAddress' => $this->getFormattedBillingAddress($order), + 'order_data' => [ + 'customer_name' => $order->getCustomerName(), + 'frontend_status_label' => $order->getFrontendStatusLabel() + ] ]; $transportObject = new DataObject($transport); diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php index bfbe1fb4fd7ff..c67804475cd65 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/OrderSender.php @@ -130,6 +130,13 @@ protected function prepareTemplate(Order $order) 'store' => $order->getStore(), 'formattedShippingAddress' => $this->getFormattedShippingAddress($order), 'formattedBillingAddress' => $this->getFormattedBillingAddress($order), + 'created_at_formatted' => $order->getCreatedAtFormatted(2), + 'order_data' => [ + 'customer_name' => $order->getCustomerName(), + 'is_not_virtual' => $order->getIsNotVirtual(), + 'email_customer_note' => $order->getEmailCustomerNote(), + 'frontend_status_label' => $order->getFrontendStatusLabel() + ] ]; $transportObject = new DataObject($transport); diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentCommentSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentCommentSender.php index b0b4907b96e70..ad305c8b7199f 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentCommentSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentCommentSender.php @@ -73,6 +73,10 @@ public function send(Shipment $shipment, $notify = true, $comment = '') 'store' => $order->getStore(), 'formattedShippingAddress' => $this->getFormattedShippingAddress($order), 'formattedBillingAddress' => $this->getFormattedBillingAddress($order), + 'order_data' => [ + 'customer_name' => $order->getCustomerName(), + 'frontend_status_label' => $order->getFrontendStatusLabel() + ] ]; $transportObject = new DataObject($transport); diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php index df28dec701290..4dbc10308f3be 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php @@ -114,7 +114,13 @@ public function send(Shipment $shipment, $forceSyncMode = false) 'payment_html' => $this->getPaymentHtml($order), 'store' => $order->getStore(), 'formattedShippingAddress' => $this->getFormattedShippingAddress($order), - 'formattedBillingAddress' => $this->getFormattedBillingAddress($order) + 'formattedBillingAddress' => $this->getFormattedBillingAddress($order), + 'order_data' => [ + 'customer_name' => $order->getCustomerName(), + 'is_not_virtual' => $order->getIsNotVirtual(), + 'email_customer_note' => $order->getEmailCustomerNote(), + 'frontend_status_label' => $order->getFrontendStatusLabel() + ] ]; $transportObject = new DataObject($transport); diff --git a/app/code/Magento/Sales/Model/Order/Payment/Info.php b/app/code/Magento/Sales/Model/Order/Payment/Info.php index fee846fe6a62c..479d96b5842d9 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/Info.php +++ b/app/code/Magento/Sales/Model/Order/Payment/Info.php @@ -192,6 +192,7 @@ public function getAdditionalInformation($key = null) */ public function unsAdditionalInformation($key = null) { + $this->initAdditionalInformation(); if ($key && isset($this->additionalInformation[$key])) { unset($this->additionalInformation[$key]); return $this->setData('additional_information', $this->additionalInformation); diff --git a/app/code/Magento/Sales/Model/Order/Payment/State/RegisterCaptureNotificationCommand.php b/app/code/Magento/Sales/Model/Order/Payment/State/RegisterCaptureNotificationCommand.php index d38e58d7341c1..2551092a64e9a 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/State/RegisterCaptureNotificationCommand.php +++ b/app/code/Magento/Sales/Model/Order/Payment/State/RegisterCaptureNotificationCommand.php @@ -11,6 +11,11 @@ use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\StatusResolver; +/** + * Class RegisterCaptureNotificationCommand + * + * Command that Register Capture Notification + */ class RegisterCaptureNotificationCommand implements CommandInterface { /** @@ -23,11 +28,12 @@ class RegisterCaptureNotificationCommand implements CommandInterface */ public function __construct(StatusResolver $statusResolver = null) { - $this->statusResolver = $statusResolver - ? : ObjectManager::getInstance()->get(StatusResolver::class); + $this->statusResolver = $statusResolver ?: ObjectManager::getInstance()->get(StatusResolver::class); } /** + * Registers a capture event for this payment + * * @param OrderPaymentInterface $payment * @param string|float|int $amount * @param OrderInterface $order @@ -35,7 +41,11 @@ public function __construct(StatusResolver $statusResolver = null) */ public function execute(OrderPaymentInterface $payment, $amount, OrderInterface $order) { - $state = $order->getState() ?: Order::STATE_PROCESSING; + $state = $order->getState(); + if (!$state || $state === Order::STATE_NEW || $state === Order::STATE_PENDING_PAYMENT) { + $state = Order::STATE_PROCESSING; + } + $status = null; $message = 'Registered notification about captured amount of %1.'; @@ -61,6 +71,8 @@ public function execute(OrderPaymentInterface $payment, $amount, OrderInterface } /** + * Sets the state and status of the order + * * @deprecated 100.2.0 Replaced by a StatusResolver class call. * * @param Order $order diff --git a/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php b/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php index 5bcf579a1cbf4..24ccf45d60145 100644 --- a/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php +++ b/app/code/Magento/Sales/Model/Order/Shipment/TrackRepository.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Model\Order\Shipment; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; @@ -14,7 +16,13 @@ use Magento\Sales\Api\Data\ShipmentTrackSearchResultInterfaceFactory; use Magento\Sales\Api\ShipmentTrackRepositoryInterface; use Magento\Sales\Model\Spi\ShipmentTrackResourceInterface; +use Magento\Sales\Model\ResourceModel\Order\Shipment\CollectionFactory; +use Magento\Framework\App\ObjectManager; +use Psr\Log\LoggerInterface; +/** + * Repository of shipment tracking information + */ class TrackRepository implements ShipmentTrackRepositoryInterface { /** @@ -37,23 +45,40 @@ class TrackRepository implements ShipmentTrackRepositoryInterface */ private $collectionProcessor; + /** + * @var CollectionFactory + */ + private $shipmentCollection; + + /** + * @var LoggerInterface + */ + private $logger; + /** * @param ShipmentTrackResourceInterface $trackResource * @param ShipmentTrackInterfaceFactory $trackFactory * @param ShipmentTrackSearchResultInterfaceFactory $searchResultFactory * @param CollectionProcessorInterface $collectionProcessor + * @param CollectionFactory|null $shipmentCollection + * @param LoggerInterface|null $logger */ public function __construct( ShipmentTrackResourceInterface $trackResource, ShipmentTrackInterfaceFactory $trackFactory, ShipmentTrackSearchResultInterfaceFactory $searchResultFactory, - CollectionProcessorInterface $collectionProcessor + CollectionProcessorInterface $collectionProcessor, + CollectionFactory $shipmentCollection = null, + LoggerInterface $logger = null ) { - $this->trackResource = $trackResource; $this->trackFactory = $trackFactory; $this->searchResultFactory = $searchResultFactory; $this->collectionProcessor = $collectionProcessor; + $this->shipmentCollection = $shipmentCollection ?: + ObjectManager::getInstance()->get(CollectionFactory::class); + $this->logger = $logger ?: + ObjectManager::getInstance()->get(LoggerInterface::class); } /** @@ -95,6 +120,16 @@ public function delete(ShipmentTrackInterface $entity) */ public function save(ShipmentTrackInterface $entity) { + $shipments = $this->shipmentCollection->create() + ->addFieldToFilter('order_id', $entity['order_id']) + ->addFieldToFilter('entity_id', $entity['parent_id']) + ->toArray(); + + if (empty($shipments['items'])) { + $this->logger->error('The shipment doesn\'t belong to the order.'); + throw new CouldNotSaveException(__('Could not save the shipment tracking.')); + } + try { $this->trackResource->save($entity); } catch (\Exception $e) { diff --git a/app/code/Magento/Sales/Model/ResourceModel/Status/Collection.php b/app/code/Magento/Sales/Model/ResourceModel/Status/Collection.php index 23d835db603df..83346d4528c22 100644 --- a/app/code/Magento/Sales/Model/ResourceModel/Status/Collection.php +++ b/app/code/Magento/Sales/Model/ResourceModel/Status/Collection.php @@ -1,12 +1,13 @@ <?php /** - * Oder statuses grid collection - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Sales\Model\ResourceModel\Status; +/** + * Order statuses grid collection. + */ class Collection extends \Magento\Sales\Model\ResourceModel\Order\Status\Collection { /** diff --git a/app/code/Magento/Sales/Model/Service/PaymentFailuresService.php b/app/code/Magento/Sales/Model/Service/PaymentFailuresService.php index a698276332af8..a05ed2be9c82c 100644 --- a/app/code/Magento/Sales/Model/Service/PaymentFailuresService.php +++ b/app/code/Magento/Sales/Model/Service/PaymentFailuresService.php @@ -130,10 +130,12 @@ public function handle( foreach ($sendTo as $recipient) { $transport = $this->transportBuilder ->setTemplateIdentifier($template) - ->setTemplateOptions([ - 'area' => FrontNameResolver::AREA_CODE, - 'store' => Store::DEFAULT_STORE_ID, - ]) + ->setTemplateOptions( + [ + 'area' => FrontNameResolver::AREA_CODE, + 'store' => Store::DEFAULT_STORE_ID, + ] + ) ->setTemplateVars($this->getTemplateVars($quote, $message, $checkoutType)) ->setFrom($this->getSendFrom($quote)) ->addTo($recipient['email'], $recipient['name']) @@ -170,6 +172,8 @@ private function getTemplateVars(Quote $quote, string $message, string $checkout 'customerEmail' => $quote->getBillingAddress()->getEmail(), 'billingAddress' => $quote->getBillingAddress(), 'shippingAddress' => $quote->getShippingAddress(), + 'billingAddressHtml' => $quote->getBillingAddress()->format('html'), + 'shippingAddressHtml' => $quote->getShippingAddress()->format('html'), 'shippingMethod' => $this->getConfigValue( 'carriers/' . $this->getShippingMethod($quote) . '/title', $quote diff --git a/app/code/Magento/Sales/Setup/Patch/Data/UpdateCreditmemoGridCurrencyCode.php b/app/code/Magento/Sales/Setup/Patch/Data/UpdateCreditmemoGridCurrencyCode.php deleted file mode 100644 index b143ae7c3ba7f..0000000000000 --- a/app/code/Magento/Sales/Setup/Patch/Data/UpdateCreditmemoGridCurrencyCode.php +++ /dev/null @@ -1,85 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Sales\Setup\Patch\Data; - -use Magento\Framework\DB\Adapter\Pdo\Mysql; -use Magento\Framework\Setup\ModuleDataSetupInterface; -use Magento\Framework\Setup\Patch\DataPatchInterface; -use Magento\Framework\Setup\Patch\PatchVersionInterface; -use Magento\Sales\Setup\SalesSetup; -use Magento\Sales\Setup\SalesSetupFactory; - -/** - * Update credit memo grid currency code. - */ -class UpdateCreditmemoGridCurrencyCode implements DataPatchInterface, PatchVersionInterface -{ - /** - * @var ModuleDataSetupInterface - */ - private $moduleDataSetup; - - /** - * @var SalesSetupFactory - */ - private $salesSetupFactory; - - /** - * @param ModuleDataSetupInterface $moduleDataSetup - * @param SalesSetupFactory $salesSetupFactory - */ - public function __construct( - ModuleDataSetupInterface $moduleDataSetup, - SalesSetupFactory $salesSetupFactory - ) { - $this->moduleDataSetup = $moduleDataSetup; - $this->salesSetupFactory = $salesSetupFactory; - } - - /** - * @inheritdoc - */ - public function apply() - { - /** @var SalesSetup $salesSetup */ - $salesSetup = $this->salesSetupFactory->create(['setup' => $this->moduleDataSetup]); - /** @var Mysql $connection */ - $connection = $salesSetup->getConnection(); - $creditMemoGridTable = $salesSetup->getTable('sales_creditmemo_grid'); - $orderTable = $salesSetup->getTable('sales_order'); - $select = $connection->select(); - $condition = 'so.entity_id = scg.order_id'; - $select->join(['so' => $orderTable], $condition, ['order_currency_code', 'base_currency_code']); - $sql = $connection->updateFromSelect($select, ['scg' => $creditMemoGridTable]); - $connection->query($sql); - } - - /** - * @inheritdoc - */ - public static function getDependencies() - { - return []; - } - - /** - * @inheritdoc - */ - public static function getVersion() - { - return '2.0.13'; - } - - /** - * @inheritdoc - */ - public function getAliases() - { - return []; - } -} diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddBundleProductToOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddBundleProductToOrderActionGroup.xml new file mode 100644 index 0000000000000..ee252990e0646 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddBundleProductToOrderActionGroup.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="AddBundleProductToOrderActionGroup"> + <annotations> + <description>Adds the provided Bundled Product with the provided Option to an Order. Fills in the provided Product Qty. Clicks on 'Add Selected Product(s) to Order'.</description> + </annotations> + <arguments> + <argument name="product"/> + <argument name="quantity" type="string" defaultValue="1"/> + </arguments> + + <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickAddProducts"/> + <fillField selector="{{AdminOrderFormItemsSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillSkuFilterBundle"/> + <click selector="{{AdminOrderFormItemsSection.search}}" stepKey="clickSearchBundle"/> + <scrollTo selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" x="0" y="-100" stepKey="scrollToCheckColumn"/> + <checkOption selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" stepKey="selectBundleProduct"/> + <waitForElementVisible selector="{{AdminOrderFormBundleProductSection.quantity}}" stepKey="waitForBundleOptionLoad"/> + <wait time="2" stepKey="waitForOptionsToLoad"/> + <fillField selector="{{AdminOrderFormBundleProductSection.quantity}}" userInput="{{quantity}}" stepKey="fillQuantity"/> + <click selector="{{AdminOrderFormConfigureProductSection.ok}}" stepKey="clickOk"/> + <scrollTo selector="{{AdminOrderFormItemsSection.addSelected}}" x="0" y="-100" stepKey="scrollToAddSelectedButton"/> + <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="clickAddSelectedProducts"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddBundleProductToOrderAndCheckPriceInGridActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddBundleProductToOrderAndCheckPriceInGridActionGroup.xml new file mode 100644 index 0000000000000..71205ff9808d0 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddBundleProductToOrderAndCheckPriceInGridActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddBundleProductToOrderAndCheckPriceInGridActionGroup" extends="AddBundleProductToOrderActionGroup"> + <annotations> + <description>EXTENDS: addBundleProductToOrder. Validates that the provided Product Price is present and correct in the 'Items Ordered' section.</description> + </annotations> + <arguments> + <argument name="price" type="string"/> + </arguments> + + <grabTextFrom selector="{{AdminOrderFormItemsSection.rowPrice('1')}}" stepKey="grabProductPriceFromGrid" after="clickOk"/> + <assertEquals stepKey="assertProductPriceInGrid" message="Bundle product price in grid should be equal {{price}}" after="grabProductPriceFromGrid"> + <expectedResult type="string">{{price}}</expectedResult> + <actualResult type="variable">grabProductPriceFromGrid</actualResult> + </assertEquals> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddConfigurableProductToOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddConfigurableProductToOrderActionGroup.xml new file mode 100644 index 0000000000000..dee2af6cd4053 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddConfigurableProductToOrderActionGroup.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddConfigurableProductToOrderActionGroup"> + <annotations> + <description>Adds the provided Configurable Product with the provided Option to an Order. Fills in the provided Product Qty. Clicks on 'Add Selected Product(s) to Order'.</description> + </annotations> + <arguments> + <argument name="product"/> + <argument name="attribute"/> + <argument name="option"/> + </arguments> + + <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickAddProducts"/> + <fillField selector="{{AdminOrderFormItemsSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillSkuFilterConfigurable"/> + <click selector="{{AdminOrderFormItemsSection.search}}" stepKey="clickSearchConfigurable"/> + <scrollTo selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" x="0" y="-100" stepKey="scrollToCheckColumn"/> + <checkOption selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" stepKey="selectConfigurableProduct"/> + <waitForElementVisible selector="{{AdminOrderFormConfigureProductSection.optionSelect(attribute.default_label)}}" stepKey="waitForConfigurablePopover"/> + <wait time="2" stepKey="waitForOptionsToLoad"/> + <selectOption selector="{{AdminOrderFormConfigureProductSection.optionSelect(attribute.default_label)}}" userInput="{{option.name}}" stepKey="selectionConfigurableOption"/> + <click selector="{{AdminOrderFormConfigureProductSection.ok}}" stepKey="clickOkConfigurablePopover"/> + <scrollTo selector="{{AdminOrderFormItemsSection.addSelected}}" x="0" y="-100" stepKey="scrollToAddSelectedButton"/> + <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="clickAddSelectedProducts"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddConfigurableProductToOrderFromAdminActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddConfigurableProductToOrderFromAdminActionGroup.xml new file mode 100644 index 0000000000000..200568fe2c05a --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddConfigurableProductToOrderFromAdminActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddConfigurableProductToOrderFromAdminActionGroup" extends="AddConfigurableProductToOrderActionGroup"> + <annotations> + <description>EXTENDS: addConfigurableProductToOrder. Selects the provided Option to the Configurable Product.</description> + </annotations> + + <waitForElementVisible selector="{{AdminOrderFormConfigureProductSection.optionSelect(attribute.default_frontend_label)}}" stepKey="waitForConfigurablePopover"/> + <selectOption selector="{{AdminOrderFormConfigureProductSection.optionSelect(attribute.default_frontend_label)}}" userInput="{{option.label}}" stepKey="selectionConfigurableOption"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddDownloadableProductToOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddDownloadableProductToOrderActionGroup.xml new file mode 100644 index 0000000000000..3018b4308b193 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddDownloadableProductToOrderActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddDownloadableProductToOrderActionGroup"> + <annotations> + <description>Adds a Downloadable Product to an Order. Clicks on 'Add Selected Product(s) to Order'.</description> + </annotations> + + <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickAddProducts"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> + <fillField selector="{{AdminOrderFormItemsSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillSkuFilterDownloadable"/> + <click selector="{{AdminOrderFormItemsSection.search}}" stepKey="clickSearchDownloadable"/> + <scrollTo selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" x="0" y="-100" stepKey="scrollToCheckColumn"/> + <checkOption selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" stepKey="selectDownloadableProduct"/> + <waitForElementVisible selector="{{AdminOrderFormDownloadableProductSection.optionSelect(link.title)}}" stepKey="waitForLinkLoad"/> + <click selector="{{AdminOrderFormDownloadableProductSection.optionSelect(link.title)}}" stepKey="selectLink"/> + <fillField selector="{{AdminOrderFormDownloadableProductSection.quantity}}" userInput="{{quantity}}" stepKey="setQuantity"/> + <click selector="{{AdminOrderFormDownloadableProductSection.ok}}" stepKey="confirmConfiguration"/> + <scrollTo selector="{{AdminOrderFormItemsSection.addSelected}}" x="0" y="-100" stepKey="scrollToAddSelectedButton"/> + <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="clickAddSelectedProducts"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddGroupedProductOptionToOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddGroupedProductOptionToOrderActionGroup.xml new file mode 100644 index 0000000000000..228198527b911 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddGroupedProductOptionToOrderActionGroup.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddGroupedProductOptionToOrderActionGroup"> + <annotations> + <description>Adds the provided Grouped Product with the provided Option to an Order. Fills in the provided Product Qty. Clicks on 'Add Selected Product(s) to Order'.</description> + </annotations> + <arguments> + <argument name="product"/> + <argument name="option"/> + <argument name="quantity" type="string" defaultValue="1"/> + </arguments> + + <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickAddProducts"/> + <fillField selector="{{AdminOrderFormItemsSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillSkuFilterGrouped"/> + <click selector="{{AdminOrderFormItemsSection.search}}" stepKey="clickSearchGrouped"/> + <scrollTo selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" x="0" y="-100" stepKey="scrollToCheckColumn"/> + <checkOption selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" stepKey="selectGroupedProduct"/> + <waitForElementVisible selector="{{AdminOrderFormGroupedProductSection.optionQty(option.sku)}}" stepKey="waitForGroupedOptionLoad"/> + <wait time="2" stepKey="waitForOptionsToLoad"/> + <fillField selector="{{AdminOrderFormGroupedProductSection.optionQty(option.sku)}}" userInput="{{quantity}}" stepKey="fillOptionQuantity"/> + <click selector="{{AdminOrderFormConfigureProductSection.ok}}" stepKey="clickOk"/> + <scrollTo selector="{{AdminOrderFormItemsSection.addSelected}}" x="0" y="-100" stepKey="scrollToAddSelectedButton"/> + <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="clickAddSelectedProducts"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddSimpleProductToOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddSimpleProductToOrderActionGroup.xml new file mode 100644 index 0000000000000..8d5062c79f828 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddSimpleProductToOrderActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddSimpleProductToOrderActionGroup"> + <annotations> + <description>Adds the provided Simple Product to an Order. Fills in the provided Product Qty. Clicks on 'Add Selected Product(s) to Order'.</description> + </annotations> + <arguments> + <argument name="product" defaultValue="_defaultProduct" type="entity"/> + <argument name="productQty" defaultValue="1" type="string"/> + </arguments> + + <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickAddProducts"/> + <fillField selector="{{AdminOrderFormItemsSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillSkuFilter"/> + <click selector="{{AdminOrderFormItemsSection.search}}" stepKey="clickSearch"/> + <scrollTo selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" x="0" y="-100" stepKey="scrollToCheckColumn"/> + <checkOption selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" stepKey="selectProduct"/> + <fillField selector="{{AdminOrderFormItemsSection.rowQty('1')}}" userInput="{{productQty}}" stepKey="fillProductQty"/> + <scrollTo selector="{{AdminOrderFormItemsSection.addSelected}}" x="0" y="-100" stepKey="scrollToAddSelectedButton"/> + <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="clickAddSelectedProducts"/> + <wait time="5" stepKey="waitForOptionsToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddSimpleProductWithQtyToOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddSimpleProductWithQtyToOrderActionGroup.xml new file mode 100644 index 0000000000000..e0d18fa9c2340 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AddSimpleProductWithQtyToOrderActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddSimpleProductWithQtyToOrderActionGroup" extends="AddSimpleProductToOrderActionGroup"> + <arguments> + <argument name="product" defaultValue="_defaultProduct" type="entity"/> + <argument name="productQty" type="string"/> + </arguments> + <fillField selector="{{AdminOrderFormItemsSection.rowQty('1')}}" userInput="{{productQty}}" stepKey="fillProductQty"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminAssertRefundOrderStatusInCommentsHistoryActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminAssertRefundOrderStatusInCommentsHistoryActionGroup.xml index aefc8abc3f05c..e5b581e14d8b0 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminAssertRefundOrderStatusInCommentsHistoryActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminAssertRefundOrderStatusInCommentsHistoryActionGroup.xml @@ -20,7 +20,7 @@ <!-- Assert refund order status in Comments History --> <click selector="{{AdminOrderDetailsOrderViewSection.commentsHistory}}" stepKey="clickOnTabCommentsHistory"/> <waitForPageLoad stepKey="waitForComments"/> - <see userInput="{{orderStatus}}" selector="{{ViewOrderSection.orderStatus}}" stepKey="assertRefundOrderStatusInCommentsHistory"/> - <see userInput="{{refundMessage}}" selector="{{ViewOrderSection.capturedAmountTextUnsubmitted}}" stepKey="assertOrderStatus"/> + <see userInput="{{orderStatus}}" selector="{{AdminOrderCommentsTabSection.orderNotesList}}" stepKey="assertRefundOrderStatusInCommentsHistory"/> + <see userInput="{{refundMessage}}" selector="{{AdminOrderCommentsTabSection.orderComment}}" stepKey="assertOrderStatus"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreateInvoiceActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreateInvoiceActionGroup.xml index 416d3f488dd1f..a90c16428ca03 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreateInvoiceActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreateInvoiceActionGroup.xml @@ -15,16 +15,4 @@ <waitForPageLoad stepKey="waitForLoadPage"/> <see userInput="The invoice has been created." stepKey="seeMessage"/> </actionGroup> - <actionGroup name="AdminCreateInvoiceAndShipmentActionGroup" extends="AdminCreateInvoiceActionGroup"> - <checkOption selector="{{AdminInvoicePaymentShippingSection.CreateShipment}}" stepKey="checkCreateShipment" after="waitForInvoicePage"/> - <see userInput="You created the invoice and shipment." stepKey="seeMessage"/> - </actionGroup> - <actionGroup name="AdminCreateInvoiceAndCreditMemoActionGroup" extends="AdminCreateInvoiceActionGroup"> - <click selector="{{AdminOrderDetailsMainActionsSection.creditMemo}}" stepKey="pushButtonCreditMemo" after="seeMessage"/> - <waitForPageLoad stepKey="waitForLoadingCreditMemoPage" after="pushButtonCreditMemo"/> - <scrollTo selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="scrollToBottom" after="waitForLoadingCreditMemoPage"/> - <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickSubmitRefund" after="scrollToBottom"/> - <waitForPageLoad stepKey="waitForMainOrderPageLoad" after="clickSubmitRefund"/> - <see userInput="You created the credit memo." stepKey="seeCreditMemoMessage" after="waitForMainOrderPageLoad"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreateInvoiceAndCreditMemoActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreateInvoiceAndCreditMemoActionGroup.xml new file mode 100644 index 0000000000000..a69cd8688c6c5 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreateInvoiceAndCreditMemoActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateInvoiceAndCreditMemoActionGroup" extends="AdminCreateInvoiceActionGroup"> + <click selector="{{AdminOrderDetailsMainActionsSection.creditMemo}}" stepKey="pushButtonCreditMemo" after="seeMessage"/> + <waitForPageLoad stepKey="waitForLoadingCreditMemoPage" after="pushButtonCreditMemo"/> + <scrollTo selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="scrollToBottom" after="waitForLoadingCreditMemoPage"/> + <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickSubmitRefund" after="scrollToBottom"/> + <waitForPageLoad stepKey="waitForMainOrderPageLoad" after="clickSubmitRefund"/> + <see userInput="You created the credit memo." stepKey="seeCreditMemoMessage" after="waitForMainOrderPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreateInvoiceAndShipmentActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreateInvoiceAndShipmentActionGroup.xml new file mode 100644 index 0000000000000..047b78a81c2cd --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreateInvoiceAndShipmentActionGroup.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="AdminCreateInvoiceAndShipmentActionGroup" extends="AdminCreateInvoiceActionGroup"> + <checkOption selector="{{AdminInvoicePaymentShippingSection.CreateShipment}}" stepKey="checkCreateShipment" after="waitForInvoicePage"/> + <see userInput="You created the invoice and shipment." stepKey="seeMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreditMemoActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreditMemoActionGroup.xml deleted file mode 100644 index 68cd1c42e1dd8..0000000000000 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreditMemoActionGroup.xml +++ /dev/null @@ -1,70 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Check customer information is correct in credit memo--> - <actionGroup name="verifyBasicCreditMemoInformation"> - <annotations> - <description>Validates that the provided Customer, Shipping/Billing Address and Customer Group are present and correct on the Admin Credit Memo view page.</description> - </annotations> - <arguments> - <argument name="customer" defaultValue=""/> - <argument name="shippingAddress" defaultValue=""/> - <argument name="billingAddress" defaultValue=""/> - <argument name="customerGroup" defaultValue="GeneralCustomerGroup"/> - </arguments> - - <see selector="{{AdminCreditMemoOrderInformationSection.customerName}}" userInput="{{customer.firstname}}" stepKey="seeCustomerName"/> - <see selector="{{AdminCreditMemoOrderInformationSection.customerEmail}}" userInput="{{customer.email}}" stepKey="seeCustomerEmail"/> - <see selector="{{AdminCreditMemoOrderInformationSection.customerGroup}}" userInput="{{customerGroup.code}}" stepKey="seeCustomerGroup"/> - - <see selector="{{AdminCreditMemoAddressInformationSection.billingAddress}}" userInput="{{billingAddress.street[0]}}" stepKey="seeBillingAddressStreet"/> - <see selector="{{AdminCreditMemoAddressInformationSection.billingAddress}}" userInput="{{billingAddress.city}}" stepKey="seeBillingAddressCity"/> - <see selector="{{AdminCreditMemoAddressInformationSection.billingAddress}}" userInput="{{billingAddress.country_id}}" stepKey="seeBillingAddressCountry"/> - <see selector="{{AdminCreditMemoAddressInformationSection.billingAddress}}" userInput="{{billingAddress.postcode}}" stepKey="seeBillingAddressPostcode"/> - - <see selector="{{AdminCreditMemoAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.street[0]}}" stepKey="seeShippingAddressStreet"/> - <see selector="{{AdminCreditMemoAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.city}}" stepKey="seeShippingAddressCity"/> - <see selector="{{AdminCreditMemoAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.country_id}}" stepKey="seeShippingAddressCountry"/> - <see selector="{{AdminCreditMemoAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.postcode}}" stepKey="seeShippingAddressPostcode"/> - </actionGroup> - - <actionGroup name="seeProductInItemsRefunded"> - <annotations> - <description>Validates that the provided Product appears in the 'Product' column on the Admin Credit Memo view page.</description> - </annotations> - <arguments> - <argument name="product"/> - </arguments> - - <see selector="{{AdminCreditMemoItemsSection.skuColumn}}" userInput="{{product.sku}}" stepKey="seeProductSkuInGrid"/> - </actionGroup> - - <actionGroup name="StartToCreateCreditMemoActionGroup"> - <arguments> - <argument name="orderId" type="string"/> - </arguments> - <amOnPage url="{{AdminOrderPage.url(orderId)}}" stepKey="navigateToOrderPage"/> - <click selector="{{AdminOrderDetailsMainActionsSection.creditMemo}}" stepKey="clickCreditMemo"/> - <waitForElementVisible selector="{{AdminHeaderSection.pageTitle}}" stepKey="waitForPageTitle"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Memo" stepKey="seeNewMemoPageTitle"/> - </actionGroup> - <actionGroup name="SubmitCreditMemoActionGroup"> - <grabFromCurrentUrl regex="~/order_id/(\d+)/~" stepKey="grabOrderId"/> - <waitForElementVisible selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="waitButtonEnabled"/> - <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickSubmitCreditMemo"/> - <waitForElementVisible selector="{{AdminMessagesSection.successMessage}}" stepKey="waitForMessageAppears"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="You created the credit memo." stepKey="seeCreditMemoCreateSuccess"/> - <seeInCurrentUrl url="{{AdminOrderDetailsPage.url}}$grabOrderId" stepKey="seeViewOrderPageCreditMemo"/> - </actionGroup> - <actionGroup name="UpdateCreditMemoTotalsActionGroup"> - <waitForElementVisible selector="{{AdminCreditMemoTotalSection.updateTotals}}" stepKey="waitUpdateTotalsButtonEnabled"/> - <click selector="{{AdminCreditMemoTotalSection.updateTotals}}" stepKey="clickUpdateTotals"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminFastCreateInvoiceActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminFastCreateInvoiceActionGroup.xml new file mode 100644 index 0000000000000..b5175b6c3002e --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminFastCreateInvoiceActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminFastCreateInvoiceActionGroup"> + <annotations> + <description>Clicks on 'Invoice' on the Admin Orders view page. Clicks on 'Submit Invoice'. Clicks on 'View Invoice'.</description> + </annotations> + + <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceButton"/> + <waitForPageLoad stepKey="waitForNewInvoicePageLoad"/> + <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="seeSuccessMessage"/> + <click selector="{{AdminOrderDetailsOrderViewSection.invoices}}" stepKey="clickInvoices"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask5"/> + <conditionalClick selector="{{AdminOrderInvoicesTabSection.clearFilters}}" dependentSelector="{{AdminOrderInvoicesTabSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilters"/> + <click selector="{{AdminOrderInvoicesTabSection.viewInvoice}}" stepKey="openInvoicePage"/> + <waitForPageLoad stepKey="waitForInvoicePageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml deleted file mode 100644 index 03639546631d1..0000000000000 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml +++ /dev/null @@ -1,130 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Check customer information is correct in invoice--> - <actionGroup name="verifyBasicInvoiceInformation"> - <annotations> - <description>Validates that the provided Customer, Address and Customer Group details are present and correct on the Admin View Invoice page.</description> - </annotations> - <arguments> - <argument name="customer"/> - <argument name="shippingAddress"/> - <argument name="billingAddress"/> - <argument name="customerGroup" defaultValue="GeneralCustomerGroup"/> - </arguments> - - <see selector="{{AdminInvoiceOrderInformationSection.customerName}}" userInput="{{customer.firstname}}" stepKey="seeCustomerName"/> - <see selector="{{AdminInvoiceOrderInformationSection.customerEmail}}" userInput="{{customer.email}}" stepKey="seeCustomerEmail"/> - <see selector="{{AdminInvoiceOrderInformationSection.customerGroup}}" userInput="{{customerGroup.code}}" stepKey="seeCustomerGroup"/> - <see selector="{{AdminInvoiceAddressInformationSection.billingAddress}}" userInput="{{billingAddress.street[0]}}" stepKey="seeBillingAddressStreet"/> - <see selector="{{AdminInvoiceAddressInformationSection.billingAddress}}" userInput="{{billingAddress.city}}" stepKey="seeBillingAddressCity"/> - <see selector="{{AdminInvoiceAddressInformationSection.billingAddress}}" userInput="{{billingAddress.country_id}}" stepKey="seeBillingAddressCountry"/> - <see selector="{{AdminInvoiceAddressInformationSection.billingAddress}}" userInput="{{billingAddress.postcode}}" stepKey="seeBillingAddressPostcode"/> - <see selector="{{AdminInvoiceAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.street[0]}}" stepKey="seeShippingAddressStreet"/> - <see selector="{{AdminInvoiceAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.city}}" stepKey="seeShippingAddressCity"/> - <see selector="{{AdminInvoiceAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.country_id}}" stepKey="seeShippingAddressCountry"/> - <see selector="{{AdminInvoiceAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.postcode}}" stepKey="seeShippingAddressPostcode"/> - </actionGroup> - - <!--Check that product is in invoice items--> - <actionGroup name="seeProductInInvoiceItems"> - <annotations> - <description>Validates that the provided Product appears under the 'SKU' column in the Admin Invoices edit page.</description> - </annotations> - <arguments> - <argument name="product"/> - </arguments> - - <see selector="{{AdminInvoiceItemsSection.skuColumn}}" userInput="{{product.sku}}" stepKey="seeProductSkuInGrid"/> - </actionGroup> - - <!--Admin Fast Create Invoice--> - <actionGroup name="adminFastCreateInvoice"> - <annotations> - <description>Clicks on 'Invoice' on the Admin Orders view page. Clicks on 'Submit Invoice'. Clicks on 'View Invoice'.</description> - </annotations> - - <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceButton"/> - <waitForPageLoad stepKey="waitForNewInvoicePageLoad"/> - <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="seeSuccessMessage"/> - <click selector="{{AdminOrderDetailsOrderViewSection.invoices}}" stepKey="clickInvoices"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask5"/> - <conditionalClick selector="{{AdminOrderInvoicesTabSection.clearFilters}}" dependentSelector="{{AdminOrderInvoicesTabSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilters"/> - <click selector="{{AdminOrderInvoicesTabSection.viewInvoice}}" stepKey="openInvoicePage"/> - <waitForPageLoad stepKey="waitForInvoicePageLoad"/> - </actionGroup> - - <actionGroup name="clearInvoicesGridFilters"> - <annotations> - <description>Goes to the Admin Invoices grid page. Clicks on 'Clear Filters', if present.</description> - </annotations> - - <amOnPage url="{{AdminInvoicesPage.url}}" stepKey="goToInvoices"/> - <waitForPageLoad stepKey="waitInvoicesGridToLoad"/> - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearFilters"/> - <waitForPageLoad stepKey="waitInvoicesGrid"/> - </actionGroup> - - <actionGroup name="goToInvoiceIntoOrder"> - <annotations> - <description>Clicks on 'Invoice' on the Admin Orders view page. Validates that the URL and Page Title are correct.</description> - </annotations> - - <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceAction"/> - <seeInCurrentUrl url="{{AdminInvoiceNewPage.url}}" stepKey="seeOrderInvoiceUrl"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Invoice" stepKey="seePageNameNewInvoicePage"/> - </actionGroup> - - <actionGroup name="StartCreateInvoiceFromOrderPage"> - <annotations> - <description>Clicks on 'Invoice' on the Admin Orders view page. Validates that the URL and Page Title are correct.</description> - </annotations> - - <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceAction"/> - <seeInCurrentUrl url="{{AdminInvoiceNewPage.url}}" stepKey="seeNewInvoiceUrl"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Invoice" stepKey="seeNewInvoicePageTitle"/> - </actionGroup> - - <actionGroup name="SubmitInvoice"> - <annotations> - <description>Clicks on 'Submit Invoice' on the Admin 'New Invoice' page. Validates that the Success Message is present and correct. Validates that the Order ID appears in the URL.</description> - </annotations> - - <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - <waitForElementVisible selector="{{AdminMessagesSection.successMessage}}" stepKey="waitForMessageAppears"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="seeInvoiceCreateSuccess"/> - <grabFromCurrentUrl regex="~/order_id/(\d+)/~" stepKey="grabOrderId"/> - <seeInCurrentUrl url="{{AdminOrderDetailsPage.url('$grabOrderId')}}" stepKey="seeViewOrderPageInvoice"/> - </actionGroup> - - <!--Filter invoices by order id --> - <actionGroup name="filterInvoiceGridByOrderId"> - <annotations> - <description>Goes to the Admin Invoices grid page. Filters the grid for the provided Order ID.</description> - </annotations> - <arguments> - <argument name="orderId" type="string"/> - </arguments> - - <amOnPage url="{{AdminInvoicesPage.url}}" stepKey="goToInvoices"/> - <click selector="{{AdminInvoicesGridSection.filter}}" stepKey="clickFilter"/> - <fillField selector="{{AdminInvoicesFiltersSection.orderNum}}" userInput="{{orderId}}" stepKey="fillOrderIdForFilter"/> - <click selector="{{AdminInvoicesFiltersSection.applyFilters}}" stepKey="clickApplyFilters"/> - <waitForPageLoad stepKey="waitForFiltersApply"/> - </actionGroup> - - <actionGroup name="FilterInvoiceGridByOrderIdWithCleanFiltersActionGroup" extends="filterInvoiceGridByOrderId"> - <arguments> - <argument name="orderId" type="string"/> - </arguments> - <conditionalClick selector="{{AdminInvoicesGridSection.clearFilters}}" dependentSelector="{{AdminInvoicesGridSection.clearFilters}}" visible="true" stepKey="clearFilters" after="goToInvoices"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenAndFillCreditMemoRefundActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenAndFillCreditMemoRefundActionGroup.xml index 78717e9c2f963..b11985fc8e7ef 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenAndFillCreditMemoRefundActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenAndFillCreditMemoRefundActionGroup.xml @@ -19,7 +19,7 @@ <argument name="adjustmentFee" type="string" defaultValue="0"/> <argument name="rowNumber" type="string" defaultValue="1"/> </arguments> - + <!-- Click 'Credit Memo' button --> <click selector="{{AdminOrderDetailsMainActionsSection.creditMemo}}" stepKey="clickCreateCreditMemo"/> <seeInCurrentUrl url="{{AdminCreditMemoNewPage.url}}" stepKey="seeNewCreditMemoPage"/> @@ -34,15 +34,8 @@ <fillField userInput="{{shippingRefund}}" selector="{{AdminCreditMemoTotalSection.refundShipping}}" stepKey="fillShipping"/> <fillField userInput="{{adjustmentRefund}}" selector="{{AdminCreditMemoTotalSection.adjustmentRefund}}" stepKey="fillAdjustmentRefund"/> <fillField userInput="{{adjustmentFee}}" selector="{{AdminCreditMemoTotalSection.adjustmentFee}}" stepKey="fillAdjustmentFee"/> + <waitForElementVisible selector="{{AdminCreditMemoTotalSection.updateTotals}}" stepKey="waitForUpdateTotalsButton"/> + <click selector="{{AdminCreditMemoTotalSection.updateTotals}}" stepKey="clickUpdateTotals"/> <checkOption selector="{{AdminCreditMemoTotalSection.emailCopy}}" stepKey="checkSendEmailCopy"/> </actionGroup> - - <!-- Open and fill CreditMemo refund with back to stock --> - <actionGroup name="AdminOpenAndFillCreditMemoRefundAndBackToStockActionGroup" extends="AdminOpenAndFillCreditMemoRefundActionGroup"> - <annotations> - <description>EXTENDS: AdminOpenAndFillCreditMemoRefundActionGroup. Checks 'Return to Stock'.</description> - </annotations> - - <checkOption selector="{{AdminCreditMemoItemsSection.itemReturnToStock(rowNumber)}}" stepKey="backToStock" after="scrollToItemsToRefund"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenAndFillCreditMemoRefundAndBackToStockActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenAndFillCreditMemoRefundAndBackToStockActionGroup.xml new file mode 100644 index 0000000000000..0dc205df18249 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenAndFillCreditMemoRefundAndBackToStockActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOpenAndFillCreditMemoRefundAndBackToStockActionGroup" extends="AdminOpenAndFillCreditMemoRefundActionGroup"> + <annotations> + <description>EXTENDS: AdminOpenAndFillCreditMemoRefundActionGroup. Checks 'Return to Stock'.</description> + </annotations> + + <checkOption selector="{{AdminCreditMemoItemsSection.itemReturnToStock(rowNumber)}}" stepKey="backToStock" after="scrollToItemsToRefund"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenOrderByEntityIdActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenOrderByEntityIdActionGroup.xml new file mode 100644 index 0000000000000..c1beb0ee9790a --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOpenOrderByEntityIdActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOpenOrderByEntityIdActionGroup"> + <arguments> + <argument name="entityId" type="string"/> + </arguments> + <amOnPage url="{{AdminOrderPage.url(entityId)}}" stepKey="openOrder"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml deleted file mode 100644 index 90e2aa8e12527..0000000000000 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml +++ /dev/null @@ -1,557 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Navigate to create order page (New Order -> Create New Customer)--> - <actionGroup name="navigateToNewOrderPageNewCustomer"> - <annotations> - <description>Goes to the Admin Orders grid page. Clicks on 'Create New Order'. Clicks on 'Create New Customer'. Select the provided Store View, if present. Validates that Page Title is present and correct.</description> - </annotations> - <arguments> - <argument name="storeView" defaultValue="_defaultStore"/> - </arguments> - - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderIndexPage"/> - <waitForPageLoad stepKey="waitForIndexPageLoad"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Orders" stepKey="seeIndexPageTitle"/> - <click selector="{{AdminOrdersGridSection.createNewOrder}}" stepKey="clickCreateNewOrder"/> - <click selector="{{AdminOrderFormActionSection.CreateNewCustomer}}" stepKey="clickCreateCustomer"/> - <conditionalClick selector="{{AdminOrderStoreScopeTreeSection.storeOption(storeView.name)}}" dependentSelector="{{AdminOrderStoreScopeTreeSection.storeOption(storeView.name)}}" visible="true" stepKey="selectStoreViewIfAppears"/> - <waitForPageLoad stepKey="waitForCreateOrderPageLoadAfterStoreSelect"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Create New Order" stepKey="seeNewOrderPageTitle"/> - </actionGroup> - - <!--Navigate to create order page (New Order -> Create New Customer)--> - <actionGroup name="navigateToNewOrderPageNewCustomerSingleStore"> - <annotations> - <description>Goes to the Admin Orders grid page. Clicks on 'Create New Order'. Clicks on 'Create New Customer'. Validates that Page Title is present and correct.</description> - </annotations> - <arguments> - <argument name="storeView" defaultValue="_defaultStore"/> - </arguments> - - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderIndexPage"/> - <waitForPageLoad stepKey="waitForIndexPageLoad"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Orders" stepKey="seeIndexPageTitle"/> - <click selector="{{AdminOrdersGridSection.createNewOrder}}" stepKey="clickCreateNewOrder"/> - <click selector="{{AdminOrderFormActionSection.CreateNewCustomer}}" stepKey="clickCreateCustomer"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Create New Order" stepKey="seeNewOrderPageTitle"/> - </actionGroup> - - <!--Navigate to create order page (New Order -> Select Customer)--> - <actionGroup name="navigateToNewOrderPageExistingCustomer"> - <annotations> - <description>Goes tot he Admin Orders grid page. Clicks on 'Create New Order'. Filters the grid for the provided Customer. Clicks on the Customer. Selects the provided Store View, if present. Validates that the Page Title is present and correct.</description> - </annotations> - <arguments> - <argument name="customer"/> - <argument name="storeView" defaultValue="_defaultStore"/> - </arguments> - - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderIndexPage"/> - <waitForPageLoad stepKey="waitForIndexPageLoad"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Orders" stepKey="seeIndexPageTitle"/> - <click selector="{{AdminOrdersGridSection.createNewOrder}}" stepKey="clickCreateNewOrder"/> - <waitForPageLoad stepKey="waitForCustomerGridLoad"/> - - <!--Clear grid filters--> - <conditionalClick selector="{{AdminOrderCustomersGridSection.resetButton}}" dependentSelector="{{AdminOrderCustomersGridSection.resetButton}}" visible="true" stepKey="clearExistingCustomerFilters"/> - <fillField userInput="{{customer.email}}" selector="{{AdminOrderCustomersGridSection.emailInput}}" stepKey="filterEmail"/> - <click selector="{{AdminOrderCustomersGridSection.apply}}" stepKey="applyFilter"/> - <waitForPageLoad stepKey="waitForFilteredCustomerGridLoad"/> - <click selector="{{AdminOrderCustomersGridSection.firstRow}}" stepKey="clickOnCustomer"/> - <waitForPageLoad stepKey="waitForCreateOrderPageLoad"/> - - <!-- Select store view if appears --> - <conditionalClick selector="{{AdminOrderStoreScopeTreeSection.storeOption(storeView.name)}}" dependentSelector="{{AdminOrderStoreScopeTreeSection.storeOption(storeView.name)}}" visible="true" stepKey="selectStoreViewIfAppears"/> - <waitForPageLoad stepKey="waitForCreateOrderPageLoadAfterStoreSelect"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Create New Order" stepKey="seeNewOrderPageTitle"/> - </actionGroup> - - <!--Navigate to New Order Page for existing Customer And Store--> - <actionGroup name="NavigateToNewOrderPageExistingCustomerAndStoreActionGroup" extends="navigateToNewOrderPageExistingCustomer"> - <annotations> - <description>EXTENDS: navigateToNewOrderPageExistingCustomer. Clicks on the provided Store View.</description> - </annotations> - <arguments> - <argument name="storeView" defaultValue="_defaultStore"/> - </arguments> - - <click selector="{{AdminOrderStoreScopeTreeSection.storeOption(storeView.name)}}" stepKey="selectStoreView" after="waitForCreateOrderPageLoad"/> - <waitForPageLoad stepKey="waitForLoad" after="selectStoreView"/> - </actionGroup> - - <!--Check the required fields are actually required--> - <actionGroup name="checkRequiredFieldsNewOrderForm"> - <annotations> - <description>Clears the Email, First Name, Last Name, Street Line 1, City, Postal Code and Phone fields when adding an Order and then verifies that they are required after attempting to Save.</description> - </annotations> - - <seeElement selector="{{AdminOrderFormAccountSection.requiredGroup}}" stepKey="seeCustomerGroupRequired"/> - <seeElement selector="{{AdminOrderFormAccountSection.requiredEmail}}" stepKey="seeEmailRequired"/> - <clearField selector="{{AdminOrderFormAccountSection.email}}" stepKey="clearEmailField"/> - <clearField selector="{{AdminOrderFormBillingAddressSection.FirstName}}" stepKey="clearFirstNameField"/> - <clearField selector="{{AdminOrderFormBillingAddressSection.LastName}}" stepKey="clearLastNameField"/> - <clearField selector="{{AdminOrderFormBillingAddressSection.StreetLine1}}" stepKey="clearStreetField"/> - <clearField selector="{{AdminOrderFormBillingAddressSection.City}}" stepKey="clearCityField"/> - <selectOption selector="{{AdminOrderFormBillingAddressSection.Country}}" userInput="United States" stepKey="selectUSCountry"/> - <selectOption selector="{{AdminOrderFormBillingAddressSection.State}}" userInput="Please select" stepKey="selectNoState"/> - <clearField selector="{{AdminOrderFormBillingAddressSection.PostalCode}}" stepKey="clearPostalCodeField"/> - <clearField selector="{{AdminOrderFormBillingAddressSection.Phone}}" stepKey="clearPhoneField"/> - <seeElement selector="{{AdminOrderFormPaymentSection.getShippingMethods}}" stepKey="seeShippingMethodNotSelected"/> - <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="trySubmitOrder"/> - <see selector="{{AdminOrderFormBillingAddressSection.emailError}}" userInput="This is a required field." stepKey="seeThatEmailIsRequired"/> - <see selector="{{AdminOrderFormBillingAddressSection.firstNameError}}" userInput="This is a required field." stepKey="seeFirstNameRequired"/> - <see selector="{{AdminOrderFormBillingAddressSection.lastNameError}}" userInput="This is a required field." stepKey="seeLastNameRequired"/> - <see selector="{{AdminOrderFormBillingAddressSection.streetAddressError}}" userInput="This is a required field." stepKey="seeStreetRequired"/> - <see selector="{{AdminOrderFormBillingAddressSection.cityError}}" userInput="This is a required field." stepKey="seeCityRequired"/> - <see selector="{{AdminOrderFormBillingAddressSection.stateError}}" userInput="This is a required field." stepKey="seeStateRequired"/> - <see selector="{{AdminOrderFormBillingAddressSection.postalCodeError}}" userInput="This is a required field." stepKey="seePostalCodeRequired"/> - <see selector="{{AdminOrderFormBillingAddressSection.phoneError}}" userInput="This is a required field." stepKey="seePhoneRequired"/> - <see selector="{{AdminOrderFormPaymentSection.shippingError}}" userInput="This is a required field." stepKey="seeShippingMethodRequired"/> - </actionGroup> - - <!--Add a simple product to order--> - <actionGroup name="addSimpleProductToOrder"> - <annotations> - <description>Adds the provided Simple Product to an Order. Fills in the provided Product Qty. Clicks on 'Add Selected Product(s) to Order'.</description> - </annotations> - <arguments> - <argument name="product" defaultValue="_defaultProduct" type="entity"/> - <argument name="productQty" defaultValue="1" type="string"/> - </arguments> - - <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickAddProducts"/> - <fillField selector="{{AdminOrderFormItemsSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillSkuFilter"/> - <click selector="{{AdminOrderFormItemsSection.search}}" stepKey="clickSearch"/> - <scrollTo selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" x="0" y="-100" stepKey="scrollToCheckColumn"/> - <checkOption selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" stepKey="selectProduct"/> - <fillField selector="{{AdminOrderFormItemsSection.rowQty('1')}}" userInput="{{productQty}}" stepKey="fillProductQty"/> - <scrollTo selector="{{AdminOrderFormItemsSection.addSelected}}" x="0" y="-100" stepKey="scrollToAddSelectedButton"/> - <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="clickAddSelectedProducts"/> - <wait time="5" stepKey="waitForOptionsToLoad"/> - </actionGroup> - - <actionGroup name="AddSimpleProductWithQtyToOrderActionGroup" extends="addSimpleProductToOrder"> - <arguments> - <argument name="product" defaultValue="_defaultProduct" type="entity"/> - <argument name="productQty" type="string"/> - </arguments> - <fillField selector="{{AdminOrderFormItemsSection.rowQty('1')}}" userInput="{{productQty}}" stepKey="fillProductQty"/> - </actionGroup> - - <!--Add configurable product to order --> - <actionGroup name="addConfigurableProductToOrder"> - <annotations> - <description>Adds the provided Configurable Product with the provided Option to an Order. Fills in the provided Product Qty. Clicks on 'Add Selected Product(s) to Order'.</description> - </annotations> - <arguments> - <argument name="product"/> - <argument name="attribute"/> - <argument name="option"/> - </arguments> - - <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickAddProducts"/> - <fillField selector="{{AdminOrderFormItemsSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillSkuFilterConfigurable"/> - <click selector="{{AdminOrderFormItemsSection.search}}" stepKey="clickSearchConfigurable"/> - <scrollTo selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" x="0" y="-100" stepKey="scrollToCheckColumn"/> - <checkOption selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" stepKey="selectConfigurableProduct"/> - <waitForElementVisible selector="{{AdminOrderFormConfigureProductSection.optionSelect(attribute.default_label)}}" stepKey="waitForConfigurablePopover"/> - <wait time="2" stepKey="waitForOptionsToLoad"/> - <selectOption selector="{{AdminOrderFormConfigureProductSection.optionSelect(attribute.default_label)}}" userInput="{{option.name}}" stepKey="selectionConfigurableOption"/> - <click selector="{{AdminOrderFormConfigureProductSection.ok}}" stepKey="clickOkConfigurablePopover"/> - <scrollTo selector="{{AdminOrderFormItemsSection.addSelected}}" x="0" y="-100" stepKey="scrollToAddSelectedButton"/> - <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="clickAddSelectedProducts"/> - </actionGroup> - - <actionGroup name="newAddConfigurableProductToOrder" extends="addConfigurableProductToOrder"> - <remove keyForRemoval="waitForConfigurablePopover"/> - <remove keyForRemoval="selectionConfigurableOption"/> - <selectOption selector="{{AdminOrderFormConfigureProductSection.selectOption}}" userInput="{{option.value}}" stepKey="selectOption" after="waitForOptionsToLoad"/> - </actionGroup> - - <!--Add configurable product to order --> - <actionGroup name="addConfigurableProductToOrderFromAdmin" extends="addConfigurableProductToOrder"> - <annotations> - <description>EXTENDS: addConfigurableProductToOrder. Selects the provided Option to the Configurable Product.</description> - </annotations> - - <waitForElementVisible selector="{{AdminOrderFormConfigureProductSection.optionSelect(attribute.default_frontend_label)}}" stepKey="waitForConfigurablePopover"/> - <selectOption selector="{{AdminOrderFormConfigureProductSection.optionSelect(attribute.default_frontend_label)}}" userInput="{{option.label}}" stepKey="selectionConfigurableOption"/> - </actionGroup> - - <actionGroup name="configureOrderedConfigurableProduct"> - <annotations> - <description>Clicks on 'Configure' for a Product in the 'Please select products' under the 'Create New Order for' page. Selects the provided Option and Attribute. Fills in the provided Qty. Clicks on Ok.</description> - </annotations> - <arguments> - <argument name="attribute"/> - <argument name="option"/> - <argument name="quantity" type="string"/> - </arguments> - - <click selector="{{AdminOrderFormItemsSection.configure}}" stepKey="clickConfigure"/> - <waitForElementVisible selector="{{AdminOrderFormConfigureProductSection.optionSelect(attribute.default_frontend_label)}}" stepKey="waitForConfigurablePopover"/> - <wait time="2" stepKey="waitForOptionsToLoad"/> - <selectOption selector="{{AdminOrderFormConfigureProductSection.optionSelect(attribute.default_frontend_label)}}" userInput="{{option.label}}" stepKey="selectionConfigurableOption"/> - <fillField selector="{{AdminOrderFormConfigureProductSection.quantity}}" userInput="{{quantity}}" stepKey="fillQuantity"/> - <click selector="{{AdminOrderFormConfigureProductSection.ok}}" stepKey="clickOkConfigurablePopover"/> - </actionGroup> - - <!--Add bundle product to order --> - <actionGroup name="addBundleProductToOrder"> - <annotations> - <description>Adds the provided Bundled Product with the provided Option to an Order. Fills in the provided Product Qty. Clicks on 'Add Selected Product(s) to Order'.</description> - </annotations> - <arguments> - <argument name="product"/> - <argument name="quantity" type="string" defaultValue="1"/> - </arguments> - - <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickAddProducts"/> - <fillField selector="{{AdminOrderFormItemsSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillSkuFilterBundle"/> - <click selector="{{AdminOrderFormItemsSection.search}}" stepKey="clickSearchBundle"/> - <scrollTo selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" x="0" y="-100" stepKey="scrollToCheckColumn"/> - <checkOption selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" stepKey="selectBundleProduct"/> - <waitForElementVisible selector="{{AdminOrderFormBundleProductSection.quantity}}" stepKey="waitForBundleOptionLoad"/> - <wait time="2" stepKey="waitForOptionsToLoad"/> - <fillField selector="{{AdminOrderFormBundleProductSection.quantity}}" userInput="{{quantity}}" stepKey="fillQuantity"/> - <click selector="{{AdminOrderFormConfigureProductSection.ok}}" stepKey="clickOk"/> - <scrollTo selector="{{AdminOrderFormItemsSection.addSelected}}" x="0" y="-100" stepKey="scrollToAddSelectedButton"/> - <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="clickAddSelectedProducts"/> - </actionGroup> - - <!--Add bundle product to order and check product price in the grid--> - <actionGroup name="addBundleProductToOrderAndCheckPriceInGrid" extends="addBundleProductToOrder"> - <annotations> - <description>EXTENDS: addBundleProductToOrder. Validates that the provided Product Price is present and correct in the 'Items Ordered' section.</description> - </annotations> - <arguments> - <argument name="price" type="string"/> - </arguments> - - <grabTextFrom selector="{{AdminOrderFormItemsSection.rowPrice('1')}}" stepKey="grabProductPriceFromGrid" after="clickOk"/> - <assertEquals stepKey="assertProductPriceInGrid" message="Bundle product price in grid should be equal {{price}}" after="grabProductPriceFromGrid"> - <expectedResult type="string">{{price}}</expectedResult> - <actualResult type="variable">grabProductPriceFromGrid</actualResult> - </assertEquals> - </actionGroup> - - <!--Add downloadable product to order --> - <actionGroup name="addDownloadableProductToOrder"> - <annotations> - <description>Adds a Downloadable Product to an Order. Clicks on 'Add Selected Product(s) to Order'.</description> - </annotations> - - <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickAddProducts"/> - <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> - <fillField selector="{{AdminOrderFormItemsSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillSkuFilterDownloadable"/> - <click selector="{{AdminOrderFormItemsSection.search}}" stepKey="clickSearchDownloadable"/> - <scrollTo selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" x="0" y="-100" stepKey="scrollToCheckColumn"/> - <checkOption selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" stepKey="selectDownloadableProduct"/> - <waitForElementVisible selector="{{AdminOrderFormDownloadableProductSection.optionSelect(link.title)}}" stepKey="waitForLinkLoad"/> - <click selector="{{AdminOrderFormDownloadableProductSection.optionSelect(link.title)}}" stepKey="selectLink"/> - <fillField selector="{{AdminOrderFormDownloadableProductSection.quantity}}" userInput="{{quantity}}" stepKey="setQuantity"/> - <click selector="{{AdminOrderFormDownloadableProductSection.ok}}" stepKey="confirmConfiguration"/> - <scrollTo selector="{{AdminOrderFormItemsSection.addSelected}}" x="0" y="-100" stepKey="scrollToAddSelectedButton"/> - <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="clickAddSelectedProducts"/> - </actionGroup> - - <!--Add grouped product option to order --> - <actionGroup name="addGroupedProductOptionToOrder"> - <annotations> - <description>Adds the provided Grouped Product with the provided Option to an Order. Fills in the provided Product Qty. Clicks on 'Add Selected Product(s) to Order'.</description> - </annotations> - <arguments> - <argument name="product"/> - <argument name="option"/> - <argument name="quantity" type="string" defaultValue="1"/> - </arguments> - - <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickAddProducts"/> - <fillField selector="{{AdminOrderFormItemsSection.skuFilter}}" userInput="{{product.sku}}" stepKey="fillSkuFilterGrouped"/> - <click selector="{{AdminOrderFormItemsSection.search}}" stepKey="clickSearchGrouped"/> - <scrollTo selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" x="0" y="-100" stepKey="scrollToCheckColumn"/> - <checkOption selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" stepKey="selectGroupedProduct"/> - <waitForElementVisible selector="{{AdminOrderFormGroupedProductSection.optionQty(option.sku)}}" stepKey="waitForGroupedOptionLoad"/> - <wait time="2" stepKey="waitForOptionsToLoad"/> - <fillField selector="{{AdminOrderFormGroupedProductSection.optionQty(option.sku)}}" userInput="{{quantity}}" stepKey="fillOptionQuantity"/> - <click selector="{{AdminOrderFormConfigureProductSection.ok}}" stepKey="clickOk"/> - <scrollTo selector="{{AdminOrderFormItemsSection.addSelected}}" x="0" y="-100" stepKey="scrollToAddSelectedButton"/> - <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="clickAddSelectedProducts"/> - </actionGroup> - - <!--Fill customer billing address--> - <actionGroup name="fillOrderCustomerInformation"> - <annotations> - <description>Fills in the provided Customer and Address details on the Admin 'Create New Order for' page.</description> - </annotations> - <arguments> - <argument name="customer"/> - <argument name="address"/> - </arguments> - - <fillField selector="{{AdminOrderFormBillingAddressSection.FirstName}}" userInput="{{customer.firstname}}" stepKey="fillFirstName"/> - <fillField selector="{{AdminOrderFormBillingAddressSection.LastName}}" userInput="{{customer.lastname}}" stepKey="fillLastName"/> - <fillField selector="{{AdminOrderFormBillingAddressSection.StreetLine1}}" userInput="{{address.street[0]}}" stepKey="fillStreetLine1"/> - <fillField selector="{{AdminOrderFormBillingAddressSection.City}}" userInput="{{address.city}}" stepKey="fillCity"/> - <selectOption selector="{{AdminOrderFormBillingAddressSection.Country}}" userInput="{{address.country_id}}" stepKey="fillCountry"/> - <selectOption selector="{{AdminOrderFormBillingAddressSection.State}}" userInput="{{address.state}}" stepKey="fillState"/> - <fillField selector="{{AdminOrderFormBillingAddressSection.PostalCode}}" userInput="{{address.postcode}}" stepKey="fillPostalCode"/> - <fillField selector="{{AdminOrderFormBillingAddressSection.Phone}}" userInput="{{address.telephone}}" stepKey="fillPhone"/> - </actionGroup> - - <!--Select flat rate shipping method--> - <actionGroup name="orderSelectFlatRateShipping"> - <annotations> - <description>Selects the 'Flat Rate' Shipping Method on the Admin 'Create New Order for' page.</description> - </annotations> - - <click selector="{{AdminOrderFormPaymentSection.header}}" stepKey="unfocus"/> - <waitForPageLoad stepKey="waitForJavascriptToFinish"/> - <click selector="{{AdminOrderFormPaymentSection.getShippingMethods}}" stepKey="clickShippingMethods"/> - <waitForElementVisible selector="{{AdminOrderFormPaymentSection.flatRateOption}}" stepKey="waitForShippingOptions"/> - <selectOption selector="{{AdminOrderFormPaymentSection.flatRateOption}}" userInput="flatrate_flatrate" stepKey="checkFlatRate"/> - </actionGroup> - - <actionGroup name="changeShippingMethod"> - <annotations> - <description>Change Shipping Method on the Admin 'Create New Order for' page.</description> - </annotations> - <arguments> - <argument name="shippingMethod" defaultValue="flatrate_flatrate" type="string"/> - </arguments> - <click selector="{{AdminOrderFormPaymentSection.header}}" stepKey="unfocus"/> - <waitForPageLoad stepKey="waitForJavascriptToFinish"/> - <click selector="{{AdminOrderFormPaymentSection.getShippingMethods}}" stepKey="clickShippingMethods1"/> - <waitForElementVisible selector="{{AdminOrderFormPaymentSection.getShippingMethods}}" stepKey="waitForChangeShippingMethod"/> - <click selector="{{AdminOrderFormPaymentSection.getShippingMethods}}" stepKey="clickShippingMethods2"/> - <waitForElementVisible selector="{{AdminOrderFormPaymentSection.shippingMethod}}" stepKey="waitForShippingOptions2"/> - <selectOption selector="{{AdminOrderFormPaymentSection.shippingMethod}}" userInput="{{shippingMethod}}" stepKey="checkFlatRate"/> - </actionGroup> - - <!--Select free shipping method--> - <actionGroup name="orderSelectFreeShipping"> - <annotations> - <description>Selects the 'Free Shipping' Shipping Method on the Admin 'Create New Order for' page.</description> - </annotations> - - <click selector="{{AdminOrderFormPaymentSection.header}}" stepKey="unfocus"/> - <waitForPageLoad stepKey="waitForJavascriptToFinish"/> - <click selector="{{AdminOrderFormPaymentSection.getShippingMethods}}" stepKey="clickShippingMethods"/> - <waitForElementVisible selector="{{AdminOrderFormPaymentSection.freeShippingOption}}" stepKey="waitForShippingOptions"/> - <selectOption selector="{{AdminOrderFormPaymentSection.freeShippingOption}}" userInput="freeshipping_freeshipping" stepKey="checkFreeShipping"/> - </actionGroup> - - <!--Check that customer information is correct in order--> - <actionGroup name="verifyBasicOrderInformation"> - <annotations> - <description>Validates that the provided Customer, Shipping/Billing Address and Customer Group are present and correct on the Admin Orders view page.</description> - </annotations> - <arguments> - <argument name="customer"/> - <argument name="shippingAddress"/> - <argument name="billingAddress"/> - <argument name="customerGroup" defaultValue="GeneralCustomerGroup"/> - </arguments> - - <see selector="{{AdminOrderDetailsInformationSection.customerName}}" userInput="{{customer.firstname}}" stepKey="seeCustomerName"/> - <see selector="{{AdminOrderDetailsInformationSection.customerEmail}}" userInput="{{customer.email}}" stepKey="seeCustomerEmail"/> - <see selector="{{AdminOrderDetailsInformationSection.customerGroup}}" userInput="{{customerGroup.code}}" stepKey="seeCustomerGroup"/> - <see selector="{{AdminOrderAddressInformationSection.billingAddress}}" userInput="{{billingAddress.street[0]}}" stepKey="seeBillingAddressStreet"/> - <see selector="{{AdminOrderAddressInformationSection.billingAddress}}" userInput="{{billingAddress.city}}" stepKey="seeBillingAddressCity"/> - <see selector="{{AdminOrderAddressInformationSection.billingAddress}}" userInput="{{billingAddress.country_id}}" stepKey="seeBillingAddressCountry"/> - <see selector="{{AdminOrderAddressInformationSection.billingAddress}}" userInput="{{billingAddress.postcode}}" stepKey="seeBillingAddressPostcode"/> - <see selector="{{AdminOrderAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.street[0]}}" stepKey="seeShippingAddressStreet"/> - <see selector="{{AdminOrderAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.city}}" stepKey="seeShippingAddressCity"/> - <see selector="{{AdminOrderAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.country_id}}" stepKey="seeShippingAddressCountry"/> - <see selector="{{AdminOrderAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.postcode}}" stepKey="seeShippingAddressPostcode"/> - </actionGroup> - <actionGroup name="AssertOrderAddressInformationActionGroup" extends="verifyBasicOrderInformation"> - <remove keyForRemoval="seeCustomerName"/> - <remove keyForRemoval="seeCustomerEmail"/> - <remove keyForRemoval="seeCustomerGroup"/> - <remove keyForRemoval="seeBillingAddressCountry"/> - <remove keyForRemoval="seeShippingAddressCountry"/> - <see selector="{{AdminOrderAddressInformationSection.billingAddress}}" userInput="{{billingAddress.country}}" stepKey="seeBillingCountry" after="seeBillingAddressCity"/> - <see selector="{{AdminOrderAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.country}}" stepKey="seeAddressCountry" after="seeShippingAddressCity"/> - </actionGroup> - - <!--Verify order information--> - <actionGroup name="verifyCreatedOrderInformation"> - <annotations> - <description>Validates that the Success Message, Order Status (Pending) and Order ID are present and correct.</description> - </annotations> - - <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeSuccessMessage"/> - <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeOrderPendingStatus" after="seeSuccessMessage"/> - <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId" after="seeOrderPendingStatus"/> - <assertNotEmpty actual="$getOrderId" stepKey="assertOrderIdIsNotEmpty" after="getOrderId"/> - </actionGroup> - - <!--Check for product in order items list--> - <actionGroup name="seeProductInItemsOrdered"> - <annotations> - <description>Validates that the provided Product is present and correct in the 'Items Ordered' section on the Admin Orders view page.</description> - </annotations> - <arguments> - <argument name="product"/> - </arguments> - - <see selector="{{AdminOrderItemsOrderedSection.productSkuColumn}}" userInput="{{product.sku}}" stepKey="seeSkuInItemsOrdered"/> - </actionGroup> - - <actionGroup name="CreateOrderInStoreActionGroup"> - <annotations> - <description>Goes to the Admin Create Order page. Creates an Order based on the provided Customer, Store View and Product. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <argument name="product"/> - <argument name="customer"/> - <argument name="storeView"/> - </arguments> - - <amOnPage stepKey="navigateToNewOrderPage" url="{{AdminOrderCreatePage.url}}"/> - <click stepKey="chooseCustomer" selector="{{AdminOrdersGridSection.customerInOrdersSection(customer.firstname)}}"/> - <waitForPageLoad stepKey="waitForStoresPageOpened"/> - <click stepKey="chooseStore" selector="{{AdminOrderStoreScopeTreeSection.storeForOrder(storeView.name)}}"/> - <scrollToTopOfPage stepKey="scrollToTop"/> - <waitForPageLoad stepKey="waitForStoreToAppear"/> - <click selector="{{OrdersGridSection.addProducts}}" stepKey="clickOnAddProducts"/> - <waitForPageLoad stepKey="waitForProductsListForOrder"/> - <click selector="{{AdminOrdersGridSection.productForOrder(product.sku)}}" stepKey="chooseTheProduct"/> - <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="addSelectedProductToOrder"/> - <waitForPageLoad stepKey="waitForProductAddedInOrder"/> - <click selector="{{AdminInvoicePaymentShippingSection.getShippingMethodAndRates}}" stepKey="openShippingMethod"/> - <waitForPageLoad stepKey="waitForShippingMethods"/> - <click selector="{{AdminInvoicePaymentShippingSection.shippingMethod}}" stepKey="chooseShippingMethod"/> - <waitForPageLoad stepKey="waitForShippingMethodsThickened"/> - <click selector="{{OrdersGridSection.submitOrder}}" stepKey="submitOrder"/> - <see stepKey="seeSuccessMessageForOrder" userInput="You created the order."/> - </actionGroup> - - <actionGroup name="CreateOrderInStoreChoosingPaymentMethodActionGroup"> - <annotations> - <description>Goes to the Admin Create Order page. Creates an Order based on the provided Customer, Store View and Product. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <argument name="product"/> - <argument name="customer"/> - <argument name="storeView"/> - </arguments> - - <amOnPage stepKey="navigateToNewOrderPage" url="{{AdminOrderCreatePage.url}}"/> - <click stepKey="chooseCustomer" selector="{{AdminOrdersGridSection.customerInOrdersSection(customer.firstname)}}"/> - <waitForPageLoad stepKey="waitForStoresPageOpened"/> - <click stepKey="chooseStore" selector="{{AdminOrderStoreScopeTreeSection.storeForOrder(storeView.name)}}"/> - <scrollToTopOfPage stepKey="scrollToTop"/> - <click selector="{{OrdersGridSection.addProducts}}" stepKey="clickOnAddProducts"/> - <waitForPageLoad stepKey="waitForProductsListForOrder"/> - <click selector="{{AdminOrdersGridSection.productForOrder(product.sku)}}" stepKey="chooseTheProduct"/> - <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="addSelectedProductToOrder"/> - <waitForPageLoad stepKey="waitForProductAddedInOrder"/> - <click selector="{{AdminInvoicePaymentShippingSection.getShippingMethodAndRates}}" stepKey="openShippingMethod"/> - <waitForPageLoad stepKey="waitForShippingMethods"/> - <click selector="{{AdminInvoicePaymentShippingSection.shippingMethod}}" stepKey="chooseShippingMethod"/> - <waitForPageLoad stepKey="waitForShippingMethodsThickened"/> - <waitForElementVisible selector="{{AdminOrderFormPaymentSection.paymentBlock}}" stepKey="waitForPaymentOptions"/> - <conditionalClick selector="{{AdminOrderFormPaymentSection.checkMoneyOption}}" dependentSelector="{{AdminOrderFormPaymentSection.checkMoneyOption}}" visible="true" stepKey="checkCheckMoneyOption"/> - <click selector="{{OrdersGridSection.submitOrder}}" stepKey="submitOrder"/> - <see stepKey="seeSuccessMessageForOrder" userInput="You created the order."/> - </actionGroup> - - <!--Cancel order that is in pending status--> - <actionGroup name="cancelPendingOrder"> - <annotations> - <description>Cancels the Pending Order on the Admin Orders view page. Validates that the provided Order Status is present and correct.</description> - </annotations> - <arguments> - <argument name="orderStatus" type="string" defaultValue="Canceled"/> - </arguments> - - <click selector="{{AdminOrderDetailsMainActionsSection.cancel}}" stepKey="clickCancelOrder"/> - <waitForElement selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForCancelConfirmation"/> - <see selector="{{AdminConfirmationModalSection.message}}" userInput="Are you sure you want to cancel this order?" stepKey="seeConfirmationMessage"/> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmOrderCancel"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You canceled the order." stepKey="seeCancelSuccessMessage"/> - <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{orderStatus}}" stepKey="seeOrderStatusCanceled"/> - </actionGroup> - - <!--Assert that the product is not in the order items list--> - <actionGroup name="dontSeeProductInItemsOrdered"> - <arguments> - <argument name="product"/> - </arguments> - <dontSee selector="{{AdminOrderItemsOrderedSection.productSkuColumn}}" userInput="{{product.sku}}" stepKey="dontseeSkuInItemsOrdered"/> - </actionGroup> - - <!--Select Check Money payment method--> - <actionGroup name="SelectCheckMoneyPaymentMethod"> - <annotations> - <description>Selects the 'Check / Money Order' Payment Method on the Admin Create New Order page.</description> - </annotations> - - <waitForElementVisible selector="{{AdminOrderFormPaymentSection.paymentBlock}}" stepKey="waitForPaymentOptions"/> - <conditionalClick selector="{{AdminOrderFormPaymentSection.checkMoneyOption}}" dependentSelector="{{AdminOrderFormPaymentSection.checkMoneyOption}}" visible="true" stepKey="checkCheckMoneyOption"/> - </actionGroup> - - <!--Select Bank Transfer payment method--> - <actionGroup name="SelectBankTransferPaymentMethodActionGroup" extends="SelectCheckMoneyPaymentMethod"> - <remove keyForRemoval="checkCheckMoneyOption"/> - <conditionalClick selector="{{AdminOrderFormPaymentSection.checkBankTransfer}}" dependentSelector="{{AdminOrderFormPaymentSection.checkBankTransfer}}" visible="true" stepKey="checkBankTransferOption" after="waitForPaymentOptions"/> - </actionGroup> - - <!--Select Cash on Delivery payment method--> - <actionGroup name="SelectCashOnDeliveryPaymentMethodActionGroup" extends="SelectCheckMoneyPaymentMethod"> - <remove keyForRemoval="checkCheckMoneyOption"/> - <conditionalClick selector="{{AdminOrderFormPaymentSection.checkCashOnDelivery}}" dependentSelector="{{AdminOrderFormPaymentSection.checkCashOnDelivery}}" visible="true" stepKey="checkCashOnDeliveryOption" after="waitForPaymentOptions"/> - </actionGroup> - - <!--Select Purchase Order payment method--> - <actionGroup name="SelectPurchaseOrderPaymentMethodActionGroup" extends="SelectCheckMoneyPaymentMethod"> - <arguments> - <argument name="purchaseOrderNumber" type="string"/> - </arguments> - <remove keyForRemoval="checkCheckMoneyOption"/> - <conditionalClick selector="{{AdminOrderFormPaymentSection.checkPurchaseOrder}}" dependentSelector="{{AdminOrderFormPaymentSection.checkPurchaseOrder}}" visible="true" stepKey="checkPurchaseOrderOption" after="waitForPaymentOptions"/> - <fillField selector="{{AdminOrderFormPaymentSection.fieldPurchaseOrderNumber}}" userInput="{{purchaseOrderNumber}}" stepKey="fillPurchaseOrderNumber"/> - </actionGroup> - - <!-- Create Order --> - <actionGroup name="CreateOrderActionGroup"> - <annotations> - <description>Goes to the Admin Create New Order page. Selects the provided Customer. Adds the provided Product to the Order. Clicks on Submit Order. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <argument name="product"/> - <argument name="customer"/> - </arguments> - - <amOnPage stepKey="navigateToNewOrderPage" url="{{AdminOrderCreatePage.url}}"/> - <waitForPageLoad stepKey="waitForNewOrderPageOpened"/> - <click stepKey="chooseCustomer" selector="{{AdminOrdersGridSection.customerInOrdersSection(customer.firstname)}}"/> - <waitForPageLoad stepKey="waitForStoresPageOpened"/> - <click selector="{{OrdersGridSection.addProducts}}" stepKey="clickOnAddProducts"/> - <waitForPageLoad stepKey="waitForProductsListForOrder"/> - <click selector="{{AdminOrdersGridSection.productForOrder(product.sku)}}" stepKey="chooseTheProduct"/> - <waitForPageLoad stepKey="waitForClickProduct"/> - <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="addSelectedProductToOrder"/> - <waitForPageLoad stepKey="waitForProductAddedInOrder"/> - <click selector="{{AdminInvoicePaymentShippingSection.getShippingMethodAndRates}}" stepKey="openShippingMethod"/> - <waitForPageLoad stepKey="waitForShippingMethods"/> - <click selector="{{AdminInvoicePaymentShippingSection.shippingMethod}}" stepKey="chooseShippingMethod"/> - <waitForPageLoad stepKey="waitForShippingMethodsThickened"/> - <click selector="{{OrdersGridSection.submitOrder}}" stepKey="submitOrder"/> - <waitForPageLoad stepKey="waitForSubmitOrder"/> - <see stepKey="seeSuccessMessageForOrder" userInput="You created the order."/> - </actionGroup> - <actionGroup name="CreateOrderFilteringCustomerByEmailActionGroup" extends="CreateOrderActionGroup"> - <click stepKey="chooseCustomer" selector="{{AdminOrdersGridSection.customerInOrdersSection(customer.email)}}"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml deleted file mode 100644 index e8ed82148b007..0000000000000 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderGridActionGroup.xml +++ /dev/null @@ -1,113 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Filter order grid by order id field--> - <actionGroup name="filterOrderGridById"> - <annotations> - <description>Goes to the Admin Orders page. Filters the grid based on the provided Order ID.</description> - </annotations> - <arguments> - <argument name="orderId" type="string"/> - </arguments> - - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderGridPage"/> - <waitForPageLoad stepKey="waitForOrdersPage"/> - <conditionalClick selector="{{AdminOrdersGridSection.clearFilters}}" dependentSelector="{{AdminOrdersGridSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilters"/> - <waitForPageLoad stepKey="waitForClearFilters"/> - <click selector="{{AdminOrdersGridSection.filters}}" stepKey="openOrderGridFilters"/> - <waitForPageLoad stepKey="waitForClickFilters"/> - <fillField selector="{{AdminOrdersGridSection.idFilter}}" userInput="{{orderId}}" stepKey="fillOrderIdFilter"/> - <click selector="{{AdminOrdersGridSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> - <waitForPageLoad stepKey="waitForApplyFilters"/> - </actionGroup> - - <!--Filter order grid by the billing name field--> - <actionGroup name="filterOrderGridByBillingName"> - <annotations> - <description>Goes to the Admin Orders page. Filters the grid based on the provided Customer.</description> - </annotations> - <arguments> - <argument name="customer"/> - </arguments> - - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderGridPage"/> - <waitForPageLoad stepKey="waitForOrderGridLoad"/> - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilters"/> - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openOrderGridFilters"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('billing_name')}}" userInput="{{customer.fullname}}" stepKey="fillBillToNameFilter"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> - </actionGroup> - - <!--Filter order grid by order total range--> - <actionGroup name="filterOrderGridByBaseTotalRange"> - <annotations> - <description>Goes to the Admin Orders page. Filters the grid based on the provided Grand Total From/To values.</description> - </annotations> - <arguments> - <argument name="from"/> - <argument name="to"/> - </arguments> - - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilters"/> - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openOrderGridFilters"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('base_grand_total[from]')}}" userInput="{{from}}" stepKey="fillOrderTotalFrom"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('base_grand_total[to]')}}" userInput="{{to}}" stepKey="fillOrderTotalTo"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFilters"/> - </actionGroup> - - <actionGroup name="filterOrderGridByPurchaseDate"> - <annotations> - <description>Goes to the Admin Orders page. Filters the grid based on the provided Purchased Date From/To values.</description> - </annotations> - <arguments> - <argument name="from"/> - <argument name="to"/> - </arguments> - - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilters"/> - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openOrderGridFilters"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('created_at[from]')}}" userInput="{{from}}" stepKey="fillOrderPurchaseDateFrom"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('created_at[to]')}}" userInput="{{to}}" stepKey="fillOrderPurchaseDateTo"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFilters"/> - </actionGroup> - - <actionGroup name="filterOrderGridByStatus"> - <annotations> - <description>Filters the Admin Orders grid based on the provided Order Status.</description> - </annotations> - <arguments> - <argument name="status"/> - </arguments> - - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilters"/> - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openOrderGridFilters"/> - <selectOption selector="{{AdminDataGridHeaderSection.filterFieldSelect('status')}}" userInput="{{status}}" stepKey="fillOrderStatusFilter"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFilters"/> - </actionGroup> - - <actionGroup name="AdminOrdersGridClearFiltersActionGroup"> - <annotations> - <description>Goes to the Admin Orders grid page. Clicks on 'Clear Filters', if present.</description> - </annotations> - - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToGridOrdersPage"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> - <conditionalClick selector="{{AdminOrdersGridSection.clearFilters}}" dependentSelector="{{AdminOrdersGridSection.enabledFilters}}" visible="true" stepKey="clickOnButtonToRemoveFiltersIfPresent"/> - </actionGroup> - - <actionGroup name="OpenOrderById" extends="filterOrderGridById"> - <annotations> - <description>EXTENDS: filterOrderGridById. Clicks on the 1st row of the Admin Orders grid.</description> - </annotations> - - <click selector="{{AdminDataGridTableSection.firstRow}}" after="clickOrderApplyFilters" stepKey="openOrderViewPage"/> - <waitForPageLoad after="openOrderViewPage" stepKey="waitForOrderViewPageOpened"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrdersGridClearFiltersActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrdersGridClearFiltersActionGroup.xml new file mode 100644 index 0000000000000..b301864212c8b --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrdersGridClearFiltersActionGroup.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="AdminOrdersGridClearFiltersActionGroup"> + <annotations> + <description>Goes to the Admin Orders grid page. Clicks on 'Clear Filters', if present.</description> + </annotations> + + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToGridOrdersPage"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <conditionalClick selector="{{AdminOrdersGridSection.clearFilters}}" dependentSelector="{{AdminOrdersGridSection.enabledFilters}}" visible="true" stepKey="clickOnButtonToRemoveFiltersIfPresent"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminShippingDescriptionInOrderViewActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminShippingDescriptionInOrderViewActionGroup.xml new file mode 100644 index 0000000000000..1e4c0a958b2e4 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminShippingDescriptionInOrderViewActionGroup.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="AssertAdminShippingDescriptionInOrderViewActionGroup"> + <annotations> + <description>Validates that the Shipping Description will shown in Shipping total description.</description> + </annotations> + + <arguments> + <argument name="description" type="string"/> + </arguments> + <waitForElementVisible selector="{{AdminOrderTotalSection.shippingDescription}}" time="30" stepKey="waitForElement"/> + <see selector="{{AdminOrderTotalSection.shippingDescription}}" userInput="{{description}}" stepKey="seeOrderTotalShippingDescription"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderAddressInformationActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderAddressInformationActionGroup.xml new file mode 100644 index 0000000000000..db3343794de01 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertOrderAddressInformationActionGroup.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="AssertOrderAddressInformationActionGroup" extends="VerifyBasicOrderInformationActionGroup"> + <remove keyForRemoval="seeCustomerName"/> + <remove keyForRemoval="seeCustomerEmail"/> + <remove keyForRemoval="seeCustomerGroup"/> + <remove keyForRemoval="seeBillingAddressCountry"/> + <remove keyForRemoval="seeShippingAddressCountry"/> + <see selector="{{AdminOrderAddressInformationSection.billingAddress}}" userInput="{{billingAddress.country}}" stepKey="seeBillingCountry" after="seeBillingAddressCity"/> + <see selector="{{AdminOrderAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.country}}" stepKey="seeAddressCountry" after="seeShippingAddressCity"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertStorefrontShippingDescriptionInOrderViewActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertStorefrontShippingDescriptionInOrderViewActionGroup.xml new file mode 100644 index 0000000000000..27e91883eb15c --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertStorefrontShippingDescriptionInOrderViewActionGroup.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="AssertStorefrontShippingDescriptionInOrderViewActionGroup"> + <annotations> + <description>Validates that the Shipping Description will shown in Shipping total description.</description> + </annotations> + + <arguments> + <argument name="description" type="string"/> + </arguments> + <waitForElementVisible selector="{{StorefrontOrderDetailsSection.shippingTotalDescription}}" time="30" stepKey="waitForElement"/> + <see selector="{{StorefrontOrderDetailsSection.shippingTotalDescription}}" userInput="{{description}}" stepKey="seeShippingTotalDescription"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/CancelPendingOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/CancelPendingOrderActionGroup.xml new file mode 100644 index 0000000000000..b440817dfd80e --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/CancelPendingOrderActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CancelPendingOrderActionGroup"> + <annotations> + <description>Cancels the Pending Order on the Admin Orders view page. Validates that the provided Order Status is present and correct.</description> + </annotations> + <arguments> + <argument name="orderStatus" type="string" defaultValue="Canceled"/> + </arguments> + + <click selector="{{AdminOrderDetailsMainActionsSection.cancel}}" stepKey="clickCancelOrder"/> + <waitForElement selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForCancelConfirmation"/> + <see selector="{{AdminConfirmationModalSection.message}}" userInput="Are you sure you want to cancel this order?" stepKey="seeConfirmationMessage"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmOrderCancel"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You canceled the order." stepKey="seeCancelSuccessMessage"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{orderStatus}}" stepKey="seeOrderStatusCanceled"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/ChangeShippingMethodActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/ChangeShippingMethodActionGroup.xml new file mode 100644 index 0000000000000..49ad1013f7b73 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/ChangeShippingMethodActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ChangeShippingMethodActionGroup"> + <annotations> + <description>Change Shipping Method on the Admin 'Create New Order for' page.</description> + </annotations> + <arguments> + <argument name="shippingMethod" defaultValue="flatrate_flatrate" type="string"/> + </arguments> + <click selector="{{AdminOrderFormPaymentSection.header}}" stepKey="unfocus"/> + <waitForPageLoad stepKey="waitForJavascriptToFinish"/> + <click selector="{{AdminOrderFormPaymentSection.getShippingMethods}}" stepKey="clickShippingMethods1"/> + <waitForElementVisible selector="{{AdminOrderFormPaymentSection.getShippingMethods}}" stepKey="waitForChangeShippingMethod"/> + <click selector="{{AdminOrderFormPaymentSection.getShippingMethods}}" stepKey="clickShippingMethods2"/> + <waitForElementVisible selector="{{AdminOrderFormPaymentSection.shippingMethod}}" stepKey="waitForShippingOptions2"/> + <selectOption selector="{{AdminOrderFormPaymentSection.shippingMethod}}" userInput="{{shippingMethod}}" stepKey="checkFlatRate"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/CheckRequiredFieldsNewOrderFormActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/CheckRequiredFieldsNewOrderFormActionGroup.xml new file mode 100644 index 0000000000000..25936ad3f6002 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/CheckRequiredFieldsNewOrderFormActionGroup.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CheckRequiredFieldsNewOrderFormActionGroup"> + <annotations> + <description>Clears the Email, First Name, Last Name, Street Line 1, City, Postal Code and Phone fields when adding an Order and then verifies that they are required after attempting to Save.</description> + </annotations> + + <seeElement selector="{{AdminOrderFormAccountSection.requiredGroup}}" stepKey="seeCustomerGroupRequired"/> + <seeElement selector="{{AdminOrderFormAccountSection.requiredEmail}}" stepKey="seeEmailRequired"/> + <clearField selector="{{AdminOrderFormAccountSection.email}}" stepKey="clearEmailField"/> + <clearField selector="{{AdminOrderFormBillingAddressSection.FirstName}}" stepKey="clearFirstNameField"/> + <clearField selector="{{AdminOrderFormBillingAddressSection.LastName}}" stepKey="clearLastNameField"/> + <clearField selector="{{AdminOrderFormBillingAddressSection.StreetLine1}}" stepKey="clearStreetField"/> + <clearField selector="{{AdminOrderFormBillingAddressSection.City}}" stepKey="clearCityField"/> + <selectOption selector="{{AdminOrderFormBillingAddressSection.Country}}" userInput="United States" stepKey="selectUSCountry"/> + <selectOption selector="{{AdminOrderFormBillingAddressSection.State}}" userInput="Please select" stepKey="selectNoState"/> + <clearField selector="{{AdminOrderFormBillingAddressSection.PostalCode}}" stepKey="clearPostalCodeField"/> + <clearField selector="{{AdminOrderFormBillingAddressSection.Phone}}" stepKey="clearPhoneField"/> + <seeElement selector="{{AdminOrderFormPaymentSection.getShippingMethods}}" stepKey="seeShippingMethodNotSelected"/> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="trySubmitOrder"/> + <see selector="{{AdminOrderFormBillingAddressSection.emailError}}" userInput="This is a required field." stepKey="seeThatEmailIsRequired"/> + <see selector="{{AdminOrderFormBillingAddressSection.firstNameError}}" userInput="This is a required field." stepKey="seeFirstNameRequired"/> + <see selector="{{AdminOrderFormBillingAddressSection.lastNameError}}" userInput="This is a required field." stepKey="seeLastNameRequired"/> + <see selector="{{AdminOrderFormBillingAddressSection.streetAddressError}}" userInput="This is a required field." stepKey="seeStreetRequired"/> + <see selector="{{AdminOrderFormBillingAddressSection.cityError}}" userInput="This is a required field." stepKey="seeCityRequired"/> + <see selector="{{AdminOrderFormBillingAddressSection.stateError}}" userInput="This is a required field." stepKey="seeStateRequired"/> + <see selector="{{AdminOrderFormBillingAddressSection.postalCodeError}}" userInput="This is a required field." stepKey="seePostalCodeRequired"/> + <see selector="{{AdminOrderFormBillingAddressSection.phoneError}}" userInput="This is a required field." stepKey="seePhoneRequired"/> + <see selector="{{AdminOrderFormPaymentSection.shippingError}}" userInput="This is a required field." stepKey="seeShippingMethodRequired"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/ClearInvoicesGridFiltersActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/ClearInvoicesGridFiltersActionGroup.xml new file mode 100644 index 0000000000000..8b8f400fca928 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/ClearInvoicesGridFiltersActionGroup.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="ClearInvoicesGridFiltersActionGroup"> + <annotations> + <description>Goes to the Admin Invoices grid page. Clicks on 'Clear Filters', if present.</description> + </annotations> + + <amOnPage url="{{AdminInvoicesPage.url}}" stepKey="goToInvoices"/> + <waitForPageLoad stepKey="waitInvoicesGridToLoad"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearFilters"/> + <waitForPageLoad stepKey="waitInvoicesGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/ConfigureOrderedConfigurableProductActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/ConfigureOrderedConfigurableProductActionGroup.xml new file mode 100644 index 0000000000000..1a0f1cb9689f1 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/ConfigureOrderedConfigurableProductActionGroup.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="ConfigureOrderedConfigurableProductActionGroup"> + <annotations> + <description>Clicks on 'Configure' for a Product in the 'Please select products' under the 'Create New Order for' page. Selects the provided Option and Attribute. Fills in the provided Qty. Clicks on Ok.</description> + </annotations> + <arguments> + <argument name="attribute"/> + <argument name="option"/> + <argument name="quantity" type="string"/> + </arguments> + + <click selector="{{AdminOrderFormItemsSection.configure}}" stepKey="clickConfigure"/> + <waitForElementVisible selector="{{AdminOrderFormConfigureProductSection.optionSelect(attribute.default_frontend_label)}}" stepKey="waitForConfigurablePopover"/> + <wait time="2" stepKey="waitForOptionsToLoad"/> + <selectOption selector="{{AdminOrderFormConfigureProductSection.optionSelect(attribute.default_frontend_label)}}" userInput="{{option.label}}" stepKey="selectionConfigurableOption"/> + <fillField selector="{{AdminOrderFormConfigureProductSection.quantity}}" userInput="{{quantity}}" stepKey="fillQuantity"/> + <click selector="{{AdminOrderFormConfigureProductSection.ok}}" stepKey="clickOkConfigurablePopover"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderActionGroup.xml new file mode 100644 index 0000000000000..576735a9a36c5 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderActionGroup.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateOrderActionGroup"> + <annotations> + <description>Goes to the Admin Create New Order page. Selects the provided Customer. Adds the provided Product to the Order. Clicks on Submit Order. Validates that the Success Message is present and correct.</description> + </annotations> + <arguments> + <argument name="product"/> + <argument name="customer"/> + </arguments> + + <amOnPage stepKey="navigateToNewOrderPage" url="{{AdminOrderCreatePage.url}}"/> + <waitForPageLoad stepKey="waitForNewOrderPageOpened"/> + <click stepKey="chooseCustomer" selector="{{AdminOrdersGridSection.customerInOrdersSection(customer.firstname)}}"/> + <waitForPageLoad stepKey="waitForStoresPageOpened"/> + <click selector="{{OrdersGridSection.addProducts}}" stepKey="clickOnAddProducts"/> + <waitForPageLoad stepKey="waitForProductsListForOrder"/> + <click selector="{{AdminOrdersGridSection.productForOrder(product.sku)}}" stepKey="chooseTheProduct"/> + <waitForPageLoad stepKey="waitForClickProduct"/> + <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="addSelectedProductToOrder"/> + <waitForPageLoad stepKey="waitForProductAddedInOrder"/> + <click selector="{{AdminInvoicePaymentShippingSection.getShippingMethodAndRates}}" stepKey="openShippingMethod"/> + <waitForPageLoad stepKey="waitForShippingMethods"/> + <click selector="{{AdminInvoicePaymentShippingSection.shippingMethod}}" stepKey="chooseShippingMethod"/> + <waitForPageLoad stepKey="waitForShippingMethodsThickened"/> + <click selector="{{OrdersGridSection.submitOrder}}" stepKey="submitOrder"/> + <waitForPageLoad stepKey="waitForSubmitOrder"/> + <see stepKey="seeSuccessMessageForOrder" userInput="You created the order."/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderFilteringCustomerByEmailActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderFilteringCustomerByEmailActionGroup.xml new file mode 100644 index 0000000000000..fb65531b14bfb --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderFilteringCustomerByEmailActionGroup.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="CreateOrderFilteringCustomerByEmailActionGroup" extends="CreateOrderActionGroup"> + <click stepKey="chooseCustomer" selector="{{AdminOrdersGridSection.customerInOrdersSection(customer.email)}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderInStoreActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderInStoreActionGroup.xml new file mode 100644 index 0000000000000..2349be636cfa7 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderInStoreActionGroup.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateOrderInStoreActionGroup"> + <annotations> + <description>Goes to the Admin Create Order page. Creates an Order based on the provided Customer, Store View and Product. Validates that the Success Message is present and correct.</description> + </annotations> + <arguments> + <argument name="product"/> + <argument name="customer"/> + <argument name="storeView"/> + </arguments> + + <amOnPage stepKey="navigateToNewOrderPage" url="{{AdminOrderCreatePage.url}}"/> + <click stepKey="chooseCustomer" selector="{{AdminOrdersGridSection.customerInOrdersSection(customer.firstname)}}"/> + <waitForPageLoad stepKey="waitForStoresPageOpened"/> + <click stepKey="chooseStore" selector="{{AdminOrderStoreScopeTreeSection.storeForOrder(storeView.name)}}"/> + <scrollToTopOfPage stepKey="scrollToTop"/> + <waitForPageLoad stepKey="waitForStoreToAppear"/> + <click selector="{{OrdersGridSection.addProducts}}" stepKey="clickOnAddProducts"/> + <waitForPageLoad stepKey="waitForProductsListForOrder"/> + <click selector="{{AdminOrdersGridSection.productForOrder(product.sku)}}" stepKey="chooseTheProduct"/> + <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="addSelectedProductToOrder"/> + <waitForPageLoad stepKey="waitForProductAddedInOrder"/> + <click selector="{{AdminInvoicePaymentShippingSection.getShippingMethodAndRates}}" stepKey="openShippingMethod"/> + <waitForPageLoad stepKey="waitForShippingMethods"/> + <click selector="{{AdminInvoicePaymentShippingSection.shippingMethod}}" stepKey="chooseShippingMethod"/> + <waitForPageLoad stepKey="waitForShippingMethodsThickened"/> + <click selector="{{OrdersGridSection.submitOrder}}" stepKey="submitOrder"/> + <see stepKey="seeSuccessMessageForOrder" userInput="You created the order."/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderInStoreChoosingPaymentMethodActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderInStoreChoosingPaymentMethodActionGroup.xml new file mode 100644 index 0000000000000..7277a75c9bc73 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderInStoreChoosingPaymentMethodActionGroup.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateOrderInStoreChoosingPaymentMethodActionGroup"> + <annotations> + <description>Goes to the Admin Create Order page. Creates an Order based on the provided Customer, Store View and Product. Validates that the Success Message is present and correct.</description> + </annotations> + <arguments> + <argument name="product"/> + <argument name="customer"/> + <argument name="storeView"/> + </arguments> + + <amOnPage stepKey="navigateToNewOrderPage" url="{{AdminOrderCreatePage.url}}"/> + <click stepKey="chooseCustomer" selector="{{AdminOrdersGridSection.customerInOrdersSection(customer.firstname)}}"/> + <waitForPageLoad stepKey="waitForStoresPageOpened"/> + <click stepKey="chooseStore" selector="{{AdminOrderStoreScopeTreeSection.storeForOrder(storeView.name)}}"/> + <scrollToTopOfPage stepKey="scrollToTop"/> + <click selector="{{OrdersGridSection.addProducts}}" stepKey="clickOnAddProducts"/> + <waitForPageLoad stepKey="waitForProductsListForOrder"/> + <click selector="{{AdminOrdersGridSection.productForOrder(product.sku)}}" stepKey="chooseTheProduct"/> + <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="addSelectedProductToOrder"/> + <waitForPageLoad stepKey="waitForProductAddedInOrder"/> + <click selector="{{AdminInvoicePaymentShippingSection.getShippingMethodAndRates}}" stepKey="openShippingMethod"/> + <waitForPageLoad stepKey="waitForShippingMethods"/> + <click selector="{{AdminInvoicePaymentShippingSection.shippingMethod}}" stepKey="chooseShippingMethod"/> + <waitForPageLoad stepKey="waitForShippingMethodsThickened"/> + <waitForElementVisible selector="{{AdminOrderFormPaymentSection.paymentBlock}}" stepKey="waitForPaymentOptions"/> + <conditionalClick selector="{{AdminOrderFormPaymentSection.checkMoneyOption}}" dependentSelector="{{AdminOrderFormPaymentSection.checkMoneyOption}}" visible="true" stepKey="checkCheckMoneyOption"/> + <click selector="{{OrdersGridSection.submitOrder}}" stepKey="submitOrder"/> + <see stepKey="seeSuccessMessageForOrder" userInput="You created the order."/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderToPrintPageActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderToPrintPageActionGroup.xml index 7388eaa96f215..2b09048a7a11f 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderToPrintPageActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderToPrintPageActionGroup.xml @@ -34,13 +34,4 @@ <click selector="{{CheckoutSuccessMainSection.orderLink}}" stepKey="clickOrderLink"/> <click selector="{{StorefrontCustomerOrderViewSection.printOrderLink}}" stepKey="clickPrintOrderLink"/> </actionGroup> - - <actionGroup name="CreateOrderToPrintPageWithSelectedPaymentMethodActionGroup" extends="CreateOrderToPrintPageActionGroup"> - <annotations> - <description>EXTENDS: CreateOrderToPrintPageActionGroup. Clicks on 'Check / Money Order'.</description> - </annotations> - - <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" after="clickNext" stepKey="waitForPaymentSectionLoaded"/> - <conditionalClick selector="{{CheckoutPaymentSection.checkMoneyOrderPayment}}" dependentSelector="{{CheckoutPaymentSection.billingAddress}}" visible="false" before="waitForPlaceOrderButton" stepKey="clickCheckMoneyOrderPayment"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderToPrintPageWithSelectedPaymentMethodActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderToPrintPageWithSelectedPaymentMethodActionGroup.xml new file mode 100644 index 0000000000000..289855aee7e18 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderToPrintPageWithSelectedPaymentMethodActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateOrderToPrintPageWithSelectedPaymentMethodActionGroup" extends="CreateOrderToPrintPageActionGroup"> + <annotations> + <description>EXTENDS: CreateOrderToPrintPageActionGroup. Clicks on 'Check / Money Order'.</description> + </annotations> + + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" after="clickNext" stepKey="waitForPaymentSectionLoaded"/> + <conditionalClick selector="{{CheckoutPaymentSection.checkMoneyOrderPayment}}" dependentSelector="{{CheckoutPaymentSection.billingAddress}}" visible="false" before="waitForPlaceOrderButton" stepKey="clickCheckMoneyOrderPayment"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/DontSeeProductInItemsOrderedActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/DontSeeProductInItemsOrderedActionGroup.xml new file mode 100644 index 0000000000000..da0deb15f35c1 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/DontSeeProductInItemsOrderedActionGroup.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="DontSeeProductInItemsOrderedActionGroup"> + <arguments> + <argument name="product"/> + </arguments> + <dontSee selector="{{AdminOrderItemsOrderedSection.productSkuColumn}}" userInput="{{product.sku}}" stepKey="dontseeSkuInItemsOrdered"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/FillOrderCustomerInformationActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FillOrderCustomerInformationActionGroup.xml new file mode 100644 index 0000000000000..188e6856db186 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FillOrderCustomerInformationActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FillOrderCustomerInformationActionGroup"> + <annotations> + <description>Fills in the provided Customer and Address details on the Admin 'Create New Order for' page.</description> + </annotations> + <arguments> + <argument name="customer"/> + <argument name="address"/> + </arguments> + + <fillField selector="{{AdminOrderFormBillingAddressSection.FirstName}}" userInput="{{customer.firstname}}" stepKey="fillFirstName"/> + <fillField selector="{{AdminOrderFormBillingAddressSection.LastName}}" userInput="{{customer.lastname}}" stepKey="fillLastName"/> + <fillField selector="{{AdminOrderFormBillingAddressSection.StreetLine1}}" userInput="{{address.street[0]}}" stepKey="fillStreetLine1"/> + <fillField selector="{{AdminOrderFormBillingAddressSection.City}}" userInput="{{address.city}}" stepKey="fillCity"/> + <selectOption selector="{{AdminOrderFormBillingAddressSection.Country}}" userInput="{{address.country_id}}" stepKey="fillCountry"/> + <selectOption selector="{{AdminOrderFormBillingAddressSection.State}}" userInput="{{address.state}}" stepKey="fillState"/> + <fillField selector="{{AdminOrderFormBillingAddressSection.PostalCode}}" userInput="{{address.postcode}}" stepKey="fillPostalCode"/> + <fillField selector="{{AdminOrderFormBillingAddressSection.Phone}}" userInput="{{address.telephone}}" stepKey="fillPhone"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterInvoiceGridByOrderIdActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterInvoiceGridByOrderIdActionGroup.xml new file mode 100644 index 0000000000000..5ad2b23089c76 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterInvoiceGridByOrderIdActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FilterInvoiceGridByOrderIdActionGroup"> + <annotations> + <description>Goes to the Admin Invoices grid page. Filters the grid for the provided Order ID.</description> + </annotations> + <arguments> + <argument name="orderId" type="string"/> + </arguments> + + <amOnPage url="{{AdminInvoicesPage.url}}" stepKey="goToInvoices"/> + <click selector="{{AdminInvoicesGridSection.filter}}" stepKey="clickFilter"/> + <fillField selector="{{AdminInvoicesFiltersSection.orderNum}}" userInput="{{orderId}}" stepKey="fillOrderIdForFilter"/> + <click selector="{{AdminInvoicesFiltersSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForPageLoad stepKey="waitForFiltersApply"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterInvoiceGridByOrderIdWithCleanFiltersActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterInvoiceGridByOrderIdWithCleanFiltersActionGroup.xml new file mode 100644 index 0000000000000..88dfc1dc888c2 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterInvoiceGridByOrderIdWithCleanFiltersActionGroup.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="FilterInvoiceGridByOrderIdWithCleanFiltersActionGroup" extends="FilterInvoiceGridByOrderIdActionGroup"> + <arguments> + <argument name="orderId" type="string"/> + </arguments> + <conditionalClick selector="{{AdminInvoicesGridSection.clearFilters}}" dependentSelector="{{AdminInvoicesGridSection.clearFilters}}" visible="true" stepKey="clearFilters" after="goToInvoices"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderGridByBaseTotalRangeActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderGridByBaseTotalRangeActionGroup.xml new file mode 100644 index 0000000000000..cf200a99fc57c --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderGridByBaseTotalRangeActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FilterOrderGridByBaseTotalRangeActionGroup"> + <annotations> + <description>Goes to the Admin Orders page. Filters the grid based on the provided Grand Total From/To values.</description> + </annotations> + <arguments> + <argument name="from"/> + <argument name="to"/> + </arguments> + + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilters"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openOrderGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('base_grand_total[from]')}}" userInput="{{from}}" stepKey="fillOrderTotalFrom"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('base_grand_total[to]')}}" userInput="{{to}}" stepKey="fillOrderTotalTo"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFilters"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderGridByBillingNameActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderGridByBillingNameActionGroup.xml new file mode 100644 index 0000000000000..8636bd823e744 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderGridByBillingNameActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FilterOrderGridByBillingNameActionGroup"> + <annotations> + <description>Goes to the Admin Orders page. Filters the grid based on the provided Customer.</description> + </annotations> + <arguments> + <argument name="customer"/> + </arguments> + + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderGridPage"/> + <waitForPageLoad stepKey="waitForOrderGridLoad"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilters"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openOrderGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('billing_name')}}" userInput="{{customer.fullname}}" stepKey="fillBillToNameFilter"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderGridByIdActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderGridByIdActionGroup.xml new file mode 100644 index 0000000000000..bbab664809cdd --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderGridByIdActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!--Filter order grid by order id field--> + <actionGroup name="FilterOrderGridByIdActionGroup"> + <annotations> + <description>Goes to the Admin Orders page. Filters the grid based on the provided Order ID.</description> + </annotations> + <arguments> + <argument name="orderId" type="string"/> + </arguments> + + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderGridPage"/> + <waitForPageLoad stepKey="waitForOrdersPage"/> + <conditionalClick selector="{{AdminOrdersGridSection.clearFilters}}" dependentSelector="{{AdminOrdersGridSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilters"/> + <waitForPageLoad stepKey="waitForClearFilters"/> + <click selector="{{AdminOrdersGridSection.filters}}" stepKey="openOrderGridFilters"/> + <waitForPageLoad stepKey="waitForClickFilters"/> + <fillField selector="{{AdminOrdersGridSection.idFilter}}" userInput="{{orderId}}" stepKey="fillOrderIdFilter"/> + <click selector="{{AdminOrdersGridSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> + <waitForPageLoad stepKey="waitForApplyFilters"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderGridByPurchaseDateActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderGridByPurchaseDateActionGroup.xml new file mode 100644 index 0000000000000..538faf1327b35 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderGridByPurchaseDateActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FilterOrderGridByPurchaseDateActionGroup"> + <annotations> + <description>Goes to the Admin Orders page. Filters the grid based on the provided Purchased Date From/To values.</description> + </annotations> + <arguments> + <argument name="from"/> + <argument name="to"/> + </arguments> + + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilters"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openOrderGridFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('created_at[from]')}}" userInput="{{from}}" stepKey="fillOrderPurchaseDateFrom"/> + <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('created_at[to]')}}" userInput="{{to}}" stepKey="fillOrderPurchaseDateTo"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFilters"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderGridByStatusActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderGridByStatusActionGroup.xml new file mode 100644 index 0000000000000..05731e06c6228 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderGridByStatusActionGroup.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="FilterOrderGridByStatusActionGroup"> + <annotations> + <description>Filters the Admin Orders grid based on the provided Order Status.</description> + </annotations> + <arguments> + <argument name="status"/> + </arguments> + + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilters"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openOrderGridFilters"/> + <selectOption selector="{{AdminDataGridHeaderSection.filterFieldSelect('status')}}" userInput="{{status}}" stepKey="fillOrderStatusFilter"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFilters"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderStatusByLabelAndCodeActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderStatusByLabelAndCodeActionGroup.xml index 96e562cb95c6f..06ff1597dc608 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderStatusByLabelAndCodeActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderStatusByLabelAndCodeActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="FilterOrderStatusByLabelAndCodeActionGroup"> <arguments> <argument name="statusLabel" type="string"/> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/GoToInvoiceIntoOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/GoToInvoiceIntoOrderActionGroup.xml new file mode 100644 index 0000000000000..15c7f3e9a7022 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/GoToInvoiceIntoOrderActionGroup.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="GoToInvoiceIntoOrderActionGroup"> + <annotations> + <description>Clicks on 'Invoice' on the Admin Orders view page. Validates that the URL and Page Title are correct.</description> + </annotations> + + <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceAction"/> + <seeInCurrentUrl url="{{AdminInvoiceNewPage.url}}" stepKey="seeOrderInvoiceUrl"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Invoice" stepKey="seePageNameNewInvoicePage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/NavigateToNewOrderPageExistingCustomerActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/NavigateToNewOrderPageExistingCustomerActionGroup.xml new file mode 100644 index 0000000000000..a8f9d61ad6803 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/NavigateToNewOrderPageExistingCustomerActionGroup.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="NavigateToNewOrderPageExistingCustomerActionGroup"> + <annotations> + <description>Goes tot he Admin Orders grid page. Clicks on 'Create New Order'. Filters the grid for the provided Customer. Clicks on the Customer. Selects the provided Store View, if present. Validates that the Page Title is present and correct.</description> + </annotations> + <arguments> + <argument name="customer"/> + <argument name="storeView" defaultValue="_defaultStore"/> + </arguments> + + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderIndexPage"/> + <waitForPageLoad stepKey="waitForIndexPageLoad"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Orders" stepKey="seeIndexPageTitle"/> + <click selector="{{AdminOrdersGridSection.createNewOrder}}" stepKey="clickCreateNewOrder"/> + <waitForPageLoad stepKey="waitForCustomerGridLoad"/> + + <!--Clear grid filters--> + <conditionalClick selector="{{AdminOrderCustomersGridSection.resetButton}}" dependentSelector="{{AdminOrderCustomersGridSection.resetButton}}" visible="true" stepKey="clearExistingCustomerFilters"/> + <fillField userInput="{{customer.email}}" selector="{{AdminOrderCustomersGridSection.emailInput}}" stepKey="filterEmail"/> + <click selector="{{AdminOrderCustomersGridSection.apply}}" stepKey="applyFilter"/> + <waitForPageLoad stepKey="waitForFilteredCustomerGridLoad"/> + <click selector="{{AdminOrderCustomersGridSection.firstRow}}" stepKey="clickOnCustomer"/> + <waitForPageLoad stepKey="waitForCreateOrderPageLoad"/> + + <!-- Select store view if appears --> + <conditionalClick selector="{{AdminOrderStoreScopeTreeSection.storeOption(storeView.name)}}" dependentSelector="{{AdminOrderStoreScopeTreeSection.storeOption(storeView.name)}}" visible="true" stepKey="selectStoreViewIfAppears"/> + <waitForPageLoad stepKey="waitForCreateOrderPageLoadAfterStoreSelect"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Create New Order" stepKey="seeNewOrderPageTitle"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/NavigateToNewOrderPageExistingCustomerAndStoreActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/NavigateToNewOrderPageExistingCustomerAndStoreActionGroup.xml new file mode 100644 index 0000000000000..883f1047feb79 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/NavigateToNewOrderPageExistingCustomerAndStoreActionGroup.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="NavigateToNewOrderPageExistingCustomerAndStoreActionGroup" extends="NavigateToNewOrderPageExistingCustomerActionGroup"> + <annotations> + <description>EXTENDS: NavigateToNewOrderPageExistingCustomerActionGroup. Clicks on the provided Store View.</description> + </annotations> + <arguments> + <argument name="storeView" defaultValue="_defaultStore"/> + </arguments> + + <click selector="{{AdminOrderStoreScopeTreeSection.storeOption(storeView.name)}}" stepKey="selectStoreView" after="waitForCreateOrderPageLoad"/> + <waitForPageLoad stepKey="waitForLoad" after="selectStoreView"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/NavigateToNewOrderPageNewCustomerActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/NavigateToNewOrderPageNewCustomerActionGroup.xml new file mode 100644 index 0000000000000..73a4da42eb093 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/NavigateToNewOrderPageNewCustomerActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!--Navigate to create order page (New Order -> Create New Customer)--> + <actionGroup name="NavigateToNewOrderPageNewCustomerActionGroup"> + <annotations> + <description>Goes to the Admin Orders grid page. Clicks on 'Create New Order'. Clicks on 'Create New Customer'. Select the provided Store View, if present. Validates that Page Title is present and correct.</description> + </annotations> + <arguments> + <argument name="storeView" defaultValue="_defaultStore"/> + </arguments> + + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderIndexPage"/> + <waitForPageLoad stepKey="waitForIndexPageLoad"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Orders" stepKey="seeIndexPageTitle"/> + <click selector="{{AdminOrdersGridSection.createNewOrder}}" stepKey="clickCreateNewOrder"/> + <click selector="{{AdminOrderFormActionSection.CreateNewCustomer}}" stepKey="clickCreateCustomer"/> + <conditionalClick selector="{{AdminOrderStoreScopeTreeSection.storeOption(storeView.name)}}" dependentSelector="{{AdminOrderStoreScopeTreeSection.storeOption(storeView.name)}}" visible="true" stepKey="selectStoreViewIfAppears"/> + <waitForPageLoad stepKey="waitForCreateOrderPageLoadAfterStoreSelect"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Create New Order" stepKey="seeNewOrderPageTitle"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/NavigateToNewOrderPageNewCustomerSingleStoreActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/NavigateToNewOrderPageNewCustomerSingleStoreActionGroup.xml new file mode 100644 index 0000000000000..cd1be0ac34279 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/NavigateToNewOrderPageNewCustomerSingleStoreActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="NavigateToNewOrderPageNewCustomerSingleStoreActionGroup"> + <annotations> + <description>Goes to the Admin Orders grid page. Clicks on 'Create New Order'. Clicks on 'Create New Customer'. Validates that Page Title is present and correct.</description> + </annotations> + <arguments> + <argument name="storeView" defaultValue="_defaultStore"/> + </arguments> + + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderIndexPage"/> + <waitForPageLoad stepKey="waitForIndexPageLoad"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Orders" stepKey="seeIndexPageTitle"/> + <click selector="{{AdminOrdersGridSection.createNewOrder}}" stepKey="clickCreateNewOrder"/> + <click selector="{{AdminOrderFormActionSection.CreateNewCustomer}}" stepKey="clickCreateCustomer"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Create New Order" stepKey="seeNewOrderPageTitle"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/NewAddConfigurableProductToOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/NewAddConfigurableProductToOrderActionGroup.xml new file mode 100644 index 0000000000000..20f2ab162688d --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/NewAddConfigurableProductToOrderActionGroup.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="NewAddConfigurableProductToOrderActionGroup" extends="AddConfigurableProductToOrderActionGroup"> + <remove keyForRemoval="waitForConfigurablePopover"/> + <remove keyForRemoval="selectionConfigurableOption"/> + <selectOption selector="{{AdminOrderFormConfigureProductSection.selectOption}}" userInput="{{option.value}}" stepKey="selectOption" after="waitForOptionsToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/OpenOrderByIdActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/OpenOrderByIdActionGroup.xml new file mode 100644 index 0000000000000..16dacff3ecf81 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/OpenOrderByIdActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="OpenOrderByIdActionGroup" extends="FilterOrderGridByIdActionGroup"> + <annotations> + <description>EXTENDS: FilterOrderGridByIdActionGroup. Clicks on the 1st row of the Admin Orders grid.</description> + </annotations> + + <click selector="{{AdminDataGridTableSection.firstRow}}" after="clickOrderApplyFilters" stepKey="openOrderViewPage"/> + <waitForPageLoad after="openOrderViewPage" stepKey="waitForOrderViewPageOpened"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/OrderSelectFlatRateShippingActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/OrderSelectFlatRateShippingActionGroup.xml new file mode 100644 index 0000000000000..b85dc21e0b1b5 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/OrderSelectFlatRateShippingActionGroup.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="OrderSelectFlatRateShippingActionGroup"> + <annotations> + <description>Selects the 'Flat Rate' Shipping Method on the Admin 'Create New Order for' page.</description> + </annotations> + + <click selector="{{AdminOrderFormPaymentSection.header}}" stepKey="unfocus"/> + <waitForPageLoad stepKey="waitForJavascriptToFinish"/> + <click selector="{{AdminOrderFormPaymentSection.getShippingMethods}}" stepKey="clickShippingMethods"/> + <waitForElementVisible selector="{{AdminOrderFormPaymentSection.flatRateOption}}" stepKey="waitForShippingOptions"/> + <selectOption selector="{{AdminOrderFormPaymentSection.flatRateOption}}" userInput="flatrate_flatrate" stepKey="checkFlatRate"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/OrderSelectFreeShippingActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/OrderSelectFreeShippingActionGroup.xml new file mode 100644 index 0000000000000..ef78fe892e933 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/OrderSelectFreeShippingActionGroup.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="OrderSelectFreeShippingActionGroup"> + <annotations> + <description>Selects the 'Free Shipping' Shipping Method on the Admin 'Create New Order for' page.</description> + </annotations> + + <click selector="{{AdminOrderFormPaymentSection.header}}" stepKey="unfocus"/> + <waitForPageLoad stepKey="waitForJavascriptToFinish"/> + <click selector="{{AdminOrderFormPaymentSection.getShippingMethods}}" stepKey="clickShippingMethods"/> + <waitForElementVisible selector="{{AdminOrderFormPaymentSection.freeShippingOption}}" stepKey="waitForShippingOptions"/> + <selectOption selector="{{AdminOrderFormPaymentSection.freeShippingOption}}" userInput="freeshipping_freeshipping" stepKey="checkFreeShipping"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/SeeProductInComparisonListActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SeeProductInComparisonListActionGroup.xml new file mode 100644 index 0000000000000..44dc8353d314d --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SeeProductInComparisonListActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SeeProductInComparisonListActionGroup"> + <annotations> + <description>Validate that the Product is present in the comparison list</description> + </annotations> + <arguments> + <argument name="productVar"/> + </arguments> + + <amOnPage url="{{StorefrontProductComparePage.url}}" stepKey="navigateToComparePage"/> + <waitForPageLoad stepKey="waitForStorefrontProductComparePageLoad"/> + <seeElement selector="{{StorefrontProductCompareMainSection.ProductLinkByName(productVar.name)}}" + stepKey="seeProductInCompareList"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/SeeProductInInvoiceItemsActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SeeProductInInvoiceItemsActionGroup.xml new file mode 100644 index 0000000000000..0d35414617ff8 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SeeProductInInvoiceItemsActionGroup.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="SeeProductInInvoiceItemsActionGroup"> + <annotations> + <description>Validates that the provided Product appears under the 'SKU' column in the Admin Invoices edit page.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <see selector="{{AdminInvoiceItemsSection.skuColumn}}" userInput="{{product.sku}}" stepKey="seeProductSkuInGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/SeeProductInItemsOrderedActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SeeProductInItemsOrderedActionGroup.xml new file mode 100644 index 0000000000000..05328ab046bf8 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SeeProductInItemsOrderedActionGroup.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="SeeProductInItemsOrderedActionGroup"> + <annotations> + <description>Validates that the provided Product is present and correct in the 'Items Ordered' section on the Admin Orders view page.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <see selector="{{AdminOrderItemsOrderedSection.productSkuColumn}}" userInput="{{product.sku}}" stepKey="seeSkuInItemsOrdered"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/SeeProductInItemsRefundedActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SeeProductInItemsRefundedActionGroup.xml new file mode 100644 index 0000000000000..0690a190d3f0f --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SeeProductInItemsRefundedActionGroup.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="SeeProductInItemsRefundedActionGroup"> + <annotations> + <description>Validates that the provided Product appears in the 'Product' column on the Admin Credit Memo view page.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <see selector="{{AdminCreditMemoItemsSection.skuColumn}}" userInput="{{product.sku}}" stepKey="seeProductSkuInGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectActionForOrdersActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectActionForOrdersActionGroup.xml index 073eb03b11bfa..4351c44ed75aa 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectActionForOrdersActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectActionForOrdersActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="SelectActionForOrdersActionGroup"> <arguments> <argument name="action" type="string"/> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectBankTransferPaymentMethodActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectBankTransferPaymentMethodActionGroup.xml new file mode 100644 index 0000000000000..8c1ceeb97201d --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectBankTransferPaymentMethodActionGroup.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="SelectBankTransferPaymentMethodActionGroup" extends="SelectCheckMoneyPaymentMethodActionGroup"> + <remove keyForRemoval="checkCheckMoneyOption"/> + <conditionalClick selector="{{AdminOrderFormPaymentSection.checkBankTransfer}}" dependentSelector="{{AdminOrderFormPaymentSection.checkBankTransfer}}" visible="true" stepKey="checkBankTransferOption" after="waitForPaymentOptions"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectCashOnDeliveryPaymentMethodActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectCashOnDeliveryPaymentMethodActionGroup.xml new file mode 100644 index 0000000000000..dc6d8d4a8ffaf --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectCashOnDeliveryPaymentMethodActionGroup.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="SelectCashOnDeliveryPaymentMethodActionGroup" extends="SelectCheckMoneyPaymentMethodActionGroup"> + <remove keyForRemoval="checkCheckMoneyOption"/> + <conditionalClick selector="{{AdminOrderFormPaymentSection.checkCashOnDelivery}}" dependentSelector="{{AdminOrderFormPaymentSection.checkCashOnDelivery}}" visible="true" stepKey="checkCashOnDeliveryOption" after="waitForPaymentOptions"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectCheckMoneyPaymentMethodActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectCheckMoneyPaymentMethodActionGroup.xml new file mode 100644 index 0000000000000..044a7fa9f8b3b --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectCheckMoneyPaymentMethodActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SelectCheckMoneyPaymentMethodActionGroup"> + <annotations> + <description>Selects the 'Check / Money Order' Payment Method on the Admin Create New Order page.</description> + </annotations> + + <waitForElementVisible selector="{{AdminOrderFormPaymentSection.paymentBlock}}" stepKey="waitForPaymentOptions"/> + <conditionalClick selector="{{AdminOrderFormPaymentSection.checkMoneyOption}}" dependentSelector="{{AdminOrderFormPaymentSection.checkMoneyOption}}" visible="true" stepKey="checkCheckMoneyOption"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectPurchaseOrderPaymentMethodActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectPurchaseOrderPaymentMethodActionGroup.xml new file mode 100644 index 0000000000000..c9502afb3777f --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectPurchaseOrderPaymentMethodActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SelectPurchaseOrderPaymentMethodActionGroup" extends="SelectCheckMoneyPaymentMethodActionGroup"> + <arguments> + <argument name="purchaseOrderNumber" type="string"/> + </arguments> + <remove keyForRemoval="checkCheckMoneyOption"/> + <conditionalClick selector="{{AdminOrderFormPaymentSection.checkPurchaseOrder}}" dependentSelector="{{AdminOrderFormPaymentSection.checkPurchaseOrder}}" visible="true" stepKey="checkPurchaseOrderOption" after="waitForPaymentOptions"/> + <fillField selector="{{AdminOrderFormPaymentSection.fieldPurchaseOrderNumber}}" userInput="{{purchaseOrderNumber}}" stepKey="fillPurchaseOrderNumber"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/StartCreateInvoiceFromOrderPageActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/StartCreateInvoiceFromOrderPageActionGroup.xml new file mode 100644 index 0000000000000..25906e895726d --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/StartCreateInvoiceFromOrderPageActionGroup.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="StartCreateInvoiceFromOrderPageActionGroup"> + <annotations> + <description>Clicks on 'Invoice' on the Admin Orders view page. Validates that the URL and Page Title are correct.</description> + </annotations> + + <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceAction"/> + <seeInCurrentUrl url="{{AdminInvoiceNewPage.url}}" stepKey="seeNewInvoiceUrl"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Invoice" stepKey="seeNewInvoicePageTitle"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/StartToCreateCreditMemoActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/StartToCreateCreditMemoActionGroup.xml new file mode 100644 index 0000000000000..fbfdf4979fda8 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/StartToCreateCreditMemoActionGroup.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="StartToCreateCreditMemoActionGroup"> + <arguments> + <argument name="orderId" type="string"/> + </arguments> + <amOnPage url="{{AdminOrderPage.url(orderId)}}" stepKey="navigateToOrderPage"/> + <click selector="{{AdminOrderDetailsMainActionsSection.creditMemo}}" stepKey="clickCreditMemo"/> + <waitForElementVisible selector="{{AdminHeaderSection.pageTitle}}" stepKey="waitForPageTitle"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Memo" stepKey="seeNewMemoPageTitle"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/SubmitCreditMemoActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SubmitCreditMemoActionGroup.xml new file mode 100644 index 0000000000000..fbd2a0047d558 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SubmitCreditMemoActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SubmitCreditMemoActionGroup"> + <grabFromCurrentUrl regex="~/order_id/(\d+)/~" stepKey="grabOrderId"/> + <waitForElementVisible selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="waitButtonEnabled"/> + <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickSubmitCreditMemo"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForMessageAppears"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You created the credit memo." stepKey="seeCreditMemoCreateSuccess"/> + <seeInCurrentUrl url="{{AdminOrderDetailsPage.url}}$grabOrderId" stepKey="seeViewOrderPageCreditMemo"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/SubmitInvoiceActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SubmitInvoiceActionGroup.xml new file mode 100644 index 0000000000000..2ff4dd210a187 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SubmitInvoiceActionGroup.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="SubmitInvoiceActionGroup"> + <annotations> + <description>Clicks on 'Submit Invoice' on the Admin 'New Invoice' page. Validates that the Success Message is present and correct. Validates that the Order ID appears in the URL.</description> + </annotations> + + <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForMessageAppears"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The invoice has been created." stepKey="seeInvoiceCreateSuccess"/> + <grabFromCurrentUrl regex="~/order_id/(\d+)/~" stepKey="grabOrderId"/> + <seeInCurrentUrl url="{{AdminOrderDetailsPage.url('$grabOrderId')}}" stepKey="seeViewOrderPageInvoice"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/UpdateCreditMemoTotalsActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/UpdateCreditMemoTotalsActionGroup.xml new file mode 100644 index 0000000000000..18707a39853a3 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/UpdateCreditMemoTotalsActionGroup.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="UpdateCreditMemoTotalsActionGroup"> + <waitForElementVisible selector="{{AdminCreditMemoTotalSection.updateTotals}}" stepKey="waitUpdateTotalsButtonEnabled"/> + <click selector="{{AdminCreditMemoTotalSection.updateTotals}}" stepKey="clickUpdateTotals"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyBasicCreditMemoInformationActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyBasicCreditMemoInformationActionGroup.xml new file mode 100644 index 0000000000000..04e4c40060d27 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyBasicCreditMemoInformationActionGroup.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!--Check customer information is correct in credit memo--> + <actionGroup name="VerifyBasicCreditMemoInformationActionGroup"> + <annotations> + <description>Validates that the provided Customer, Shipping/Billing Address and Customer Group are present and correct on the Admin Credit Memo view page.</description> + </annotations> + <arguments> + <argument name="customer" defaultValue=""/> + <argument name="shippingAddress" defaultValue=""/> + <argument name="billingAddress" defaultValue=""/> + <argument name="customerGroup" defaultValue="GeneralCustomerGroup"/> + </arguments> + + <see selector="{{AdminCreditMemoOrderInformationSection.customerName}}" userInput="{{customer.firstname}}" stepKey="seeCustomerName"/> + <see selector="{{AdminCreditMemoOrderInformationSection.customerEmail}}" userInput="{{customer.email}}" stepKey="seeCustomerEmail"/> + <see selector="{{AdminCreditMemoOrderInformationSection.customerGroup}}" userInput="{{customerGroup.code}}" stepKey="seeCustomerGroup"/> + + <see selector="{{AdminCreditMemoAddressInformationSection.billingAddress}}" userInput="{{billingAddress.street[0]}}" stepKey="seeBillingAddressStreet"/> + <see selector="{{AdminCreditMemoAddressInformationSection.billingAddress}}" userInput="{{billingAddress.city}}" stepKey="seeBillingAddressCity"/> + <see selector="{{AdminCreditMemoAddressInformationSection.billingAddress}}" userInput="{{billingAddress.country_id}}" stepKey="seeBillingAddressCountry"/> + <see selector="{{AdminCreditMemoAddressInformationSection.billingAddress}}" userInput="{{billingAddress.postcode}}" stepKey="seeBillingAddressPostcode"/> + + <see selector="{{AdminCreditMemoAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.street[0]}}" stepKey="seeShippingAddressStreet"/> + <see selector="{{AdminCreditMemoAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.city}}" stepKey="seeShippingAddressCity"/> + <see selector="{{AdminCreditMemoAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.country_id}}" stepKey="seeShippingAddressCountry"/> + <see selector="{{AdminCreditMemoAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.postcode}}" stepKey="seeShippingAddressPostcode"/> + </actionGroup> + + + +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyBasicInvoiceInformationActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyBasicInvoiceInformationActionGroup.xml new file mode 100644 index 0000000000000..431e5e7dbfee1 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyBasicInvoiceInformationActionGroup.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!--Check customer information is correct in invoice--> + <actionGroup name="VerifyBasicInvoiceInformationActionGroup"> + <annotations> + <description>Validates that the provided Customer, Address and Customer Group details are present and correct on the Admin View Invoice page.</description> + </annotations> + <arguments> + <argument name="customer"/> + <argument name="shippingAddress"/> + <argument name="billingAddress"/> + <argument name="customerGroup" defaultValue="GeneralCustomerGroup"/> + </arguments> + + <see selector="{{AdminInvoiceOrderInformationSection.customerName}}" userInput="{{customer.firstname}}" stepKey="seeCustomerName"/> + <see selector="{{AdminInvoiceOrderInformationSection.customerEmail}}" userInput="{{customer.email}}" stepKey="seeCustomerEmail"/> + <see selector="{{AdminInvoiceOrderInformationSection.customerGroup}}" userInput="{{customerGroup.code}}" stepKey="seeCustomerGroup"/> + <see selector="{{AdminInvoiceAddressInformationSection.billingAddress}}" userInput="{{billingAddress.street[0]}}" stepKey="seeBillingAddressStreet"/> + <see selector="{{AdminInvoiceAddressInformationSection.billingAddress}}" userInput="{{billingAddress.city}}" stepKey="seeBillingAddressCity"/> + <see selector="{{AdminInvoiceAddressInformationSection.billingAddress}}" userInput="{{billingAddress.country_id}}" stepKey="seeBillingAddressCountry"/> + <see selector="{{AdminInvoiceAddressInformationSection.billingAddress}}" userInput="{{billingAddress.postcode}}" stepKey="seeBillingAddressPostcode"/> + <see selector="{{AdminInvoiceAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.street[0]}}" stepKey="seeShippingAddressStreet"/> + <see selector="{{AdminInvoiceAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.city}}" stepKey="seeShippingAddressCity"/> + <see selector="{{AdminInvoiceAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.country_id}}" stepKey="seeShippingAddressCountry"/> + <see selector="{{AdminInvoiceAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.postcode}}" stepKey="seeShippingAddressPostcode"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyBasicOrderInformationActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyBasicOrderInformationActionGroup.xml new file mode 100644 index 0000000000000..412586739006b --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyBasicOrderInformationActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="VerifyBasicOrderInformationActionGroup"> + <annotations> + <description>Validates that the provided Customer, Shipping/Billing Address and Customer Group are present and correct on the Admin Orders view page.</description> + </annotations> + <arguments> + <argument name="customer"/> + <argument name="shippingAddress"/> + <argument name="billingAddress"/> + <argument name="customerGroup" defaultValue="GeneralCustomerGroup"/> + </arguments> + + <see selector="{{AdminOrderDetailsInformationSection.customerName}}" userInput="{{customer.firstname}}" stepKey="seeCustomerName"/> + <see selector="{{AdminOrderDetailsInformationSection.customerEmail}}" userInput="{{customer.email}}" stepKey="seeCustomerEmail"/> + <see selector="{{AdminOrderDetailsInformationSection.customerGroup}}" userInput="{{customerGroup.code}}" stepKey="seeCustomerGroup"/> + <see selector="{{AdminOrderAddressInformationSection.billingAddress}}" userInput="{{billingAddress.street[0]}}" stepKey="seeBillingAddressStreet"/> + <see selector="{{AdminOrderAddressInformationSection.billingAddress}}" userInput="{{billingAddress.city}}" stepKey="seeBillingAddressCity"/> + <see selector="{{AdminOrderAddressInformationSection.billingAddress}}" userInput="{{billingAddress.country_id}}" stepKey="seeBillingAddressCountry"/> + <see selector="{{AdminOrderAddressInformationSection.billingAddress}}" userInput="{{billingAddress.postcode}}" stepKey="seeBillingAddressPostcode"/> + <see selector="{{AdminOrderAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.street[0]}}" stepKey="seeShippingAddressStreet"/> + <see selector="{{AdminOrderAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.city}}" stepKey="seeShippingAddressCity"/> + <see selector="{{AdminOrderAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.country_id}}" stepKey="seeShippingAddressCountry"/> + <see selector="{{AdminOrderAddressInformationSection.shippingAddress}}" userInput="{{shippingAddress.postcode}}" stepKey="seeShippingAddressPostcode"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyCreatedOrderInformationActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyCreatedOrderInformationActionGroup.xml new file mode 100644 index 0000000000000..9a62771fb54cd --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/VerifyCreatedOrderInformationActionGroup.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="VerifyCreatedOrderInformationActionGroup"> + <annotations> + <description>Validates that the Success Message, Order Status (Pending) and Order ID are present and correct.</description> + </annotations> + + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeSuccessMessage"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeOrderPendingStatus" after="seeSuccessMessage"/> + <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId" after="seeOrderPendingStatus"/> + <assertNotEmpty actual="$getOrderId" stepKey="assertOrderIdIsNotEmpty" after="getOrderId"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml b/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml index bc9486d61fbfe..680d44ebb34fe 100644 --- a/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml +++ b/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml @@ -19,5 +19,6 @@ <section name="AdminOrderFormTotalSection"/> <section name="AdminOrderFormStoreSelectorSection"/> <section name="AdminOrderFormDiscountSection"/> + <section name="AdminOrderFormMessagesSection"/> </page> </pages> diff --git a/app/code/Magento/Sales/Test/Mftf/Page/StorefrontCustomerSignOutPage.xml b/app/code/Magento/Sales/Test/Mftf/Page/StorefrontCustomerSignOutPage.xml index 4e89e5476c3bc..0cfc9f2231f85 100644 --- a/app/code/Magento/Sales/Test/Mftf/Page/StorefrontCustomerSignOutPage.xml +++ b/app/code/Magento/Sales/Test/Mftf/Page/StorefrontCustomerSignOutPage.xml @@ -7,6 +7,6 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="StorefrontCustomerSignOutPage" url="/customer/account/logout/" area="storefront" module="Magento_Customer"/> </pages> diff --git a/app/code/Magento/Sales/Test/Mftf/Page/StorefrontSalesOrderPrintPage.xml b/app/code/Magento/Sales/Test/Mftf/Page/StorefrontSalesOrderPrintPage.xml index 874e6889ec58c..437449aa887d2 100644 --- a/app/code/Magento/Sales/Test/Mftf/Page/StorefrontSalesOrderPrintPage.xml +++ b/app/code/Magento/Sales/Test/Mftf/Page/StorefrontSalesOrderPrintPage.xml @@ -7,7 +7,7 @@ --> <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="StorefrontSalesOrderPrintPage" url="/sales/order/print/order_id/{{var1}}/" parameterized="true" area="storefront" module="Magento_Sales"> <section name="SalesOrderPrintSection"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCommentsTabSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCommentsTabSection.xml index 19f447117959a..dce0875e29336 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCommentsTabSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderCommentsTabSection.xml @@ -11,5 +11,6 @@ <section name="AdminOrderCommentsTabSection"> <element name="orderNotesList" type="text" selector="#Order_History .edit-order-comments .note-list"/> <element name="orderComments" type="text" selector="#Order_History .edit-order-comments-block"/> + <element name="orderComment" type="text" selector="#Order_History .comments-block-item-comment"/> </section> -</sections> \ No newline at end of file +</sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml index 8c093891ac46d..4f065ec7eb748 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml @@ -9,6 +9,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminOrderFormConfigureProductSection"> + <element name="configure" type="button" selector="//a[@product_id='{{productId}}']" parameterized="true"/> <element name="optionSelect" type="select" selector="//div[contains(@class,'product-options')]//select[//label[text() = '{{option}}']]" parameterized="true"/> <element name="optionSelectNew" type="select" selector="//label[text()='{{option1}}']/following-sibling::div/select" parameterized="true"/> <element name="quantity" type="input" selector="#product_composite_configure_input_qty"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormMessagesSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormMessagesSection.xml new file mode 100644 index 0000000000000..b5e6f6b6ede83 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormMessagesSection.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="AdminOrderFormMessagesSection"> + <element name="success" type="text" selector="#order-message div.message-success"/> + <element name="error" type="text" selector="#order-message div.message-error"/> + </section> +</sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderInvoicesTabSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderInvoicesTabSection.xml index 88d90bc716576..83eb733c8d298 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderInvoicesTabSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderInvoicesTabSection.xml @@ -17,7 +17,7 @@ <element name="filters" type="button" selector="//div[@id='sales_order_view_tabs_order_invoices_content']//button[@data-action='grid-filter-expand']" timeout="30"/> <element name="applyFilters" type="button" selector="//div[@id='sales_order_view_tabs_order_invoices_content']//button[@data-action='grid-filter-apply']" timeout="30"/> <element name="invoiceId" type="input" selector="//div[@id='sales_order_view_tabs_order_invoices_content']//input[@name='increment_id']" timeout="30"/> - <element name="amountFrom" type="input" selector="[name='base_grand_total[from]']" timeout="30"/> - <element name="amountTo" type="input" selector="[name='base_grand_total[to]']" timeout="30"/> + <element name="amountFrom" type="input" selector="[name='grand_total[from]']" timeout="30"/> + <element name="amountTo" type="input" selector="[name='grand_total[to]']" timeout="30"/> </section> -</sections> \ No newline at end of file +</sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderTotalSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderTotalSection.xml index 9b7356127df69..eb9c32d6ced9f 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderTotalSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderTotalSection.xml @@ -11,6 +11,7 @@ <section name="AdminOrderTotalSection"> <element name="subTotal" type="text" selector=".order-subtotal-table tbody tr.col-0>td span.price"/> <element name="grandTotal" type="text" selector=".order-subtotal-table tfoot tr.col-0>td span.price"/> + <element name="shippingDescription" type="text" selector="//table[contains(@class, 'order-subtotal-table')]//td[contains(text(), 'Shipping & Handling')]"/> <element name="shippingAndHandling" type="text" selector="//table[contains(@class, 'order-subtotal-table')]//td[normalize-space(.)='Shipping & Handling']/following-sibling::td//span[@class='price']"/> <element name="total" type="text" selector="//table[contains(@class,'order-subtotal-table')]/tbody/tr/td[contains(text(), '{{total}}')]/following-sibling::td/span/span[contains(@class, 'price')]" parameterized="true"/> </section> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/SalesOrderPrintSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/SalesOrderPrintSection.xml index b08a66140fabf..7c1d7319e30ea 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/SalesOrderPrintSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/SalesOrderPrintSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="SalesOrderPrintSection"> <element name="isOrderPrintPage" type="block" selector=".preview-area"/> </section> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/StorefrontOrderDetailsSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/StorefrontOrderDetailsSection.xml index c255e15cd6ecf..d262dfa9b010c 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/StorefrontOrderDetailsSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/StorefrontOrderDetailsSection.xml @@ -12,6 +12,7 @@ <element name="orderDetailsBlock" type="block" selector=".block-order-details-view"/> <element name="billingAddressBlock" type="block" selector=".box-order-billing-address > .box-content > address"/> <element name="discountSalesRule" type="text" selector="tr.discount span.price"/> + <element name="shippingTotalDescription" type="text" selector="#my-orders-table tr.shipping th.mark"/> <element name="grandTotalPrice" type="text" selector="tr.grand_total span.price"/> <element name="paymentMethod" type="text" selector=".box-order-billing-method dt.title"/> <element name="shippingMethod" type="text" selector=".box-order-shipping-method div.box-content"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AddSimpleProductToOrderFromShoppingCartTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AddSimpleProductToOrderFromShoppingCartTest.xml index 76be3a1094327..589da3e49dc89 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AddSimpleProductToOrderFromShoppingCartTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AddSimpleProductToOrderFromShoppingCartTest.xml @@ -46,8 +46,9 @@ </actionGroup> <!-- Add product to cart --> - <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart"> + <actionGroup ref="StorefrontAddSimpleProductWithQtyActionGroup" stepKey="addSimpleProductToCart"> <argument name="product" value="$$createProduct$$"/> + <argument name="quantity" value="2"/> </actionGroup> <!-- Login as admin --> @@ -64,6 +65,7 @@ <click selector="{{AdminCustomerMainActionsSection.createOrderBtn}}" stepKey="clickCreateOrder"/> <!-- Check product in customer's activities in shopping cart section --> + <see selector="{{AdminCreateOrderShoppingCartSection.shoppingCartBlock}}" userInput="Shopping Cart (2)" stepKey="seeCorrectNumberInCart"/> <see selector="{{AdminCustomerActivitiesShoppingCartSection.productName}}" userInput="$$createProduct.name$$" stepKey="seeProductNameInShoppingCartSection"/> <see selector="{{AdminCustomerActivitiesShoppingCartSection.productPrice}}" userInput="$$createProduct.price$$" stepKey="seeProductPriceInShoppingCartSection"/> @@ -75,6 +77,6 @@ <!-- Assert product in items ordered grid --> <see selector="{{AdminCustomerCreateNewOrderSection.productName}}" userInput="$$createProduct.name$$" stepKey="seeProductName"/> <see selector="{{AdminCustomerCreateNewOrderSection.productPrice}}" userInput="$$createProduct.price$$" stepKey="seeProductPrice"/> - <seeInField selector="{{AdminCustomerCreateNewOrderSection.productQty}}" userInput="{{ApiSimpleSingleQty.quantity}}" stepKey="seeProductQty"/> + <seeInField selector="{{AdminCustomerCreateNewOrderSection.productQty}}" userInput="2" stepKey="seeProductQty"/> </test> </tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminAvailabilityCreditMemoWithNoPaymentTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminAvailabilityCreditMemoWithNoPaymentTest.xml index e405173429b2c..ec518c79a0808 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminAvailabilityCreditMemoWithNoPaymentTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminAvailabilityCreditMemoWithNoPaymentTest.xml @@ -42,11 +42,11 @@ </after> <!--Proceed to Admin panel > SALES > Orders. Created order should be in Processing status--> - <actionGroup ref="navigateToNewOrderPageNewCustomer" stepKey="navigateToNewOrderPage"/> + <actionGroup ref="NavigateToNewOrderPageNewCustomerActionGroup" stepKey="navigateToNewOrderPage"/> <!--Check if order can be submitted without the required fields including email address--> <scrollToTopOfPage stepKey="scrollToTopOfOrderFormPage"/> - <actionGroup ref="addSimpleProductToOrder" stepKey="addFirstProductToOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addFirstProductToOrder"> <argument name="product" value="$$createProduct$$"/> </actionGroup> @@ -61,20 +61,20 @@ <fillField selector="{{AdminOrderFormAccountSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillCustomerEmail"/> <!--Fill customer address information--> - <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerAddress"> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerAddress"> <argument name="customer" value="Simple_US_Customer"/> <argument name="address" value="US_Address_TX"/> </actionGroup> <!-- Select Free shipping --> - <actionGroup ref="orderSelectFreeShipping" stepKey="selectFreeShippingOption"/> + <actionGroup ref="OrderSelectFreeShippingActionGroup" stepKey="selectFreeShippingOption"/> <!--Click *Submit Order* button--> <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="clickSubmitOrder"/> <!--Click *Invoice* button--> - <actionGroup ref="StartCreateInvoiceFromOrderPage" stepKey="startCreateInvoice"/> - <actionGroup ref="SubmitInvoice" stepKey="submitInvoice"/> + <actionGroup ref="StartCreateInvoiceFromOrderPageActionGroup" stepKey="startCreateInvoice"/> + <actionGroup ref="SubmitInvoiceActionGroup" stepKey="submitInvoice"/> <!--Verify that *Credit Memo* button is displayed--> <seeElement selector="{{AdminOrderFormItemsSection.creditMemo}}" stepKey="seeCreditMemo"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithBankTransferPaymentMethodTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithBankTransferPaymentMethodTest.xml index 74cf3e9dd6b7a..726b4a99cdec9 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithBankTransferPaymentMethodTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithBankTransferPaymentMethodTest.xml @@ -44,12 +44,12 @@ </after> <!--Create new customer order--> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> <argument name="customer" value="$$simpleCustomer$$"/> </actionGroup> <!--Add Simple product to order--> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToTheOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToTheOrder"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> @@ -64,11 +64,11 @@ <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitOrder"/> <!-- Verify order information --> - <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> + <actionGroup ref="VerifyCreatedOrderInformationActionGroup" stepKey="verifyCreatedOrderInformation"/> <grabTextFrom selector="|Order # (\d+)|" stepKey="orderId"/> <!-- Cancel the Order --> - <actionGroup ref="cancelPendingOrder" stepKey="cancelPendingOrder"/> + <actionGroup ref="CancelPendingOrderActionGroup" stepKey="cancelPendingOrder"/> <!--Log in to Storefront as Customer --> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCashOnDeliveryPaymentMethodTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCashOnDeliveryPaymentMethodTest.xml index a412adee939d2..eb46621a458ab 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCashOnDeliveryPaymentMethodTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCashOnDeliveryPaymentMethodTest.xml @@ -44,12 +44,12 @@ </after> <!-- Create new customer order --> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> <argument name="customer" value="$$simpleCustomer$$"/> </actionGroup> <!-- Add Simple product to order --> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToTheOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToTheOrder"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> @@ -64,11 +64,11 @@ <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitOrder"/> <!--Verify order information--> - <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> + <actionGroup ref="VerifyCreatedOrderInformationActionGroup" stepKey="verifyCreatedOrderInformation"/> <grabTextFrom selector="|Order # (\d+)|" stepKey="orderId"/> <!-- Cancel the Order --> - <actionGroup ref="cancelPendingOrder" stepKey="cancelPendingOrder"/> + <actionGroup ref="CancelPendingOrderActionGroup" stepKey="cancelPendingOrder"/> <!--Log in to Storefront as Customer --> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCheckMoneyOrderPaymentMethodTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCheckMoneyOrderPaymentMethodTest.xml index 116b8e7d6ca71..b303364bbf324 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCheckMoneyOrderPaymentMethodTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCheckMoneyOrderPaymentMethodTest.xml @@ -111,30 +111,30 @@ </after> <!-- Create new customer order --> <comment userInput="Create new customer order" stepKey="createNewCustomerOrderComment"/> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> <argument name="customer" value="$$simpleCustomer$$"/> </actionGroup> <!-- Add bundle product to order and check product price in grid --> <comment userInput="Add bundle product to order and check product price in grid" stepKey="addBundleProductToOrderComment"/> - <actionGroup ref="addBundleProductToOrder" stepKey="addBundleProductToOrder"> + <actionGroup ref="AddBundleProductToOrderActionGroup" stepKey="addBundleProductToOrder"> <argument name="product" value="$$createBundleProduct$$"/> <argument name="quantity" value="1"/> </actionGroup> <!-- Add configurable product to order --> <comment userInput="Add configurable product to order" stepKey="addConfigurableProductToOrderComment"/> - <actionGroup ref="newAddConfigurableProductToOrder" stepKey="addConfigurableProductToOrder"> + <actionGroup ref="NewAddConfigurableProductToOrderActionGroup" stepKey="addConfigurableProductToOrder"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="attribute" value="$$createConfigProductAttribute$$"/> <argument name="option" value="$$getConfigAttributeOption1$$"/> </actionGroup> <!-- Add Simple product to order --> <comment userInput="Add Simple product to order" stepKey="addSimpleProductToOrderComment"/> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToTheOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToTheOrder"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> <!-- Add Virtual product to order --> <comment userInput="Add Virtual product to order" stepKey="addVirtualProductToOrderComment"/> - <actionGroup ref="addSimpleProductToOrder" stepKey="addVirtualProductToTheOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addVirtualProductToTheOrder"> <argument name="product" value="$$virtualProduct$$"/> </actionGroup> <!-- Select FlatRate shipping method --> @@ -145,14 +145,14 @@ <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitOrder"/> <!-- Verify order information --> <comment userInput="Verify order information" stepKey="verifyOrderInformationComment"/> - <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> + <actionGroup ref="VerifyCreatedOrderInformationActionGroup" stepKey="verifyCreatedOrderInformation"/> <grabTextFrom selector="|Order # (\d+)|" stepKey="orderId"/> <!-- Cancel the Order --> <comment userInput="Cancel the Order" stepKey="cancelTheOrder"/> - <actionGroup ref="cancelPendingOrder" stepKey="cancelPendingOrder"/> + <actionGroup ref="CancelPendingOrderActionGroup" stepKey="cancelPendingOrder"/> <!-- Assert Simple Product Quantity in backend after order Canceled --> <comment userInput="Assert Simple Product Quantity in backend after order Canceled" stepKey="assertSimpleProductQuantityAfterOrderCanceledComment"/> - <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheProduct"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="filterAndSelectTheProduct"> <argument name="productSku" value="$$simpleProduct.sku$$"/> </actionGroup> <waitForElementVisible selector="{{AdminProductFormSection.productName}}" stepKey="waitForProductDetailsToLoad"/> @@ -167,7 +167,7 @@ </actionGroup> <!-- Assert Virtual Product Quantity in backend after order canceled --> <comment userInput="Assert Virtual Product Quantity in backend after order canceled" stepKey="assertVirtualProductQuantityAfterOrderCanceledComment"/> - <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheVirtualProduct"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="filterAndSelectTheVirtualProduct"> <argument name="productSku" value="$$virtualProduct.sku$$"/> </actionGroup> <waitForElementVisible selector="{{AdminProductFormSection.productName}}" stepKey="waitForProductDetailsToLoad1"/> @@ -182,7 +182,7 @@ </actionGroup> <!-- Assert Bundle Product Quantity in backend after order canceled --> <comment userInput="Assert Bundle Product Quantity in backend after order canceled" stepKey="assertBundleProductQuantityAfterOrderCanceledComment"/> - <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheBundleProduct"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="filterAndSelectTheBundleProduct"> <argument name="productSku" value="$$simpleProduct1.sku$$"/> </actionGroup> <waitForElementVisible selector="{{AdminProductFormSection.productName}}" stepKey="waitForProductDetailsToLoad2"/> @@ -197,7 +197,7 @@ </actionGroup> <!-- Assert Configurable Product Quantity in backend after order canceled --> <comment userInput="Assert Configurable Product quantity in backend after order canceled" stepKey="assertConfigurableProductQuantityAfterOrderCanceledComment"/> - <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheConfigProduct"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="filterAndSelectTheConfigProduct"> <argument name="productSku" value="$$createConfigChildProduct1.sku$$"/> </actionGroup> <waitForElementVisible selector="{{AdminProductFormSection.productName}}" stepKey="waitForProductDetailsToLoad3"/> @@ -216,7 +216,7 @@ <waitForPageLoad stepKey="waitForPageLoad5"/> <!-- Filter order using orderId --> <comment userInput="Filter order using orderId" stepKey="filterOrderUsingOrderIdComment"/> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$orderId"/> </actionGroup> <see selector="{{AdminOrdersGridSection.firstRow}}" userInput="$orderId" stepKey="seeOrderIdInGrid"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithProductQtyWithoutStockDecreaseTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithProductQtyWithoutStockDecreaseTest.xml index 4300f22c3fb3a..a8076bfb9f681 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithProductQtyWithoutStockDecreaseTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithProductQtyWithoutStockDecreaseTest.xml @@ -47,12 +47,12 @@ </after> <!--Create new customer order--> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> <argument name="customer" value="$$simpleCustomer$$"/> </actionGroup> <!--Add Simple product to order--> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToTheOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToTheOrder"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> @@ -63,11 +63,11 @@ <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitOrder"/> <!--Verify order information--> - <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> + <actionGroup ref="VerifyCreatedOrderInformationActionGroup" stepKey="verifyCreatedOrderInformation"/> <grabTextFrom selector="|Order # (\d+)|" stepKey="orderId"/> <!-- Assert Simple Product Quantity in backend after order --> - <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheProduct"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="filterAndSelectTheProduct"> <argument name="productSku" value="$$simpleProduct.sku$$"/> </actionGroup> <waitForElementVisible selector="{{AdminProductFormSection.productName}}" stepKey="waitForProductDetailsToLoad"/> @@ -80,16 +80,16 @@ <waitForPageLoad stepKey="waitForPageLoad5"/> <!-- Filter Order using orderId --> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$orderId"/> </actionGroup> <click selector="{{AdminOrdersGridSection.rowViewAction('1')}}" stepKey="clickOnViewAction"/> <!-- Cancel the Order --> - <actionGroup ref="cancelPendingOrder" stepKey="cancelPendingOrder"/> + <actionGroup ref="CancelPendingOrderActionGroup" stepKey="cancelPendingOrder"/> <!-- Assert Simple Product Quantity in backend after Cancelling the order --> - <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheProduct1"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="filterAndSelectTheProduct1"> <argument name="productSku" value="$$simpleProduct.sku$$"/> </actionGroup> <waitForElementVisible selector="{{AdminProductFormSection.productName}}" stepKey="waitForProductDetailsToLoad1"/> @@ -102,7 +102,7 @@ <waitForPageLoad stepKey="waitForPageLoad6"/> <!-- Filter Order using orderId --> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById1"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById1"> <argument name="orderId" value="$orderId"/> </actionGroup> <click selector="{{AdminOrdersGridSection.rowViewAction('1')}}" stepKey="clickOnViewAction1"/> @@ -114,7 +114,7 @@ <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitOrder1"/> <!-- Assert Simple Product Quantity in backend after Reorder --> - <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheProduct2"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="filterAndSelectTheProduct2"> <argument name="productSku" value="$$simpleProduct.sku$$"/> </actionGroup> <waitForElementVisible selector="{{AdminProductFormSection.productName}}" stepKey="waitForProductDetailsToLoad2"/> @@ -122,4 +122,4 @@ <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="10" stepKey="seeProductQuantityAfterReorder"/> <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="In Stock" stepKey="seeProductStockStatusAfterReorder"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithPurchaseOrderPaymentMethodTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithPurchaseOrderPaymentMethodTest.xml index d14987dd2e87b..c85a3fffc2c69 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithPurchaseOrderPaymentMethodTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithPurchaseOrderPaymentMethodTest.xml @@ -46,12 +46,12 @@ </after> <!--Create new customer order--> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> <argument name="customer" value="$$simpleCustomer$$"/> </actionGroup> <!--Add Simple product to order--> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToTheOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToTheOrder"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> @@ -70,11 +70,11 @@ <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitOrder"/> <!--Verify order information--> - <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> + <actionGroup ref="VerifyCreatedOrderInformationActionGroup" stepKey="verifyCreatedOrderInformation"/> <grabTextFrom selector="|Order # (\d+)|" stepKey="orderId"/> <!-- Cancel the Order --> - <actionGroup ref="cancelPendingOrder" stepKey="cancelPendingOrder"/> + <actionGroup ref="CancelPendingOrderActionGroup" stepKey="cancelPendingOrder"/> <!--Log in to Storefront as Customer --> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> @@ -86,4 +86,4 @@ <waitForPageLoad stepKey="waitForOrderDetailsToLoad"/> <seeElement selector="{{StorefrontCustomerOrderViewSection.orderStatusInGrid('$orderId', 'Canceled')}}" stepKey="seeOrderStatusInGrid"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml index f8c5b46dc6ff9..a8014466c5773 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml @@ -49,12 +49,12 @@ </after> <!--Create new customer order--> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> <argument name="customer" value="$$simpleCustomer$$"/> </actionGroup> <!--Add Simple product to order--> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToTheOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToTheOrder"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> @@ -65,11 +65,11 @@ <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitOrder"/> <!--Verify order information--> - <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> + <actionGroup ref="VerifyCreatedOrderInformationActionGroup" stepKey="verifyCreatedOrderInformation"/> <grabTextFrom selector="|Order # (\d+)|" stepKey="orderId"/> <!-- Cancel the Order --> - <actionGroup ref="cancelPendingOrder" stepKey="cancelPendingOrder"/> + <actionGroup ref="CancelPendingOrderActionGroup" stepKey="cancelPendingOrder"/> <!--Log in to Storefront as Customer --> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> @@ -81,4 +81,4 @@ <waitForPageLoad stepKey="waitForOrderDetailsToLoad"/> <seeElement selector="{{StorefrontCustomerOrderViewSection.orderStatusInGrid('$orderId', 'Canceled')}}" stepKey="seeOrderStatusInGrid"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrder.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrder.xml index 315a097eb2323..e19002d5ecb4c 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrder.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminChangeCustomerGroupInNewOrder.xml @@ -25,8 +25,8 @@ <after> <actionGroup ref="logout" stepKey="logout"/> </after> - - <actionGroup ref="navigateToNewOrderPageNewCustomer" stepKey="openNewOrder"/> + + <actionGroup ref="NavigateToNewOrderPageNewCustomerActionGroup" stepKey="openNewOrder"/> <selectOption selector="{{AdminOrderFormAccountSection.group}}" userInput="Retailer" stepKey="selectCustomerGroup"/> <waitForPageLoad stepKey="waitForPageLoad"/> <grabValueFrom selector="{{AdminOrderFormAccountSection.group}}" stepKey="grabGroupValue"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoUpdateTotalsTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoUpdateTotalsTest.xml index 45ea09a06ed26..251d29df43dd1 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoUpdateTotalsTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingCreditMemoUpdateTotalsTest.xml @@ -18,9 +18,6 @@ <testCaseId value="MC-18159"/> <useCaseId value="MC-17003"/> <group value="sales"/> - <skip> - <issueId value="MC-17003"/> - </skip> </annotations> <before> <!--Create product--> @@ -44,9 +41,9 @@ </actionGroup> <grabFromCurrentUrl regex="~/order_id/(\d+)/~" stepKey="grabOrderId"/> <!--Create invoice--> - <actionGroup ref="StartCreateInvoiceFromOrderPage" stepKey="startCreateInvoice"/> + <actionGroup ref="StartCreateInvoiceFromOrderPageActionGroup" stepKey="startCreateInvoice"/> <!--Submit invoice--> - <actionGroup ref="SubmitInvoice" stepKey="submitInvoice"/> + <actionGroup ref="SubmitInvoiceActionGroup" stepKey="submitInvoice"/> <!--Create Credit Memo--> <actionGroup ref="StartToCreateCreditMemoActionGroup" stepKey="startToCreateCreditMemo"> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingDateAfterChangeInterfaceLocaleTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingDateAfterChangeInterfaceLocaleTest.xml index add8f7d2ae2c3..ad3b6cf45d5bb 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingDateAfterChangeInterfaceLocaleTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCheckingDateAfterChangeInterfaceLocaleTest.xml @@ -51,7 +51,7 @@ <grabTextFrom selector="{{AdminOrderDetailsInformationSection.orderId}}" stepKey="grabOrderId"/> <!--Filter orders grid by ID on Admin page--> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="{$grabOrderId}"/> </actionGroup> @@ -73,7 +73,7 @@ </actionGroup> <!--Filter orders grid by ID on Admin page after changing "Interface Locale"--> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridByIdAfterSetFrenchLocale"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridByIdAfterSetFrenchLocale"> <argument name="orderId" value="{$grabOrderId}"/> </actionGroup> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml index 4310d412d1c98..622718d721577 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCorrectnessInvoicedItemInBundleProductTest.xml @@ -53,7 +53,7 @@ <!--Complete Bundle product creation--> <amOnPage url="{{AdminProductEditPage.url($$createBundleProduct.id$$)}}" stepKey="goToProductEditPage"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!--Run re-index task--> <magentoCLI command="indexer:reindex" stepKey="reindex"/> @@ -76,16 +76,16 @@ <!--Go to order page submit invoice--> <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> <waitForPageLoad stepKey="waitForCreatedOrderPageOpened"/> - <actionGroup ref="goToInvoiceIntoOrder" stepKey="goToInvoiceIntoOrderPage"/> + <actionGroup ref="GoToInvoiceIntoOrderActionGroup" stepKey="goToInvoiceIntoOrderPage"/> <fillField selector="{{AdminInvoiceItemsSection.qtyToInvoiceColumn}}" userInput="5" stepKey="ChangeQtyToInvoice"/> <click selector="{{AdminInvoiceItemsSection.updateQty}}" stepKey="updateQunatity"/> <waitForPageLoad stepKey="waitPageToBeLoaded"/> - <actionGroup ref="SubmitInvoice" stepKey="submitInvoice"/> + <actionGroup ref="SubmitInvoiceActionGroup" stepKey="submitInvoice"/> <!--Verify invoiced items qty in ship tab--> <actionGroup ref="goToShipmentIntoOrder" stepKey="goToShipment"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml index f79ab822d964a..4b582a09deacf 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml @@ -42,17 +42,17 @@ <actionGroup ref="logout" stepKey="logout"/> </after> <!-- Create Order --> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderPage"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderPage"> <argument name="customer" value="$createCustomer$"/> </actionGroup> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSecondProduct"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSecondProduct"> <argument name="product" value="$createProduct$"/> </actionGroup> - <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerInfo"> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerInfo"> <argument name="customer" value="$createCustomer$"/> <argument name="address" value="US_Address_TX"/> </actionGroup> - <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRate"/> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRate"/> <conditionalClick selector="{{AdminOrderFormPaymentSection.linkPaymentOptions}}" dependentSelector="{{AdminOrderFormPaymentSection.linkPaymentOptions}}" visible="true" stepKey="openMoneyOption"/> <waitForElementVisible selector="{{AdminOrderFormPaymentSection.paymentBlock}}" stepKey="waitForPaymentOptions"/> <checkOption selector="{{AdminOrderFormPaymentSection.checkBankTransfer}}" stepKey="checkBankTransfer"/> @@ -62,15 +62,15 @@ <see stepKey="seeSuccessMessageForOrder" userInput="You created the order."/> <!-- Create Invoice --> - <actionGroup ref="StartCreateInvoiceFromOrderPage" stepKey="startInvoice"/> + <actionGroup ref="StartCreateInvoiceFromOrderPageActionGroup" stepKey="startInvoice"/> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - <waitForElementVisible selector="{{AdminMessagesSection.successMessage}}" stepKey="waitForMessageAppears"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="seeInvoiceCreateSuccess"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForMessageAppears"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The invoice has been created." stepKey="seeInvoiceCreateSuccess"/> <!-- Go to Sales > Orders > find out placed order and open --> <grabTextFrom selector="|Order # (\d+)|" stepKey="grabOrderId" /> <assertNotEmpty actual="$grabOrderId" stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"/> - <actionGroup ref="OpenOrderById" stepKey="openOrder"> + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openOrder"> <argument name="orderId" value="{$grabOrderId}"/> </actionGroup> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoConfigurableProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoConfigurableProductTest.xml index 0522960a032fa..2a841b04bd647 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoConfigurableProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoConfigurableProductTest.xml @@ -100,36 +100,36 @@ <actionGroup ref="logout" stepKey="logout"/> </after> <!-- Create Order --> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderPage"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderPage"> <argument name="customer" value="$createCustomer$"/> </actionGroup> <!--Add configurable product to order--> - <actionGroup ref="addConfigurableProductToOrderFromAdmin" stepKey="addConfigurableProductToOrder"> + <actionGroup ref="AddConfigurableProductToOrderFromAdminActionGroup" stepKey="addConfigurableProductToOrder"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="attribute" value="$$createConfigProductAttribute$$"/> <argument name="option" value="$$getConfigAttributeOption1$$"/> </actionGroup> - <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerInfo"> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerInfo"> <argument name="customer" value="$createCustomer$"/> <argument name="address" value="US_Address_TX"/> </actionGroup> - <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRate"/> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRate"/> <click selector="{{OrdersGridSection.submitOrder}}" stepKey="submitOrder"/> <waitForPageLoad stepKey="waitForSubmitOrderPage"/> <see stepKey="seeSuccessMessageForOrder" userInput="You created the order."/> <!-- Create Invoice --> - <actionGroup ref="StartCreateInvoiceFromOrderPage" stepKey="startInvoice"/> + <actionGroup ref="StartCreateInvoiceFromOrderPageActionGroup" stepKey="startInvoice"/> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - <waitForElementVisible selector="{{AdminMessagesSection.successMessage}}" stepKey="waitForMessageAppears"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="seeInvoiceCreateSuccess"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForMessageAppears"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The invoice has been created." stepKey="seeInvoiceCreateSuccess"/> <!-- Go to Sales > Orders > find out placed order and open --> <grabTextFrom selector="|Order # (\d+)|" stepKey="grabOrderId" /> <assertNotEmpty actual="$grabOrderId" stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"/> - <actionGroup ref="OpenOrderById" stepKey="openOrder"> + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openOrder"> <argument name="orderId" value="{$grabOrderId}"/> </actionGroup> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml index e9b37521259a0..2b6008c93084e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml @@ -40,32 +40,32 @@ <actionGroup ref="logout" stepKey="logout"/> </after> <!-- Create Order --> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderPage"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderPage"> <argument name="customer" value="$createCustomer$"/> </actionGroup> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSecondProduct"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSecondProduct"> <argument name="product" value="$createProduct$"/> <argument name="productQty" value="2"/> </actionGroup> - <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerInfo"> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerInfo"> <argument name="customer" value="$createCustomer$"/> <argument name="address" value="US_Address_TX"/> </actionGroup> - <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRate"/> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRate"/> <click selector="{{OrdersGridSection.submitOrder}}" stepKey="submitOrder"/> <waitForPageLoad stepKey="waitForSubmitOrderPage"/> <see stepKey="seeSuccessMessageForOrder" userInput="You created the order."/> <!-- Create Invoice --> - <actionGroup ref="StartCreateInvoiceFromOrderPage" stepKey="startInvoice"/> + <actionGroup ref="StartCreateInvoiceFromOrderPageActionGroup" stepKey="startInvoice"/> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - <waitForElementVisible selector="{{AdminMessagesSection.successMessage}}" stepKey="waitForMessageAppears"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="seeInvoiceCreateSuccess"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForMessageAppears"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The invoice has been created." stepKey="seeInvoiceCreateSuccess"/> <!-- Go to Sales > Orders > find out placed order and open --> <grabTextFrom selector="|Order # (\d+)|" stepKey="grabOrderId" /> <assertNotEmpty actual="$grabOrderId" stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"/> - <actionGroup ref="OpenOrderById" stepKey="openOrder"> + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openOrder"> <argument name="orderId" value="{$grabOrderId}"/> </actionGroup> @@ -80,6 +80,7 @@ <waitForPageLoad stepKey="waitForResultPage"/> <!-- Perform all assertions: assert refund success create message --> + <waitForElementVisible selector="{{AdminIndexManagementSection.successMessage}}" stepKey="waitForSuccessMessage"/> <see selector="{{AdminIndexManagementSection.successMessage}}" userInput="You created the credit memo." stepKey="assertRefundSuccessCreateMessage"/> <!-- Assert Credit Memo button --> @@ -102,7 +103,7 @@ <see userInput="1" selector="{{AdminCreditMemoViewItemsSection.productQty}}" stepKey="seeQty"/> <!-- Go to order page --> - <actionGroup ref="OpenOrderById" stepKey="openOrderPage"> + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openOrderPage"> <argument name="orderId" value="{$grabOrderId}"/> </actionGroup> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml index 791792d0879a7..452c54d188c57 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml @@ -43,17 +43,17 @@ </after> <!-- Create Order --> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderPage"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderPage"> <argument name="customer" value="$createCustomer$"/> </actionGroup> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSecondProduct"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSecondProduct"> <argument name="product" value="$createProduct$"/> </actionGroup> - <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerInfo"> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerInfo"> <argument name="customer" value="$createCustomer$"/> <argument name="address" value="US_Address_TX"/> </actionGroup> - <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRate"/> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRate"/> <conditionalClick selector="{{AdminOrderFormPaymentSection.linkPaymentOptions}}" dependentSelector="{{AdminOrderFormPaymentSection.linkPaymentOptions}}" visible="true" stepKey="openMoneyOption"/> <waitForElementVisible selector="{{AdminOrderFormPaymentSection.paymentBlock}}" stepKey="waitForPaymentOptions"/> <checkOption selector="{{AdminOrderFormPaymentSection.checkCashOnDelivery}}" stepKey="checkCashOnDelivery"/> @@ -63,15 +63,15 @@ <see stepKey="seeSuccessMessageForOrder" userInput="You created the order."/> <!-- Create Invoice --> - <actionGroup ref="StartCreateInvoiceFromOrderPage" stepKey="startInvoice"/> + <actionGroup ref="StartCreateInvoiceFromOrderPageActionGroup" stepKey="startInvoice"/> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - <waitForElementVisible selector="{{AdminMessagesSection.successMessage}}" stepKey="waitForMessageAppears"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="seeInvoiceCreateSuccess"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForMessageAppears"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The invoice has been created." stepKey="seeInvoiceCreateSuccess"/> <!-- Go to Sales > Orders > find out placed order and open --> <grabTextFrom selector="|Order # (\d+)|" stepKey="grabOrderId" /> <assertNotEmpty actual="$grabOrderId" stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"/> - <actionGroup ref="OpenOrderById" stepKey="openOrder"> + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openOrder"> <argument name="orderId" value="{$grabOrderId}"/> </actionGroup> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml index 0a8e78d743c1d..231ff78d7d8fb 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml @@ -43,17 +43,17 @@ </after> <!-- Create Order --> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderPage"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderPage"> <argument name="customer" value="$createCustomer$"/> </actionGroup> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSecondProduct"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSecondProduct"> <argument name="product" value="$createProduct$"/> </actionGroup> - <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerInfo"> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerInfo"> <argument name="customer" value="$createCustomer$"/> <argument name="address" value="US_Address_TX"/> </actionGroup> - <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRate"/> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRate"/> <conditionalClick selector="{{AdminOrderFormPaymentSection.linkPaymentOptions}}" dependentSelector="{{AdminOrderFormPaymentSection.linkPaymentOptions}}" visible="true" stepKey="openMoneyOption"/> <waitForElementVisible selector="{{AdminOrderFormPaymentSection.paymentBlock}}" stepKey="waitForPaymentOptions"/> <checkOption selector="{{AdminOrderFormPaymentSection.checkPurchaseOrder}}" stepKey="checkPurchaseOrder"/> @@ -66,15 +66,15 @@ <see stepKey="seeSuccessMessageForOrder" selector="{{AdminIndexManagementSection.successMessage}}" userInput="You created the order."/> <!-- Create Invoice --> - <actionGroup ref="StartCreateInvoiceFromOrderPage" stepKey="startInvoice"/> + <actionGroup ref="StartCreateInvoiceFromOrderPageActionGroup" stepKey="startInvoice"/> <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - <waitForElementVisible selector="{{AdminMessagesSection.successMessage}}" stepKey="waitForMessageAppears"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="seeInvoiceCreateSuccess"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForMessageAppears"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The invoice has been created." stepKey="seeInvoiceCreateSuccess"/> <!-- Go to Sales > Orders > find out placed order and open --> <grabTextFrom selector="|Order # (\d+)|" stepKey="grabOrderId" /> <assertNotEmpty actual="$grabOrderId" stepKey="assertOrderIdIsNotEmpty" after="grabOrderId"/> - <actionGroup ref="OpenOrderById" stepKey="openOrder"> + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openOrder"> <argument name="orderId" value="{$grabOrderId}"/> </actionGroup> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml index a90fe5c49f032..73c126bb7794f 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml @@ -63,7 +63,7 @@ <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask3"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilter"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/> <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNum"/> <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearch"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask4"/> @@ -84,7 +84,7 @@ <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Processing" stepKey="seeOrderStatus"/> <amOnPage url="{{AdminInvoicesPage.url}}" stepKey="goToInvoices"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask6" /> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetGridInitial"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridInitial"/> <click selector="{{AdminInvoicesGridSection.filter}}" stepKey="clickFilters"/> <fillField selector="{{AdminInvoicesFiltersSection.orderNum}}" userInput="{$grabOrderNumber}" stepKey="searchOrderNum2"/> <click selector="{{AdminInvoicesFiltersSection.applyFilters}}" stepKey="clickApplyFilters"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAddProductCheckboxTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAddProductCheckboxTest.xml index bd13f7c847c34..8fe49558cf091 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAddProductCheckboxTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAddProductCheckboxTest.xml @@ -30,7 +30,7 @@ </before> <!-- Initiate create new order --> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> <argument name="customer" value="$$createSimpleCustomer$$"/> </actionGroup> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml new file mode 100644 index 0000000000000..338765d650d13 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderAndCheckTheReorderTest.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateOrderAndCheckTheReorderTest"> + <annotations> + <title value="'Reorder' button is not visible for customer if ordered item is out of stock"/> + <stories value="MAGETWO-63924: 'Reorder' button is not visible for customer if ordered item is out of stock"/> + <description value="'Reorder' button is not visible for customer if ordered item is out of stock"/> + <features value="Sales"/> + <testCaseId value="MC-22109"/> + <severity value="MAJOR"/> + <group value="Sales"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="FlatRateShippingMethodDefault" stepKey="setDefaultFlatRateShippingMethod"/> + <createData entity="CashOnDeliveryPaymentMethodDefault" stepKey="cashOnDeliveryPaymentMethod"/> + <createData entity="Simple_US_Customer_CA" stepKey="simpleCustomer"/> + <createData entity="SimpleProduct_25" stepKey="simpleProduct"> + <field key="price">5</field> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> + <argument name="customer" value="$$simpleCustomer$$"/> + </actionGroup> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToOrder"> + <argument name="product" value="$$simpleProduct$$"/> + <argument name="productQty" value="{{SimpleProduct_25.quantity}}"/> + </actionGroup> + <actionGroup ref="SelectCashOnDeliveryPaymentMethodActionGroup" stepKey="selectPaymentMethod"/> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="orderSelectFlatRateShippingMethod"/> + <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> + <actionGroup ref="VerifyCreatedOrderInformationActionGroup" stepKey="verifyCreatedOrderInformation"/> + <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId"/> + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openOrder"> + <argument name="orderId" value="$getOrderId"/> + </actionGroup> + <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShipAction"/> + <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="clickSubmitShipment"/> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="frontendCustomerLogIn"> + <argument name="Customer" value="$$simpleCustomer$$"/> + </actionGroup> + <actionGroup ref="StorefrontNavigateToCustomerOrdersHistoryPageActionGroup" stepKey="goToOrderHistoryPage"/> + <actionGroup ref="StorefrontCustomerReorderButtonNotVisibleActionGroup" stepKey="checkReorderButton"/> + <after> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> + <actionGroup ref="logout" stepKey="adminLogout"/> + <magentoCLI command="config:set payment/cashondelivery/active 0" stepKey="disableCashOnDeliveryMethod"/> + <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderForCustomerWithTwoAddressesTaxableAndNonTaxableTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderForCustomerWithTwoAddressesTaxableAndNonTaxableTest.xml index d6bf0eec301db..1c35a9e900a8a 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderForCustomerWithTwoAddressesTaxableAndNonTaxableTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderForCustomerWithTwoAddressesTaxableAndNonTaxableTest.xml @@ -36,23 +36,23 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <!--Step 1: Create new order for customer--> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> <argument name="customer" value="$$simpleCustomer$$"/> </actionGroup> <!--Step 2: Add product1 to the order--> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToOrder"> <argument name="product" value="$$product1$$"/> </actionGroup> <!--Step 2: Select taxable address as billing address--> <selectOption selector="{{AdminOrderFormBillingAddressSection.selectAddress}}" userInput="{{US_Address_CA.state}}" stepKey="selectTaxableAddress" /> <!--Step 3: Select FlatRate shipping method--> - <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRateShippingMethod"/> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRateShippingMethod"/> <!--Step 4: Verify that tax is applied to the order--> <seeElement selector="{{AdminOrderFormTotalSection.total('Tax')}}" stepKey="seeTax" /> <!--Step 5: Select non taxable address as billing address--> <selectOption selector="{{AdminOrderFormBillingAddressSection.selectAddress}}" userInput="{{US_Address_TX.state}}" stepKey="selectNonTaxableAddress" /> <!--Step 6: Change shipping method to Free--> - <actionGroup ref="changeShippingMethod" stepKey="changeShippingMethod"> + <actionGroup ref="ChangeShippingMethodActionGroup" stepKey="changeShippingMethod"> <argument name="shippingMethod" value="freeshipping_freeshipping"/> </actionGroup> <!--Step 7: Verify that tax is not applied to the order--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithBundleProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithBundleProductTest.xml index d087b291de87c..1749fcd6c218e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithBundleProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithBundleProductTest.xml @@ -80,25 +80,25 @@ </before> <!--Create new customer order--> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> <argument name="customer" value="$$simpleCustomer$$"/> </actionGroup> <!--Add bundle product to order and check product price in grid--> - <actionGroup ref="addBundleProductToOrderAndCheckPriceInGrid" stepKey="addBundleProductToOrder"> + <actionGroup ref="AddBundleProductToOrderAndCheckPriceInGridActionGroup" stepKey="addBundleProductToOrder"> <argument name="product" value="$$product$$"/> <argument name="quantity" value="1"/> <argument name="price" value="$738.00"/> </actionGroup> <!--Select FlatRate shipping method--> - <actionGroup ref="orderSelectFlatRateShipping" stepKey="orderSelectFlatRateShippingMethod"/> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="orderSelectFlatRateShippingMethod"/> <!--Submit order--> <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitOrder"/> <!--Verify order information--> - <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> + <actionGroup ref="VerifyCreatedOrderInformationActionGroup" stepKey="verifyCreatedOrderInformation"/> <after> <actionGroup ref="logout" stepKey="logout"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithDateTimeOptionUITest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithDateTimeOptionUITest.xml new file mode 100644 index 0000000000000..7e58e55c8981e --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithDateTimeOptionUITest.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateOrderWithDateTimeOptionUITest"> + <annotations> + <title value="Admin create order with date time option UI test"/> + <description value="Check asterisk rendered correctly for Product with custom option (datetime) at backend"/> + <features value="Sales"/> + <severity value="MINOR"/> + <group value="Sales"/> + </annotations> + + <before> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <updateData createDataKey="createProduct" entity="productWithDateTimeOption" stepKey="updateProductWithOption"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createCustomer" stepKey="deleteSimpleCustomer"/> + </after> + + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + + <click selector="{{AdminOrderFormItemsSection.addProducts}}" stepKey="clickAddProducts"/> + <fillField selector="{{AdminOrderFormItemsSection.skuFilter}}" userInput="$$createProduct.sku$$" stepKey="fillSkuFilter"/> + <click selector="{{AdminOrderFormItemsSection.search}}" stepKey="clickSearch"/> + <scrollTo selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" x="0" y="-100" stepKey="scrollToCheckColumn"/> + <checkOption selector="{{AdminOrderFormItemsSection.rowCheck('1')}}" stepKey="selectProduct"/> + <waitForAjaxLoad stepKey="waitForAjaxLoad"/> + <executeJS function="{{AdminProductCustomizableOptionsSection.requiredFieldIndicator}}" stepKey="dateTimeRequiredFieldIndicator"/> + <assertEquals expected='"*"' expectedType="string" actualType="variable" actual="dateTimeRequiredFieldIndicator" message="pass" stepKey="assertRequiredFieldIndicator"/> + </test> +</tests> + diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml index 40b26a6b46045..bfaf31007b10a 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml @@ -37,10 +37,10 @@ <!--Admin creates order--> <comment userInput="Admin creates order" stepKey="adminCreateOrder"/> - <actionGroup ref="navigateToNewOrderPageNewCustomerSingleStore" stepKey="navigateToNewOrderPage"/> + <actionGroup ref="NavigateToNewOrderPageNewCustomerSingleStoreActionGroup" stepKey="navigateToNewOrderPage"/> <conditionalClick selector="{{AdminOrderFormStoreSelectorSection.defaultStoreViewButton}}" dependentSelector="{{AdminOrderFormStoreSelectorSection.storeSelectorContainer}}" visible="true" stepKey="selectFirstStoreViewIfAppears"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskDisappearedAfterStoreSelected"/> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToOrder"> <argument name="product" value="SimpleProduct"/> </actionGroup> @@ -49,15 +49,15 @@ <fillField selector="{{AdminOrderFormAccountSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillEmail"/> <!--Fill customer address information--> - <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerAddress"> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerAddress"> <argument name="customer" value="Simple_US_Customer"/> <argument name="address" value="US_Address_TX"/> </actionGroup> - <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRateShipping"/> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRateShipping"/> <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="SelectCheckMoneyPaymentMethod" stepKey="selectCheckMoneyPayment"/> + <actionGroup ref="SelectCheckMoneyPaymentMethodActionGroup" stepKey="selectCheckMoneyPayment"/> <!--Verify totals on Order page--> <see selector="{{AdminOrderFormTotalSection.total('Subtotal')}}" userInput="${{AdminOrderSimpleProduct.subtotal}}" stepKey="seeOrderSubTotal"/> @@ -72,12 +72,12 @@ <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeOrderPendingStatus"/> <grabTextFrom selector="|Order # (\d+)|" stepKey="orderId"/> <assertNotEmpty actual="$orderId" stepKey="assertOrderIdIsNotEmpty"/> - <actionGroup ref="verifyBasicOrderInformation" stepKey="verifyOrderInformation"> + <actionGroup ref="VerifyBasicOrderInformationActionGroup" stepKey="verifyOrderInformation"> <argument name="customer" value="Simple_US_Customer"/> <argument name="shippingAddress" value="US_Address_TX"/> <argument name="billingAddress" value="US_Address_TX"/> </actionGroup> - <actionGroup ref="seeProductInItemsOrdered" stepKey="seeSimpleProductInItemsOrdered"> + <actionGroup ref="SeeProductInItemsOrderedActionGroup" stepKey="seeSimpleProductInItemsOrdered"> <argument name="product" value="SimpleProduct"/> </actionGroup> </test> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSelectedShoppingCartItemsTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSelectedShoppingCartItemsTest.xml index 513fcd7fd2699..58a680ea61c87 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSelectedShoppingCartItemsTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSelectedShoppingCartItemsTest.xml @@ -53,12 +53,12 @@ </actionGroup> <!--Step 3: Create new customer order--> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> <argument name="customer" value="$$simpleCustomer$$"/> </actionGroup> <!--Step 4: Add product2 to the order--> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToOrder"> <argument name="product" value="$$product2$$"/> </actionGroup> @@ -66,21 +66,21 @@ <click selector="{{AdminOrderFormShoppingCartSection.addProduct($$product1.name$$)}}" stepKey="selectProduct1InTheShoppingCart"/> <!--Step 6: Select FlatRate shipping method--> - <actionGroup ref="orderSelectFlatRateShipping" stepKey="orderSelectFlatRateShippingMethod"/> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="orderSelectFlatRateShippingMethod"/> <!--Step 7: Submit order--> <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> <!--Step 8: Verify order information--> - <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> + <actionGroup ref="VerifyCreatedOrderInformationActionGroup" stepKey="verifyCreatedOrderInformation"/> <!--Step 9: Check that product 2 is in the order items list --> - <actionGroup ref="seeProductInItemsOrdered" stepKey="seeProduct2InItemsOrdered"> + <actionGroup ref="SeeProductInItemsOrderedActionGroup" stepKey="seeProduct2InItemsOrdered"> <argument name="product" value="$$product2$$"/> </actionGroup> <!--Step 10: Check that product 1 is not in the order items list --> - <actionGroup ref="dontSeeProductInItemsOrdered" stepKey="dontSeeProduct1InItemsOrdered"> + <actionGroup ref="DontSeeProductInItemsOrderedActionGroup" stepKey="dontSeeProduct1InItemsOrdered"> <argument name="product" value="$$product1$$"/> </actionGroup> <after> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml new file mode 100644 index 0000000000000..76f916d55ee92 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductTest.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateOrderWithSimpleProductTest"> + <annotations> + <title value="Create Order in Admin with simple product"/> + <stories value="MAGETWO-12798: Create order with condition available product qty = ordered product qty"/> + <description value="Create order with simple product and assert if it gets out of stock after ordering it."/> + <features value="Sales"/> + <testCaseId value="MC-22110"/> + <severity value="MAJOR"/> + <group value="Sales"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="FlatRateShippingMethodDefault" stepKey="setDefaultFlatRateShippingMethod"/> + <createData entity="CashOnDeliveryPaymentMethodDefault" stepKey="cashOnDeliveryPaymentMethod"/> + <createData entity="Simple_US_Customer_CA" stepKey="simpleCustomer"/> + <createData entity="SimpleProduct_25" stepKey="simpleProduct"> + <field key="price">5</field> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> + <argument name="customer" value="$$simpleCustomer$$"/> + </actionGroup> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToOrder"> + <argument name="product" value="$$simpleProduct$$"/> + <argument name="productQty" value="{{SimpleProduct_25.quantity}}"/> + </actionGroup> + <actionGroup ref="SelectCashOnDeliveryPaymentMethodActionGroup" stepKey="selectPaymentMethod"/> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="orderSelectFlatRateShippingMethod"/> + <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> + <actionGroup ref="VerifyCreatedOrderInformationActionGroup" stepKey="verifyCreatedOrderInformation"/> + <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId"/> + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="openOrder"> + <argument name="orderId" value="$getOrderId"/> + </actionGroup> + <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShipAction"/> + <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="clickSubmitShipment"/> + <actionGroup ref="AssertAdminProductStockStatusActionGroup" stepKey="checkProductStockStatus"> + <argument name="productId" value="$$simpleProduct.id$$"/> + <argument name="stockStatus" value="Out of Stock"/> + </actionGroup> + <after> + <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI command="config:set payment/cashondelivery/active 0" stepKey="disableCashOnDeliveryMethod"/> + <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminFreeShippingNotAvailableIfMinimumOrderAmountNotMatchOrderTotalTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminFreeShippingNotAvailableIfMinimumOrderAmountNotMatchOrderTotalTest.xml index 5f6ea0937b52a..9ff6f3bdac985 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminFreeShippingNotAvailableIfMinimumOrderAmountNotMatchOrderTotalTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminFreeShippingNotAvailableIfMinimumOrderAmountNotMatchOrderTotalTest.xml @@ -41,11 +41,11 @@ <magentoCLI command="cache:flush" stepKey="flushCache2"/> </after> <!--Create new order with existing customer--> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="goToCreateOrderPage"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="goToCreateOrderPage"> <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <!--Add product to order--> - <actionGroup ref="addSimpleProductToOrder" stepKey="addProductToOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addProductToOrder"> <argument name="product" value="$$createProduct$$"/> </actionGroup> @@ -57,7 +57,7 @@ <!--Submit Order and verify that Order isn't placed--> <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="clickSubmitOrder"/> - <dontSeeElement selector="{{AdminMessagesSection.successMessage}}" stepKey="seeSuccessMessage"/> - <seeElement selector="{{AdminMessagesSection.errorMessage}}" stepKey="seeErrorMessage"/> + <dontSeeElement selector="{{AdminOrderFormMessagesSection.success}}" stepKey="seeSuccessMessage"/> + <seeElement selector="{{AdminOrderFormMessagesSection.error}}" stepKey="seeErrorMessage"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminHoldCreatedOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminHoldCreatedOrderTest.xml index 9e8949c9ba600..5dc5dbf3ab79d 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminHoldCreatedOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminHoldCreatedOrderTest.xml @@ -44,17 +44,17 @@ </after> <!--Create new customer order--> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> <argument name="customer" value="$$simpleCustomer$$"/> </actionGroup> <!--Add Simple product to order--> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToTheOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToTheOrder"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> <!--Add second product to order--> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSecondProductToTheOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSecondProductToTheOrder"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> @@ -65,7 +65,7 @@ <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitOrder"/> <!-- Verify order information --> - <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> + <actionGroup ref="VerifyCreatedOrderInformationActionGroup" stepKey="verifyCreatedOrderInformation"/> <grabTextFrom selector="|Order # (\d+)|" stepKey="orderId"/> <!-- Hold the Order --> @@ -93,4 +93,4 @@ <waitForPageLoad stepKey="waitForOrderDetailsToLoad"/> <seeElement selector="{{StorefrontCustomerOrderViewSection.orderStatusInGrid('$orderId', 'On Hold')}}" stepKey="seeOrderStatusInGrid"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminProductInTheShoppingCartCouldBeReachedByAdminDuringOrderCreationWithMultiWebsiteConfigTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminProductInTheShoppingCartCouldBeReachedByAdminDuringOrderCreationWithMultiWebsiteConfigTest.xml index b40e9d041a10e..d30074a240546 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminProductInTheShoppingCartCouldBeReachedByAdminDuringOrderCreationWithMultiWebsiteConfigTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminProductInTheShoppingCartCouldBeReachedByAdminDuringOrderCreationWithMultiWebsiteConfigTest.xml @@ -37,10 +37,10 @@ <argument name="StoreGroup" value="customStoreGroup"/> <argument name="customStore" value="customStore"/> </actionGroup> - <actionGroup ref="goToProductPageViaID" stepKey="goToProductEditPage"> + <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToProductEditPage"> <argument name="productId" value="$$createProduct.id$$"/> </actionGroup> - <actionGroup ref="ProductSetWebsite" stepKey="assignProductToSecondWebsite"> + <actionGroup ref="ProductSetWebsiteActionGroup" stepKey="assignProductToSecondWebsite"> <argument name="website" value="{{customWebsite.name}}"/> </actionGroup> </before> @@ -74,7 +74,7 @@ <actionGroup ref="StorefrontAddToTheCartActionGroup" stepKey="addProductToCart"/> <!--Create new order for existing Customer And Store--> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="createNewOrder"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="createNewOrder"> <argument name="customer" value="Simple_US_Customer"/> <argument name="storeView" value="customStore"/> </actionGroup> @@ -90,16 +90,16 @@ </actionGroup> <!--Fill customer address information--> - <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerAddress"> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerAddress"> <argument name="customer" value="Simple_US_Customer"/> <argument name="address" value="US_Address_TX"/> </actionGroup> <!--Select shipping method--> - <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRateShipping"/> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRateShipping"/> <!--Checkout select Check/Money Order payment--> - <actionGroup ref="SelectCheckMoneyPaymentMethod" stepKey="selectCheckMoneyPayment"/> + <actionGroup ref="SelectCheckMoneyPaymentMethodActionGroup" stepKey="selectCheckMoneyPayment"/> <!--Submit Order and verify information--> <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminReorderWithCatalogPriceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminReorderWithCatalogPriceTest.xml index 587b23e857c0c..4c7c8b163a38c 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminReorderWithCatalogPriceTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminReorderWithCatalogPriceTest.xml @@ -18,9 +18,6 @@ <useCaseId value="MAGETWO-99691"/> <group value="sales"/> <group value="catalogRule"/> - <skip> - <issueId value="MC-17140"/> - </skip> </annotations> <before> <!--Create the catalog price rule --> @@ -44,21 +41,20 @@ <after> <deleteData createDataKey="createSimpleProductApi" stepKey="deleteSimpleProductApi"/> <!-- Delete the rule --> - <actionGroup ref="RemoveCatalogPriceRule" stepKey="deletePriceRule"> + <actionGroup ref="RemoveCatalogPriceRuleActionGroup" stepKey="deletePriceRule"> <argument name="ruleName" value="{{CatalogRuleToPercent.name}}" /> </actionGroup> <!--Clear all filters in grid--> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="resetCatalogRuleGridFilters"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="resetCatalogRuleGridFilters"/> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilter"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/> <actionGroup ref="logout" stepKey="logout"/> </after> <!-- Login as admin --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!--Open order by Id--> - <actionGroup ref="OpenOrderById" stepKey="openOrderById"> - <argument name="orderId" value="$createGuestCart.return$"/> - </actionGroup> + <!--Open order page by Id--> + <amOnPage url="{{AdminOrderPage.url($createGuestCart.return$)}}" stepKey="navigateToOrderPage"/> + <waitForPageLoad stepKey="waitForCreatedOrderPage"/> <!--Reorder--> <click selector="{{AdminOrderDetailsMainActionsSection.reorder}}" stepKey="clickReorder"/> <!--Verify order item row--> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSaveInAddressBookCheckboxStateTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSaveInAddressBookCheckboxStateTest.xml index 650152a191d16..f2341278becba 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSaveInAddressBookCheckboxStateTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSaveInAddressBookCheckboxStateTest.xml @@ -39,12 +39,12 @@ </after> <!-- Create new order and choose an existing customer --> <comment userInput="Create new order and choose an existing customer" stepKey="createOrderAndAddCustomer"/> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderPage"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderPage"> <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <!-- Add simple product to order --> <comment userInput="Add simple product to order" stepKey="addSimpleProdToOrder"/> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToOrder"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> <!-- Just in case uncheck and check 'Same as Billing Address checkbox' --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml index 63607e59c41b2..65bba7512e228 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml @@ -91,35 +91,35 @@ </before> <!--Create new customer order--> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> <argument name="customer" value="$$simpleCustomer$$"/> </actionGroup> <!--Add configurable product to order--> - <actionGroup ref="addConfigurableProductToOrderFromAdmin" stepKey="addConfigurableProductToOrder"> + <actionGroup ref="AddConfigurableProductToOrderFromAdminActionGroup" stepKey="addConfigurableProductToOrder"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="attribute" value="$$createConfigProductAttribute$$"/> <argument name="option" value="$$getConfigAttributeOption1$$"/> </actionGroup> <!--Configure ordered configurable product--> - <actionGroup ref="configureOrderedConfigurableProduct" stepKey="configureOrderedConfigurableProduct"> + <actionGroup ref="ConfigureOrderedConfigurableProductActionGroup" stepKey="configureOrderedConfigurableProduct"> <argument name="attribute" value="$$createConfigProductAttribute$$"/> <argument name="option" value="$$getConfigAttributeOption2$$"/> <argument name="quantity" value="2"/> </actionGroup> <!--Select FlatRate shipping method--> - <actionGroup ref="orderSelectFlatRateShipping" stepKey="orderSelectFlatRateShippingMethod"/> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="orderSelectFlatRateShippingMethod"/> <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="SelectCheckMoneyPaymentMethod" stepKey="selectCheckMoneyPayment"/> + <actionGroup ref="SelectCheckMoneyPaymentMethodActionGroup" stepKey="selectCheckMoneyPayment"/> <!--Submit order--> <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitOrder"/> <!--Verify order information--> - <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> + <actionGroup ref="VerifyCreatedOrderInformationActionGroup" stepKey="verifyCreatedOrderInformation"/> <after> <actionGroup ref="logout" stepKey="logout"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderPaymentMethodValidationTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderPaymentMethodValidationTest.xml index 255a7a91f9b10..b2d424ca5d7d3 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderPaymentMethodValidationTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderPaymentMethodValidationTest.xml @@ -41,10 +41,10 @@ <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Create New Order" stepKey="seeNewOrderPageTitle"/> <!--Check if order can be submitted without the required fields--> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToOrder" after="seeNewOrderPageTitle"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToOrder" after="seeNewOrderPageTitle"> <argument name="product" value="_defaultProduct"/> </actionGroup> - <actionGroup ref="checkRequiredFieldsNewOrderForm" stepKey="checkRequiredFieldsNewOrder" after="addSimpleProductToOrder"/> + <actionGroup ref="CheckRequiredFieldsNewOrderFormActionGroup" stepKey="checkRequiredFieldsNewOrder" after="addSimpleProductToOrder"/> <see selector="{{AdminOrderFormPaymentSection.paymentError}}" userInput="Please select one of the options." stepKey="seePaymentMethodRequired" after="checkRequiredFieldsNewOrder"/> <scrollToTopOfPage stepKey="scrollToTopOfOrderFormPage" after="seePaymentMethodRequired"/> @@ -53,7 +53,7 @@ <fillField selector="{{AdminOrderFormAccountSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillCustomerEmail" after="selectCustomerGroup"/> <!--Fill customer address information--> - <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerAddress" after="fillCustomerEmail"> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerAddress" after="fillCustomerEmail"> <argument name="customer" value="Simple_US_Customer"/> <argument name="address" value="US_Address_TX"/> </actionGroup> @@ -61,7 +61,7 @@ <!-- Select payment and shipping --> <waitForElementVisible selector="{{AdminOrderFormPaymentSection.checkMoneyOption}}" stepKey="waitForPaymentOptions"/> <selectOption selector="{{AdminOrderFormPaymentSection.checkMoneyOption}}" userInput="checkmo" stepKey="checkPaymentOption"/> - <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRateShipping" after="fillCustomerAddress"/> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRateShipping" after="fillCustomerAddress"/> <!--Verify totals on Order page--> <see selector="{{AdminOrderFormTotalSection.total('Subtotal')}}" userInput="${{AdminOrderSimpleProduct.subtotal}}" stepKey="seeOrderSubTotal" after="selectFlatRateShipping"/> @@ -74,4 +74,4 @@ <seeInCurrentUrl url="{{AdminOrderDetailsPage.url}}" stepKey="seeViewOrderPage" after="clickSubmitOrder"/> <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeSuccessMessage" after="seeViewOrderPage"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml index 01021ad745f70..f2d0fffe9b4cf 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml @@ -31,7 +31,7 @@ <!--Create order via Admin--> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <comment userInput="Admin creates order" stepKey="adminCreateOrderComment"/> - <!--<actionGroup ref="navigateToNewOrderPageNewCustomer" stepKey="navigateToNewOrderPage"/>--> + <!--<actionGroup ref="NavigateToNewOrderPageNewCustomerActionGroup" stepKey="navigateToNewOrderPage"/>--> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderIndexPage"/> <waitForPageLoad stepKey="waitForIndexPageLoad"/> <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Orders" stepKey="seeIndexPageTitle"/> @@ -40,9 +40,9 @@ <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Create New Order" stepKey="seeNewOrderPageTitle"/> <!--Check if order can be submitted without the required fields including email address--> - <actionGroup ref="checkRequiredFieldsNewOrderForm" stepKey="checkRequiredFieldsNewOrder" after="seeNewOrderPageTitle"/> + <actionGroup ref="CheckRequiredFieldsNewOrderFormActionGroup" stepKey="checkRequiredFieldsNewOrder" after="seeNewOrderPageTitle"/> <scrollToTopOfPage stepKey="scrollToTopOfOrderFormPage" after="checkRequiredFieldsNewOrder"/> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToOrder" after="scrollToTopOfOrderFormPage"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToOrder" after="scrollToTopOfOrderFormPage"> <argument name="product" value="_defaultProduct"/> </actionGroup> @@ -51,15 +51,15 @@ <fillField selector="{{AdminOrderFormAccountSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillCustomerEmail" after="selectCustomerGroup"/> <!--Fill customer address information--> - <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerAddress" after="fillCustomerEmail"> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerAddress" after="fillCustomerEmail"> <argument name="customer" value="Simple_US_Customer"/> <argument name="address" value="US_Address_TX"/> </actionGroup> <!-- Select shipping --> - <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRateShipping" after="fillCustomerAddress"/> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRateShipping" after="fillCustomerAddress"/> <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="SelectCheckMoneyPaymentMethod" stepKey="selectCheckMoneyPayment"/> + <actionGroup ref="SelectCheckMoneyPaymentMethodActionGroup" stepKey="selectCheckMoneyPayment"/> <!--Verify totals on Order page--> <see selector="{{AdminOrderFormTotalSection.total('Subtotal')}}" userInput="${{AdminOrderSimpleProduct.subtotal}}" stepKey="seeOrderSubTotal" after="selectFlatRateShipping"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml index 9268e9e728658..4dbd80a351ee7 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutFieldsValidationTest.xml @@ -30,7 +30,7 @@ <!--Create order via Admin--> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <comment userInput="Admin creates order" stepKey="adminCreateOrderComment"/> - <!--<actionGroup ref="navigateToNewOrderPageNewCustomer" stepKey="navigateToNewOrderPage"/>--> + <!--<actionGroup ref="NavigateToNewOrderPageNewCustomerActionGroup" stepKey="navigateToNewOrderPage"/>--> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderIndexPage"/> <waitForPageLoad stepKey="waitForIndexPageLoad"/> <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Orders" stepKey="seeIndexPageTitle"/> @@ -39,9 +39,9 @@ <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Create New Order" stepKey="seeNewOrderPageTitle"/> <!--Check if order can be submitted without the required fields including email address--> - <actionGroup ref="checkRequiredFieldsNewOrderForm" stepKey="checkRequiredFieldsNewOrder" after="seeNewOrderPageTitle"/> + <actionGroup ref="CheckRequiredFieldsNewOrderFormActionGroup" stepKey="checkRequiredFieldsNewOrder" after="seeNewOrderPageTitle"/> <scrollToTopOfPage stepKey="scrollToTopOfOrderFormPage" after="checkRequiredFieldsNewOrder"/> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToOrder" after="scrollToTopOfOrderFormPage"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToOrder" after="scrollToTopOfOrderFormPage"> <argument name="product" value="_defaultProduct"/> </actionGroup> @@ -50,12 +50,12 @@ <fillField selector="{{AdminOrderFormAccountSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillCustomerEmail" after="selectCustomerGroup"/> <!--Fill wrong customer address information--> - <actionGroup ref="fillOrderCustomerInformation" stepKey="fillWrongCustomerAddress" after="fillCustomerEmail"> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillWrongCustomerAddress" after="fillCustomerEmail"> <argument name="customer" value="Simple_US_Customer_Incorrect_Name"/> <argument name="address" value="US_Address_TX"/> </actionGroup> <!-- Select shipping --> - <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRateShipping" + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRateShipping" after="fillWrongCustomerAddress"/> <!--Verify totals on Order page--> @@ -72,7 +72,7 @@ after="clickSubmitOrderWrong"/> <!--Fill correct customer address information--> - <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerAddress" after="firstNameError"> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerAddress" after="firstNameError"> <argument name="customer" value="Simple_US_Customer"/> <argument name="address" value="US_Address_TX"/> </actionGroup> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusNotVisibleOnStorefrontTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusNotVisibleOnStorefrontTest.xml index c292afe65cdf3..ac377a0d31606 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusNotVisibleOnStorefrontTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusNotVisibleOnStorefrontTest.xml @@ -87,7 +87,7 @@ <!-- Assert order status is correct --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrdersPage"/> <waitForPageLoad stepKey="waitForOrdersPageLoad"/> - <actionGroup ref="filterOrderGridById" stepKey="filterOrdersGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrdersGridById"> <argument name="orderId" value="$getOrderId"/> </actionGroup> <click selector="{{AdminDataGridTableSection.firstRow}}" stepKey="clickCreatedOrderInGrid"/> @@ -112,7 +112,7 @@ <!-- Cancel order --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToAdminOrdersPage"/> <waitForPageLoad stepKey="waitForAdminOrdersPageLoad"/> - <actionGroup ref="filterOrderGridById" stepKey="filterOrdersGridByOrderId"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrdersGridByOrderId"> <argument name="orderId" value="$getOrderId"/> </actionGroup> <checkOption selector="{{AdminOrdersGridSection.checkOrder}}" stepKey="selectOrder"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CheckXSSVulnerabilityDuringOrderCreationTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CheckXSSVulnerabilityDuringOrderCreationTest.xml index a728b57a743af..6eb9e37a3ab79 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CheckXSSVulnerabilityDuringOrderCreationTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CheckXSSVulnerabilityDuringOrderCreationTest.xml @@ -52,7 +52,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!-- Try to create order in admin with provided email --> - <actionGroup ref="navigateToNewOrderPageNewCustomerSingleStore" stepKey="navigateToNewOrderPage"/> + <actionGroup ref="NavigateToNewOrderPageNewCustomerSingleStoreActionGroup" stepKey="navigateToNewOrderPage"/> <fillField selector="{{AdminOrderFormAccountSection.email}}" userInput="{{Simple_US_Customer_Incorrect_Email.email}}" stepKey="fillEmailAddressAdminPanel"/> <click selector="{{AdminOrderFormActionSection.submitOrder}}" stepKey="clickSubmitOrder"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceAndCheckInvoiceOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceAndCheckInvoiceOrderTest.xml index 45953b7b584f2..0b065e0da54f0 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceAndCheckInvoiceOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceAndCheckInvoiceOrderTest.xml @@ -51,7 +51,7 @@ </after> <!-- Create order --> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="goToCreateOrderPage"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="goToCreateOrderPage"> <argument name="customer" value="$$createCustomer$$"/> </actionGroup> @@ -79,7 +79,7 @@ <!-- Open created order --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrdersPage"/> <waitForPageLoad stepKey="waitForOrdersPageLoad"/> - <actionGroup ref="filterOrderGridById" stepKey="filterOrdersGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrdersGridById"> <argument name="orderId" value="$getOrderId"/> </actionGroup> <click selector="{{AdminDataGridTableSection.firstRow}}" stepKey="clickCreatedOrderInGrid"/> @@ -105,7 +105,7 @@ <!-- Assert invoice in invoices tab --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> <waitForPageLoad stepKey="waitForOrdersLoad"/> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridByIdForAssertingInvoiceBtn"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridByIdForAssertingInvoiceBtn"> <argument name="orderId" value="$getOrderId"/> </actionGroup> <click selector="{{AdminDataGridTableSection.firstRow}}" stepKey="clickOrderInGrid"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithCashOnDeliveryPaymentMethodTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithCashOnDeliveryPaymentMethodTest.xml index ef194028a4367..295d388ced96e 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithCashOnDeliveryPaymentMethodTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithCashOnDeliveryPaymentMethodTest.xml @@ -51,7 +51,7 @@ </after> <!-- Create order --> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="goToCreateOrderPage"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="goToCreateOrderPage"> <argument name="customer" value="$$createCustomer$$"/> </actionGroup> @@ -79,7 +79,7 @@ <!-- Open created order --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrdersPage"/> <waitForPageLoad stepKey="waitForOrdersPageLoad"/> - <actionGroup ref="filterOrderGridById" stepKey="filterOrdersGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrdersGridById"> <argument name="orderId" value="$getOrderId"/> </actionGroup> <click selector="{{AdminDataGridTableSection.firstRow}}" stepKey="clickCreatedOrderInGrid"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithShipmentAndCheckInvoicedOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithShipmentAndCheckInvoicedOrderTest.xml index 30ca1f5175576..2ccecf34a5a0c 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithShipmentAndCheckInvoicedOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithShipmentAndCheckInvoicedOrderTest.xml @@ -49,12 +49,12 @@ </after> <!-- Create order--> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="goToCreateOrderPage"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="goToCreateOrderPage"> <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <!-- Add product to order --> - <actionGroup ref="addSimpleProductToOrder" stepKey="addProductToOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addProductToOrder"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> @@ -73,7 +73,7 @@ <!-- Open created order --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrdersPage"/> <waitForPageLoad stepKey="waitForOrdersPageLoad"/> - <actionGroup ref="filterOrderGridById" stepKey="filterOrdersGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrdersGridById"> <argument name="orderId" value="$getOrderId"/> </actionGroup> <click selector="{{AdminDataGridTableSection.firstRow}}" stepKey="clickCreatedOrderInGrid"/> @@ -103,7 +103,7 @@ <!-- Assert no invoice button --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> <waitForPageLoad stepKey="waitForOrdersLoad"/> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridByIdForAssertingInvoiceBtn"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridByIdForAssertingInvoiceBtn"> <argument name="orderId" value="$getOrderId"/> </actionGroup> <click selector="{{AdminDataGridTableSection.firstRow}}" stepKey="clickOrderInGrid"/> @@ -148,7 +148,7 @@ <!-- Assert no ship button --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToAdminOrdersPage"/> <waitForPageLoad stepKey="waitForOrdersPageToLoad"/> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridByIdForAssertingShipBtn"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridByIdForAssertingShipBtn"> <argument name="orderId" value="$getOrderId"/> </actionGroup> <click selector="{{AdminDataGridTableSection.firstRow}}" stepKey="selectOrderInGrid"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithZeroSubtotalCheckoutTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithZeroSubtotalCheckoutTest.xml index 4b8e5d88cdf49..944a1ed75cebd 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithZeroSubtotalCheckoutTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateInvoiceWithZeroSubtotalCheckoutTest.xml @@ -62,12 +62,12 @@ </after> <!-- Create order --> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="goToCreateOrderPage"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="goToCreateOrderPage"> <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <!-- Add product to order --> - <actionGroup ref="addSimpleProductToOrder" stepKey="addProductToOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addProductToOrder"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> @@ -77,7 +77,7 @@ </actionGroup> <!-- Select Free shipping --> - <actionGroup ref="orderSelectFreeShipping" stepKey="selectFreeShippingOption"/> + <actionGroup ref="OrderSelectFreeShippingActionGroup" stepKey="selectFreeShippingOption"/> <!-- Submit order --> <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> @@ -88,7 +88,7 @@ <!-- Open created order --> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrdersPage"/> <waitForPageLoad stepKey="waitForOrdersPageLoad"/> - <actionGroup ref="filterOrderGridById" stepKey="filterOrdersGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrdersGridById"> <argument name="orderId" value="$getOrderId"/> </actionGroup> <click selector="{{AdminDataGridTableSection.firstRow}}" stepKey="clickCreatedOrderInGrid"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml index 4c1f16192c88c..0fcfa483f1adf 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml @@ -87,6 +87,8 @@ <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigurableProduct"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="openCustomerIndexPage"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearCustomerGridFilter"/> <actionGroup ref="logout" stepKey="logout"/> </after> @@ -98,21 +100,22 @@ <waitForPageLoad stepKey="waitForCustomerEditPageToLoad"/> <click selector="{{AdminCustomerMainActionsSection.createOrderBtn}}" stepKey="clickOnCreateOrderButton"/> <waitForPageLoad stepKey="waitForOrderPageToLoad"/> + <conditionalClick selector="{{AdminOrderStoreScopeTreeSection.storeOption(_defaultStore.name)}}" dependentSelector="{{AdminOrderStoreScopeTreeSection.storeOption(_defaultStore.name)}}" visible="true" stepKey="selectStoreViewIfAppears"/> <!--Add configurable product to order--> - <actionGroup ref="addConfigurableProductToOrderFromAdmin" stepKey="addConfigurableProductToOrder"> + <actionGroup ref="AddConfigurableProductToOrderFromAdminActionGroup" stepKey="addConfigurableProductToOrder"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="attribute" value="$$createConfigProductAttribute$$"/> <argument name="option" value="$$getConfigAttributeOption1$$"/> </actionGroup> <!--Add Simple product to order--> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToOrder"> <argument name="product" value="$$simpleProduct$$"/> </actionGroup> <!--Add Second Simple product to order--> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSecondSimpleProductToOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSecondSimpleProductToOrder"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> @@ -182,7 +185,7 @@ <waitForPageLoad stepKey="waitForAdminOrderFormLoad"/> <!-- Verify order information --> - <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> + <actionGroup ref="VerifyCreatedOrderInformationActionGroup" stepKey="verifyCreatedOrderInformation"/> <grabTextFrom selector="|Order # (\d+)|" stepKey="orderId"/> <!-- Filter and Open the customer edit page --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml index ec0f97e418c8c..8ca8dbf21a3a2 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml @@ -25,10 +25,10 @@ </createData> <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearOrderFilters"/> - <actionGroup ref="SetTaxClassForShipping" stepKey="setShippingTaxClass"/> + <actionGroup ref="SetTaxClassForShippingActionGroup" stepKey="setShippingTaxClass"/> </before> <after> - <actionGroup ref="ResetTaxClassForShipping" stepKey="resetTaxClassForShipping"/> + <actionGroup ref="ResetTaxClassForShippingActionGroup" stepKey="resetTaxClassForShipping"/> <actionGroup ref="DeleteCartPriceRuleByName" stepKey="deleteSalesRule"> <argument name="ruleName" value="{{ApiSalesRule.name}}"/> </actionGroup> @@ -44,7 +44,7 @@ <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"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="chooseNotLoggedInCustomerGroup"/> <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/EndToEndB2CAdminTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/EndToEndB2CAdminTest.xml index 0fdd8d8c35b32..6a1cefae7553d 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/EndToEndB2CAdminTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/EndToEndB2CAdminTest.xml @@ -35,14 +35,14 @@ <!--Admin creates order--> <comment userInput="Admin creates order" stepKey="adminCreateOrderComment" before="navigateToNewOrderPage"/> - <actionGroup ref="navigateToNewOrderPageNewCustomer" stepKey="navigateToNewOrderPage" after="deleteCategory"/> + <actionGroup ref="NavigateToNewOrderPageNewCustomerActionGroup" stepKey="navigateToNewOrderPage" after="deleteCategory"/> - <actionGroup ref="checkRequiredFieldsNewOrderForm" stepKey="checkRequiredFieldsNewOrder" after="navigateToNewOrderPage"/> + <actionGroup ref="CheckRequiredFieldsNewOrderFormActionGroup" stepKey="checkRequiredFieldsNewOrder" after="navigateToNewOrderPage"/> <scrollToTopOfPage stepKey="scrollToTopOfOrderFormPage" after="checkRequiredFieldsNewOrder"/> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToOrder" after="scrollToTopOfOrderFormPage"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToOrder" after="scrollToTopOfOrderFormPage"> <argument name="product" value="SimpleProduct"/> </actionGroup> - <actionGroup ref="addConfigurableProductToOrder" stepKey="addConfigurableProductToOrder" after="addSimpleProductToOrder"> + <actionGroup ref="AddConfigurableProductToOrderActionGroup" stepKey="addConfigurableProductToOrder" after="addSimpleProductToOrder"> <argument name="product" value="BaseConfigurableProduct"/> <argument name="attribute" value="colorProductAttribute"/> <argument name="option" value="colorProductAttribute1"/> @@ -53,12 +53,12 @@ <fillField selector="{{AdminOrderFormAccountSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillEmail" after="selectGroup"/> <!--Fill customer address information--> - <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerAddress" after="fillEmail"> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerAddress" after="fillEmail"> <argument name="customer" value="Simple_US_Customer"/> <argument name="address" value="US_Address_TX"/> </actionGroup> - <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRateShipping" after="fillCustomerAddress"/> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRateShipping" after="fillCustomerAddress"/> <!--Verify totals on Order page--> <see selector="{{AdminOrderFormTotalSection.total('Subtotal')}}" userInput="${{AdminOrderSimpleConfigurableProduct.subtotal}}" stepKey="seeOrderSubTotal" after="selectFlatRateShipping"/> @@ -73,15 +73,15 @@ <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="seeOrderPendingStatus" after="seeSuccessMessage"/> <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId" after="seeOrderPendingStatus"/> <assertNotEmpty actual="$getOrderId" stepKey="assertOrderIdIsNotEmpty" after="getOrderId"/> - <actionGroup ref="verifyBasicOrderInformation" stepKey="verifyOrderInformation" after="assertOrderIdIsNotEmpty"> + <actionGroup ref="VerifyBasicOrderInformationActionGroup" stepKey="verifyOrderInformation" after="assertOrderIdIsNotEmpty"> <argument name="customer" value="Simple_US_Customer"/> <argument name="shippingAddress" value="US_Address_TX"/> <argument name="billingAddress" value="US_Address_TX"/> </actionGroup> - <actionGroup ref="seeProductInItemsOrdered" stepKey="seeSimpleProductInItemsOrdered" after="verifyOrderInformation"> + <actionGroup ref="SeeProductInItemsOrderedActionGroup" stepKey="seeSimpleProductInItemsOrdered" after="verifyOrderInformation"> <argument name="product" value="SimpleProduct"/> </actionGroup> - <actionGroup ref="seeProductInItemsOrdered" stepKey="seeConfigurableProductInItemsOrdered" after="seeSimpleProductInItemsOrdered"> + <actionGroup ref="SeeProductInItemsOrderedActionGroup" stepKey="seeConfigurableProductInItemsOrdered" after="seeSimpleProductInItemsOrdered"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> @@ -94,7 +94,7 @@ <!--Check Invoice Data--> <see selector="{{AdminInvoiceOrderInformationSection.orderStatus}}" userInput="Pending" stepKey="seeOrderPendingInvoice" after="seePageNameNewInvoicePage"/> - <actionGroup ref="verifyBasicInvoiceInformation" stepKey="verifyOrderInvoiceInformation" after="seeOrderPendingInvoice"> + <actionGroup ref="VerifyBasicInvoiceInformationActionGroup" stepKey="verifyOrderInvoiceInformation" after="seeOrderPendingInvoice"> <argument name="customer" value="Simple_US_Customer"/> <argument name="shippingAddress" value="US_Address_TX"/> <argument name="billingAddress" value="US_Address_TX"/> @@ -113,15 +113,15 @@ <see selector="{{AdminOrderInvoicesTabSection.gridRow('1')}}" userInput="{{Simple_US_Customer.firstname}}" stepKey="seeOrderInvoiceInTabGrid" after="waitForInvoiceGridLoadingMask"/> <click selector="{{AdminOrderInvoicesTabSection.viewGridRow('1')}}" stepKey="clickToViewInvoiceRow" after="seeOrderInvoiceInTabGrid"/> <see selector="{{AdminInvoiceOrderInformationSection.orderId}}" userInput="$getOrderId" stepKey="seeOrderIdOnInvoice" after="clickToViewInvoiceRow"/> - <actionGroup ref="verifyBasicInvoiceInformation" stepKey="verifyBasicInvoiceInformation" after="seeOrderIdOnInvoice"> + <actionGroup ref="VerifyBasicInvoiceInformationActionGroup" stepKey="verifyBasicInvoiceInformation" after="seeOrderIdOnInvoice"> <argument name="customer" value="Simple_US_Customer"/> <argument name="shippingAddress" value="US_Address_TX"/> <argument name="billingAddress" value="US_Address_TX"/> </actionGroup> - <actionGroup ref="seeProductInInvoiceItems" stepKey="seeSimpleProductInInvoice" after="verifyBasicInvoiceInformation"> + <actionGroup ref="SeeProductInInvoiceItemsActionGroup" stepKey="seeSimpleProductInInvoice" after="verifyBasicInvoiceInformation"> <argument name="product" value="SimpleProduct"/> </actionGroup> - <actionGroup ref="seeProductInInvoiceItems" stepKey="seeConfigurableProductInInvoice" after="seeSimpleProductInInvoice"> + <actionGroup ref="SeeProductInInvoiceItemsActionGroup" stepKey="seeConfigurableProductInInvoice" after="seeSimpleProductInInvoice"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> <click selector="{{AdminInvoiceOrderInformationSection.orderId}}" stepKey="clickOrderIdLinkOnInvoice" after="seeConfigurableProductInInvoice"/> @@ -132,7 +132,7 @@ <seeInCurrentUrl url="{{AdminCreditMemoNewPage.url}}" stepKey="seeNewCreditMemoPage" after="clickCreateCreditMemo"/> <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Memo" stepKey="seeNewMemoInPageTitle" after="seeNewCreditMemoPage"/> <!--Check Credit Memo Order Data--> - <actionGroup ref="verifyBasicCreditMemoInformation" stepKey="verifyOrderCreditMemoInformation" after="seeNewMemoInPageTitle"> + <actionGroup ref="VerifyBasicCreditMemoInformationActionGroup" stepKey="verifyOrderCreditMemoInformation" after="seeNewMemoInPageTitle"> <argument name="customer" value="Simple_US_Customer"/> <argument name="shippingAddress" value="US_Address_TX"/> <argument name="billingAddress" value="US_Address_TX"/> @@ -146,15 +146,15 @@ <click selector="{{AdminOrderCreditMemosTabSection.viewGridRow('1')}}" stepKey="clickToViewCreditMemoRow" after="seeOrderCreditMemoInTabGrid"/> <waitForPageLoad stepKey="waitForCreditMemoPageLoad" after="clickToViewCreditMemoRow"/> <see selector="{{AdminCreditMemoOrderInformationSection.orderId}}" userInput="$getOrderId" stepKey="seeOrderIdOnCreditMemo" after="waitForCreditMemoPageLoad"/> - <actionGroup ref="verifyBasicCreditMemoInformation" stepKey="verifyBasicCreditMemoInformation" after="seeOrderIdOnCreditMemo"> + <actionGroup ref="VerifyBasicCreditMemoInformationActionGroup" stepKey="verifyBasicCreditMemoInformation" after="seeOrderIdOnCreditMemo"> <argument name="customer" value="Simple_US_Customer"/> <argument name="shippingAddress" value="US_Address_TX"/> <argument name="billingAddress" value="US_Address_TX"/> </actionGroup> - <actionGroup ref="seeProductInItemsRefunded" stepKey="seeSimpleProductInItemsRefunded" after="verifyBasicCreditMemoInformation"> + <actionGroup ref="SeeProductInItemsRefundedActionGroup" stepKey="seeSimpleProductInItemsRefunded" after="verifyBasicCreditMemoInformation"> <argument name="product" value="SimpleProduct"/> </actionGroup> - <actionGroup ref="seeProductInItemsRefunded" stepKey="seeConfigurableProductInItemsRefunded" after="seeSimpleProductInItemsRefunded"> + <actionGroup ref="SeeProductInItemsRefundedActionGroup" stepKey="seeConfigurableProductInItemsRefunded" after="seeSimpleProductInItemsRefunded"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> <scrollToTopOfPage stepKey="scrollToTopOfCreditMemo" after="seeConfigurableProductInItemsRefunded"/> @@ -168,10 +168,10 @@ <!--Search order grid by name--> <comment userInput="Admin searches order grid by name" stepKey="searchOrderGridComment" after="waitForOrderGridPageLoad"/> - <actionGroup ref="resetAdminDataGridToDefaultView" stepKey="setOrderGridToDefaultViewForSearch" after="searchOrderGridComment"/> + <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="setOrderGridToDefaultViewForSearch" after="searchOrderGridComment"/> <!--@TODO use "Ship-to Name" when MQE-794 is fixed--> <see selector="{{AdminDataGridTableSection.column('Ship')}}" userInput="{{Simple_US_Customer.fullname}}" stepKey="seeNonFilterNameInShipNameColumn" after="setOrderGridToDefaultViewForSearch"/> - <actionGroup ref="searchAdminDataGridByKeyword" stepKey="searchOrderGridByNameKeyword" after="seeNonFilterNameInShipNameColumn"> + <actionGroup ref="SearchAdminDataGridByKeywordActionGroup" stepKey="searchOrderGridByNameKeyword" after="seeNonFilterNameInShipNameColumn"> <argument name="keyword" value="BillingAddressTX.fullname"/> </actionGroup> <dontSee selector="{{AdminDataGridTableSection.column('Ship')}}" userInput="{{Simple_US_Customer.fullname}}" stepKey="dontSeeNonFilterNameInShipNameColumn" after="searchOrderGridByNameKeyword"/> @@ -180,19 +180,19 @@ <!--Filter order grid--> <comment userInput="Admin filters order grid by 'Bill-to Name'" stepKey="filterOrderGridByNameComment" after="seeFilterNameInShipNameColumn"/> <!--Filter order grid by "Bill-to Name"--> - <actionGroup ref="resetAdminDataGridToDefaultView" stepKey="resetOrderGridForNameFilter" after="filterOrderGridByNameComment"/> + <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetOrderGridForNameFilter" after="filterOrderGridByNameComment"/> <!--@TODO use "Bill-to Name" when MQE-794 is fixed--> <see selector="{{AdminDataGridTableSection.column('Bill')}}" userInput="{{BillingAddressTX.fullname}}" stepKey="seeNonFilterNameInColumn" after="resetOrderGridForNameFilter"/> - <actionGroup ref="filterOrderGridByBillingName" stepKey="filterOrderGridByBillingName" after="seeNonFilterNameInColumn"> + <actionGroup ref="FilterOrderGridByBillingNameActionGroup" stepKey="filterOrderGridByBillingName" after="seeNonFilterNameInColumn"> <argument name="customer" value="Simple_US_Customer"/> </actionGroup> <dontSee selector="{{AdminDataGridTableSection.column('Bill')}}" userInput="{{BillingAddressTX.fullname}}" stepKey="dontSeeNonFilterNameInColumn" after="filterOrderGridByBillingName"/> <see selector="{{AdminDataGridTableSection.column('Bill')}}" userInput="{{Simple_US_Customer.fullname}}" stepKey="seeFilterNameInColumn" after="dontSeeNonFilterNameInColumn"/> <!--Filter order grid by Grand Total (Base)--> <comment userInput="Admin filters order grid by 'Grand Total'" stepKey="filterOrderGridByTotalComment" after="seeFilterNameInColumn"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilterBeforeTotalFilter" after="filterOrderGridByTotalComment"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilterBeforeTotalFilter" after="filterOrderGridByTotalComment"/> <see selector="{{AdminDataGridTableSection.column('Grand Total')}}" userInput="{{AdminOrderSimpleProduct.grandTotal}}" stepKey="seeLowerTotalInGrid" after="clearFilterBeforeTotalFilter"/> - <actionGroup ref="filterOrderGridByBaseTotalRange" stepKey="filterOrderGridByTotal" after="seeLowerTotalInGrid"> + <actionGroup ref="FilterOrderGridByBaseTotalRangeActionGroup" stepKey="filterOrderGridByTotal" after="seeLowerTotalInGrid"> <argument name="from" value="OrderGrandTotalFilterRange.from"/> <argument name="to" value="OrderGrandTotalFilterRange.to"/> </actionGroup> @@ -200,7 +200,7 @@ <see selector="{{AdminDataGridTableSection.column('Grand Total')}}" userInput="{{AdminOrderSimpleConfigurableProduct.grandTotal}}" stepKey="seeExpectedTotalInGrid" after="dontSeeLowerTotalInGrid"/> <!--Filter order grid by purchase date--> <comment userInput="Admin filters order grid by 'Purchase Date'" stepKey="filterOrderGridByDateComment" after="seeExpectedTotalInGrid"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilterBeforeOrderDateFilter" after="filterOrderGridByDateComment"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilterBeforeOrderDateFilter" after="filterOrderGridByDateComment"/> <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openOrderFilterForOrderDateFrom" after="clearFilterBeforeOrderDateFilter"/> <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('created_at[from]')}}" userInput="01/01/2018" stepKey="fillOrderDateFromFilter" after="openOrderFilterForOrderDateFrom"/> <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="applyFilterOrderDateFrom" after="fillOrderDateFromFilter"/> @@ -216,20 +216,20 @@ <dontSee selector="{{AdminDataGridTableSection.column('Bill')}}" userInput="{{BillingAddressTX.fullname}}" stepKey="dontSeeSecondOrderInGrid" after="dontSeeFirstOrderInGrid"/> <!--Filter order grid by status--> <comment userInput="Admin filters order grid by 'Status'" stepKey="filterOrderGridByStatusComment" after="dontSeeSecondOrderInGrid"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilterBeforeStatusFilter" after="filterOrderGridByStatusComment"/> - <actionGroup ref="filterOrderGridByStatus" stepKey="filterOrderGridByPendingStatus" after="clearFilterBeforeStatusFilter"> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilterBeforeStatusFilter" after="filterOrderGridByStatusComment"/> + <actionGroup ref="FilterOrderGridByStatusActionGroup" stepKey="filterOrderGridByPendingStatus" after="clearFilterBeforeStatusFilter"> <argument name="status" value="OrderStatus.pending"/> </actionGroup> <dontSee selector="{{AdminDataGridTableSection.column('Status')}}" userInput="{{OrderStatus.closed}}" stepKey="dontSeeClosedStatusInOrderGrid" after="filterOrderGridByPendingStatus"/> <see selector="{{AdminDataGridTableSection.column('Status')}}" userInput="{{OrderStatus.pending}}" stepKey="seePendingStatusInOrderGrid" after="dontSeeClosedStatusInOrderGrid"/> - <actionGroup ref="filterOrderGridByStatus" stepKey="filterOrderGridByClosedStatus" after="seePendingStatusInOrderGrid"> + <actionGroup ref="FilterOrderGridByStatusActionGroup" stepKey="filterOrderGridByClosedStatus" after="seePendingStatusInOrderGrid"> <argument name="status" value="OrderStatus.closed"/> </actionGroup> <see selector="{{AdminDataGridTableSection.column('Status')}}" userInput="{{OrderStatus.closed}}" stepKey="seeClosedStatusInOrderGrid" after="filterOrderGridByClosedStatus"/> <dontSee selector="{{AdminDataGridTableSection.column('Status')}}" userInput="{{OrderStatus.pending}}" stepKey="dontSeePendingStatusInOrderGrid" after="seeClosedStatusInOrderGrid"/> <!--Sort order grid--> - <actionGroup ref="resetAdminDataGridToDefaultView" stepKey="resetOrderGridForSorting" after="dontSeePendingStatusInOrderGrid"/> + <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetOrderGridForSorting" after="dontSeePendingStatusInOrderGrid"/> <!--Sort order grid by status--> <comment userInput="Admin sorts order grid by status" stepKey="sortOrderGridByStatusComment" after="resetOrderGridForSorting"/> <click selector="{{AdminDataGridTableSection.columnHeader('Status')}}" stepKey="clickStatusToSortAsc" after="sortOrderGridByStatusComment"/> @@ -239,9 +239,9 @@ <!--@TODO improve sort assertion and check price and date column when MQE-690 is resolved--> <!--Use paging on order grid--> - <actionGroup ref="resetAdminDataGridToDefaultView" stepKey="resetAdminGridBeforePaging" after="checkStatusSortOrderAsc"/> + <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminGridBeforePaging" after="checkStatusSortOrderAsc"/> <comment userInput="Admin uses paging on order grid" stepKey="usePagingOrderGridComment" after="resetAdminGridBeforePaging"/> - <actionGroup ref="adminDataGridSelectCustomPerPage" stepKey="select1OrderPerPage" after="usePagingOrderGridComment"> + <actionGroup ref="AdminDataGridSelectCustomPerPageActionGroup" stepKey="select1OrderPerPage" after="usePagingOrderGridComment"> <!--@TODO Change this to scalar when MQE-498 is implemented--> <argument name="perPage" value="Const.one"/> </actionGroup> @@ -250,7 +250,7 @@ <click selector="{{AdminDataGridPaginationSection.nextPage}}" stepKey="clickNextPageOrderGrid" after="seeOnFirstPageOrderGrid"/> <seeInField selector="{{AdminDataGridPaginationSection.currentPage}}" userInput="2" stepKey="seeOnSecondPageOrderGrid" after="clickNextPageOrderGrid"/> <seeNumberOfElements selector="{{AdminDataGridTableSection.rows}}" userInput="1" stepKey="see1RowOnSecondPage" after="seeOnSecondPageOrderGrid"/> - <actionGroup ref="adminDataGridSelectPerPage" stepKey="select50OrdersPerPage" after="see1RowOnSecondPage"> + <actionGroup ref="AdminDataGridSelectPerPageActionGroup" stepKey="select50OrdersPerPage" after="see1RowOnSecondPage"> <!--@TODO Change this to scalar when MQE-498 is implemented--> <argument name="perPage" value="Const.fifty"/> </actionGroup> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml index 3cc14392c6183..b3c3f045f0d59 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml @@ -69,12 +69,12 @@ </after> <!-- Create order --> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="goToCreateOrderPage"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="goToCreateOrderPage"> <argument name="customer" value="$$createCustomer$$"/> </actionGroup> <!-- Add configurable product to order --> - <actionGroup ref="addConfigurableProductToOrderFromAdmin" stepKey="addConfigurableProductToOrder"> + <actionGroup ref="AddConfigurableProductToOrderFromAdminActionGroup" stepKey="addConfigurableProductToOrder"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="attribute" value="$$createConfigProductAttribute$$"/> <argument name="option" value="$$getConfigAttributeOption$$"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedBundleFixedProductOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedBundleFixedProductOnOrderPageTest.xml index 9e794ce079b6e..0a39e8a0ac852 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedBundleFixedProductOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedBundleFixedProductOnOrderPageTest.xml @@ -54,6 +54,9 @@ <requiredEntity createDataKey="createBundleOption"/> <requiredEntity createDataKey="createSecondProduct"/> </createData> + <!-- Change configuration --> + <magentoCLI command="config:set reports/options/enabled 1" stepKey="enableReportModule"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> </before> <after> @@ -73,6 +76,9 @@ <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- Change configuration --> + <magentoCLI command="config:set reports/options/enabled 0" stepKey="disableReportModule"/> </after> <!-- Login as customer --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml index 35dc49d2b8a43..aed7447050e5f 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml @@ -57,6 +57,8 @@ <requiredEntity createDataKey="createConfigProduct"/> <requiredEntity createDataKey="createConfigChildProduct"/> </createData> + <!-- Change configuration --> + <magentoCLI command="config:set reports/options/enabled 1" stepKey="enableReportModule"/> </before> <after> <!-- Admin logout --> @@ -75,6 +77,9 @@ <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- Change configuration --> + <magentoCLI command="config:set reports/options/enabled 0" stepKey="disableReportModule"/> </after> <!-- Login as customer --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml index 0e58bb84988a2..aef3e884c4712 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml @@ -89,6 +89,9 @@ <!-- Customer is created --> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <!-- Reindex and flush the cache to display products on the category page --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> <!-- Delete category and products --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml index 0bd8ab4855e97..31f3449866984 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml @@ -36,7 +36,7 @@ </createData> <!-- Check Links can be purchased separately for Downloadable Product --> - <actionGroup ref="navigateToCreatedProductEditPage" stepKey="goToDownloadableProduct"> + <actionGroup ref="NavigateToCreatedProductEditPageActionGroup" stepKey="goToDownloadableProduct"> <argument name="product" value="$$downloadableProduct$$"/> </actionGroup> <waitForPageLoad stepKey="waitForPageLoad"/> @@ -112,7 +112,7 @@ </createData> <!-- Grab attribute name for Configurable Product --> - <actionGroup ref="navigateToCreatedProductEditPage" stepKey="goToConfigurableProduct"> + <actionGroup ref="NavigateToCreatedProductEditPageActionGroup" stepKey="goToConfigurableProduct"> <argument name="product" value="$$createConfigProduct$$"/> </actionGroup> <grabTextFrom selector="{{AdminConfigurableProductFormSection.currentAttribute}}" stepKey="grabAttribute"/> @@ -145,7 +145,7 @@ </createData> <!-- Grab bundle option name for Bundle Product --> - <actionGroup ref="navigateToCreatedProductEditPage" stepKey="goToBundleProduct"> + <actionGroup ref="NavigateToCreatedProductEditPageActionGroup" stepKey="goToBundleProduct"> <argument name="product" value="$$createBundleProduct$$"/> </actionGroup> <grabTextFrom selector="{{AdminProductFormBundleSection.currentBundleOption}}" stepKey="grabBundleOption"/> @@ -164,7 +164,7 @@ <magentoCLI command="cache:flush" stepKey="flushCache"/> <!-- Place order with options according to dataset --> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="newOrder"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="newOrder"> <argument name="customer" value="$$createCustomer$$"/> </actionGroup> @@ -200,12 +200,12 @@ <argument name="couponCode" value="$$createCouponForCartPriceRule.code$$"/> </actionGroup> - <actionGroup ref="fillOrderCustomerInformation" stepKey="fillOrder"> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillOrder"> <argument name="customer" value="$$createCustomer$$"/> <argument name="address" value="US_Address_TX"/> </actionGroup> - <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRate"/> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRate"/> </before> <after> <magentoCLI command="downloadable:domains:remove" arguments="example.com static.magento.com" stepKey="removeDownloadableDomain"/> diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/AddCommentTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/AddCommentTest.php index 053df53949296..9fe3042fa6bdf 100644 --- a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/AddCommentTest.php +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/Invoice/AddCommentTest.php @@ -3,6 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Sales\Test\Unit\Controller\Adminhtml\Order\Invoice; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; @@ -186,7 +189,8 @@ protected function setUp() 'invoiceCommentSender' => $this->commentSenderMock, 'resultPageFactory' => $this->resultPageFactoryMock, 'resultRawFactory' => $this->resultRawFactoryMock, - 'resultJsonFactory' => $this->resultJsonFactoryMock + 'resultJsonFactory' => $this->resultJsonFactoryMock, + 'invoiceRepository' => $this->invoiceRepository ] ); @@ -230,8 +234,9 @@ public function testExecute() $invoiceMock->expects($this->once()) ->method('addComment') ->with($data['comment'], false, false); - $invoiceMock->expects($this->once()) - ->method('save'); + $this->invoiceRepository->expects($this->once()) + ->method('save') + ->with($invoiceMock); $this->invoiceRepository->expects($this->once()) ->method('get') @@ -307,11 +312,11 @@ public function testExecuteModelException() public function testExecuteException() { $response = ['error' => true, 'message' => 'Cannot add new comment.']; - $e = new \Exception('test'); + $error = new \Exception('test'); $this->requestMock->expects($this->once()) ->method('getParam') - ->will($this->throwException($e)); + ->will($this->throwException($error)); $this->resultJsonFactoryMock->expects($this->once()) ->method('create') diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/TaxTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/TaxTest.php index 565d51ff515a2..f32ce7aa4715b 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/TaxTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Creditmemo/Total/TaxTest.php @@ -100,16 +100,18 @@ public function testCollect($orderData, $creditmemoData, $expectedResults) } $this->creditmemo->expects($this->any()) ->method('roundPrice') - ->will($this->returnCallback( - function ($price, $type) use (&$roundingDelta) { - if (!isset($roundingDelta[$type])) { - $roundingDelta[$type] = 0; + ->will( + $this->returnCallback( + function ($price, $type) use (&$roundingDelta) { + if (!isset($roundingDelta[$type])) { + $roundingDelta[$type] = 0; + } + $roundedPrice = round($price + $roundingDelta[$type], 2); + $roundingDelta[$type] = $price - $roundedPrice; + return $roundedPrice; } - $roundedPrice = round($price + $roundingDelta[$type], 2); - $roundingDelta[$type] = $price - $roundedPrice; - return $roundedPrice; - } - )); + ) + ); $this->model->collect($this->creditmemo); @@ -610,6 +612,143 @@ public function collectDataProvider() ], ]; + // scenario 6: 2 items, 2 invoiced, price includes tax, full discount, free shipping + // partial credit memo, make sure that discount tax compensation (with 100 % discount) is calculated correctly + $result['collect_with_full_discount_product_price'] = [ + 'order_data' => [ + 'data_fields' => [ + 'discount_amount' => -200.00, + 'discount_invoiced' => -200.00, + 'subtotal' => 181.82, + 'subtotal_incl_tax' => 200, + 'base_subtotal' => 181.82, + 'base_subtotal_incl_tax' => 200, + 'subtotal_invoiced' => 181.82, + 'discount_tax_compensation_amount' => 18.18, + 'discount_tax_compensation_invoiced' => 18.18, + 'base_discount_tax_compensation_amount' => 18.18, + 'base_discount_tax_compensation_invoiced' => 18.18, + 'grand_total' => 0, + 'base_grand_total' => 0, + 'shipping_tax_amount' => 0, + 'base_shipping_tax_amount' => 0, + 'shipping_discount_tax_compensation_amount' => 0, + 'base_shipping_discount_tax_compensation_amount' => 0, + 'tax_amount' => 0, + 'base_tax_amount' => 0, + 'tax_invoiced' => 0, + 'base_tax_invoiced' => 0, + 'tax_refunded' => 0, + 'base_tax_refunded' => 0, + 'base_shipping_amount' => 0, + ], + ], + 'creditmemo_data' => [ + 'items' => [ + 'item_1' => [ + 'order_item' => [ + 'qty_invoiced' => 1, + 'tax_amount' => 0, + 'tax_invoiced' => 0, + 'tax_refunded' => null, + 'base_tax_amount' => 0, + 'base_tax_invoiced' => 0, + 'base_tax_refunded' => 0, + 'tax_percent' => 10, + 'qty_refunded' => 0, + 'discount_percent' => 100, + 'discount_amount' => 100, + 'base_discount_amount' => 100, + 'discount_invoiced' => 100, + 'base_discount_invoiced' => 100, + 'row_total' => 90.91, + 'base_row_total' => 90.91, + 'row_invoiced' => 90.91, + 'base_row_invoiced' => 90.91, + 'price_incl_tax' => 100, + 'base_price_incl_tax' => 100, + 'row_total_incl_tax' => 100, + 'base_row_total_incl_tax' => 100, + 'discount_tax_compensation_amount' => 9.09, + 'base_discount_tax_compensation_amount' => 9.09, + 'discount_tax_compensation_invoiced' => 9.09, + 'base_discount_tax_compensation_invoiced' => 9.09, + ], + 'is_last' => true, + 'qty' => 1, + ], + 'item_2' => [ + 'order_item' => [ + 'qty_invoiced' => 1, + 'tax_amount' => 0, + 'tax_invoiced' => 0, + 'tax_refunded' => null, + 'base_tax_amount' => 0, + 'base_tax_invoiced' => 0, + 'base_tax_refunded' => null, + 'tax_percent' => 10, + 'qty_refunded' => 0, + 'discount_percent' => 100, + 'discount_amount' => 100, + 'base_discount_amount' => 100, + 'discount_invoiced' => 100, + 'base_discount_invoiced' => 100, + 'row_total' => 90.91, + 'base_row_total' => 90.91, + 'row_invoiced' => 90.91, + 'base_row_invoiced' => 90.91, + 'price_incl_tax' => 100, + 'base_price_incl_tax' => 100, + 'row_total_incl_tax' => 100, + 'base_row_total_incl_tax' => 100, + 'discount_tax_compensation_amount' => 9.09, + 'base_discount_tax_compensation_amount' => 9.09, + 'discount_tax_compensation_invoiced' => 9.09, + 'base_discount_tax_compensation_invoiced' => 9.09, + ], + 'is_last' => false, + 'qty' => 0, + ], + ], + 'is_last' => false, + 'data_fields' => [ + 'grand_total' => -9.09, + 'base_grand_total' => -9.09, + 'base_shipping_amount' => 0, + 'tax_amount' => 0, + 'base_tax_amount' => 0, + 'invoice' => new MagentoObject( + [ + 'shipping_tax_amount' => 0, + 'base_shipping_tax_amount' => 0, + 'shipping_discount_tax_compensation_amount' => 0, + 'base_shipping_discount_tax_compensation_amount' => 0, + ] + ), + ], + ], + 'expected_results' => [ + 'creditmemo_items' => [ + 'item_1' => [ + 'tax_amount' => 0, + 'base_tax_amount' => 0, + ], + 'item_2' => [ + 'tax_amount' => 0, + 'base_tax_amount' => 0, + ], + ], + 'creditmemo_data' => [ + 'grand_total' => 0, + 'base_grand_total' => 0, + 'tax_amount' => 0, + 'base_tax_amount' => 0, + 'shipping_tax_amount' => 0, + 'base_shipping_tax_amount' => 0, + ], + ], + ]; + return $result; } diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/AbstractSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/AbstractSenderTest.php index 2297d6aa711cf..2f4e0e927db2c 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/AbstractSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/AbstractSenderTest.php @@ -92,12 +92,16 @@ public function stepMockSetup() $this->storeMock = $this->createPartialMock(\Magento\Store\Model\Store::class, ['getStoreId', '__wakeup']); - $this->orderMock = $this->createPartialMock(\Magento\Sales\Model\Order::class, [ + $this->orderMock = $this->createPartialMock( + \Magento\Sales\Model\Order::class, + [ 'getStore', 'getBillingAddress', 'getPayment', '__wakeup', 'getCustomerIsGuest', 'getCustomerName', 'getCustomerEmail', 'getShippingAddress', 'setSendEmail', - 'setEmailSent' - ]); + 'setEmailSent', 'getCreatedAtFormatted', 'getIsNotVirtual', + 'getEmailCustomerNote', 'getFrontendStatusLabel' + ] + ); $this->orderMock->expects($this->any()) ->method('getStore') ->will($this->returnValue($this->storeMock)); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoCommentSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoCommentSenderTest.php index 3b97c8451d32c..40e7ce4568d20 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoCommentSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoCommentSenderTest.php @@ -59,6 +59,16 @@ public function testSendVirtualOrder() { $this->orderMock->setData(\Magento\Sales\Api\Data\OrderInterface::IS_VIRTUAL, true); $billingAddress = $this->addressMock; + $customerName = 'test customer'; + $frontendStatusLabel = 'Complete'; + + $this->orderMock->expects($this->any()) + ->method('getCustomerName') + ->willReturn($customerName); + $this->orderMock->expects($this->once()) + ->method('getFrontendStatusLabel') + ->willReturn($frontendStatusLabel); + $this->templateContainerMock->expects($this->once()) ->method('setTemplateVars') ->with( @@ -70,7 +80,11 @@ public function testSendVirtualOrder() 'billing' => $billingAddress, 'store' => $this->storeMock, 'formattedShippingAddress' => null, - 'formattedBillingAddress' => 1 + 'formattedBillingAddress' => 1, + 'order_data' => [ + 'customer_name' => $customerName, + 'frontend_status_label' => $frontendStatusLabel + ] ] ) ); @@ -83,6 +97,15 @@ public function testSendTrueWithCustomerCopy() { $billingAddress = $this->addressMock; $comment = 'comment_test'; + $customerName = 'test customer'; + $frontendStatusLabel = 'Complete'; + + $this->orderMock->expects($this->any()) + ->method('getCustomerName') + ->willReturn($customerName); + $this->orderMock->expects($this->once()) + ->method('getFrontendStatusLabel') + ->willReturn($frontendStatusLabel); $this->orderMock->expects($this->once()) ->method('getCustomerIsGuest') @@ -102,7 +125,11 @@ public function testSendTrueWithCustomerCopy() 'billing' => $billingAddress, 'store' => $this->storeMock, 'formattedShippingAddress' => 1, - 'formattedBillingAddress' => 1 + 'formattedBillingAddress' => 1, + 'order_data' => [ + 'customer_name' => $customerName, + 'frontend_status_label' => $frontendStatusLabel + ] ] ) ); @@ -115,6 +142,15 @@ public function testSendTrueWithoutCustomerCopy() { $billingAddress = $this->addressMock; $comment = 'comment_test'; + $customerName = 'test customer'; + $frontendStatusLabel = 'Complete'; + + $this->orderMock->expects($this->any()) + ->method('getCustomerName') + ->willReturn($customerName); + $this->orderMock->expects($this->once()) + ->method('getFrontendStatusLabel') + ->willReturn($frontendStatusLabel); $this->orderMock->expects($this->once()) ->method('getCustomerIsGuest') @@ -134,7 +170,11 @@ public function testSendTrueWithoutCustomerCopy() 'comment' => $comment, 'store' => $this->storeMock, 'formattedShippingAddress' => 1, - 'formattedBillingAddress' => 1 + 'formattedBillingAddress' => 1, + 'order_data' => [ + 'customer_name' => $customerName, + 'frontend_status_label' => $frontendStatusLabel + ] ] ) ); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php index 287daa2fba4b9..72a51a15db592 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/CreditmemoSenderTest.php @@ -90,6 +90,9 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema $comment = 'comment_test'; $address = 'address_test'; $configPath = 'sales_email/general/async_sending'; + $customerName = 'test customer'; + $frontendStatusLabel = 'Processing'; + $isNotVirtual = true; $this->creditmemoMock->expects($this->once()) ->method('setSendEmail') @@ -118,6 +121,22 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema ->method('getCustomerNote') ->willReturn($comment); + $this->orderMock->expects($this->any()) + ->method('getCustomerName') + ->willReturn($customerName); + + $this->orderMock->expects($this->once()) + ->method('getIsNotVirtual') + ->willReturn($isNotVirtual); + + $this->orderMock->expects($this->once()) + ->method('getEmailCustomerNote') + ->willReturn(''); + + $this->orderMock->expects($this->once()) + ->method('getFrontendStatusLabel') + ->willReturn($frontendStatusLabel); + $this->templateContainerMock->expects($this->once()) ->method('setTemplateVars') ->with( @@ -129,7 +148,13 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema 'payment_html' => 'payment', 'store' => $this->storeMock, 'formattedShippingAddress' => $address, - 'formattedBillingAddress' => $address + 'formattedBillingAddress' => $address, + 'order_data' => [ + 'customer_name' => $customerName, + 'is_not_virtual' => $isNotVirtual, + 'email_customer_note' => '', + 'frontend_status_label' => $frontendStatusLabel + ] ] ); @@ -211,9 +236,28 @@ public function sendDataProvider() public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expectedShippingAddress) { $billingAddress = 'address_test'; + $customerName = 'test customer'; + $frontendStatusLabel = 'Complete'; + $isNotVirtual = false; $this->orderMock->setData(\Magento\Sales\Api\Data\OrderInterface::IS_VIRTUAL, $isVirtualOrder); + $this->orderMock->expects($this->any()) + ->method('getCustomerName') + ->willReturn($customerName); + + $this->orderMock->expects($this->once()) + ->method('getIsNotVirtual') + ->willReturn($isNotVirtual); + + $this->orderMock->expects($this->once()) + ->method('getEmailCustomerNote') + ->willReturn(''); + + $this->orderMock->expects($this->once()) + ->method('getFrontendStatusLabel') + ->willReturn($frontendStatusLabel); + $this->creditmemoMock->expects($this->once()) ->method('setSendEmail') ->with(false); @@ -247,7 +291,14 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte 'payment_html' => 'payment', 'store' => $this->storeMock, 'formattedShippingAddress' => $expectedShippingAddress, - 'formattedBillingAddress' => $billingAddress + 'formattedBillingAddress' => $billingAddress, + 'order_data' => [ + 'customer_name' => $customerName, + 'is_not_virtual' => $isNotVirtual, + 'email_customer_note' => '', + 'frontend_status_label' => $frontendStatusLabel + ] + ] ); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceCommentSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceCommentSenderTest.php index 3e29bf04e358d..f0a05586cd972 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceCommentSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceCommentSenderTest.php @@ -63,9 +63,20 @@ public function testSendTrueWithCustomerCopy() $billingAddress = $this->addressMock; $this->stepAddressFormat($billingAddress); $comment = 'comment_test'; + $customerName = 'Test Customer'; + $frontendStatusLabel = 'Processing'; $this->orderMock->expects($this->once()) ->method('getCustomerIsGuest') ->will($this->returnValue(false)); + + $this->orderMock->expects($this->any()) + ->method('getCustomerName') + ->willReturn($customerName); + + $this->orderMock->expects($this->any()) + ->method('getFrontendStatusLabel') + ->willReturn($frontendStatusLabel); + $this->identityContainerMock->expects($this->once()) ->method('isEnabled') ->will($this->returnValue(true)); @@ -80,7 +91,11 @@ public function testSendTrueWithCustomerCopy() 'billing' => $billingAddress, 'store' => $this->storeMock, 'formattedShippingAddress' => 1, - 'formattedBillingAddress' => 1 + 'formattedBillingAddress' => 1, + 'order_data' => [ + 'customer_name' => $customerName, + 'frontend_status_label' => $frontendStatusLabel + ] ] ) ); @@ -93,12 +108,22 @@ public function testSendTrueWithCustomerCopy() public function testSendTrueWithoutCustomerCopy() { $billingAddress = $this->addressMock; + $customerName = 'Test Customer'; + $frontendStatusLabel = 'Processing'; $this->stepAddressFormat($billingAddress); $comment = 'comment_test'; $this->orderMock->expects($this->once()) ->method('getCustomerIsGuest') ->will($this->returnValue(false)); + $this->orderMock->expects($this->any()) + ->method('getCustomerName') + ->willReturn($customerName); + + $this->orderMock->expects($this->any()) + ->method('getFrontendStatusLabel') + ->willReturn($frontendStatusLabel); + $this->identityContainerMock->expects($this->once()) ->method('isEnabled') ->will($this->returnValue(true)); @@ -113,7 +138,11 @@ public function testSendTrueWithoutCustomerCopy() 'comment' => $comment, 'store' => $this->storeMock, 'formattedShippingAddress' => 1, - 'formattedBillingAddress' => 1 + 'formattedBillingAddress' => 1, + 'order_data' => [ + 'customer_name' => $customerName, + 'frontend_status_label' => $frontendStatusLabel + ] ] ) ); @@ -127,6 +156,16 @@ public function testSendVirtualOrder() $isVirtualOrder = true; $this->orderMock->setData(\Magento\Sales\Api\Data\OrderInterface::IS_VIRTUAL, $isVirtualOrder); $this->stepAddressFormat($this->addressMock, $isVirtualOrder); + $customerName = 'Test Customer'; + $frontendStatusLabel = 'Complete'; + + $this->orderMock->expects($this->any()) + ->method('getCustomerName') + ->willReturn($customerName); + + $this->orderMock->expects($this->any()) + ->method('getFrontendStatusLabel') + ->willReturn($frontendStatusLabel); $this->identityContainerMock->expects($this->once()) ->method('isEnabled') @@ -142,7 +181,11 @@ public function testSendVirtualOrder() 'comment' => '', 'store' => $this->storeMock, 'formattedShippingAddress' => null, - 'formattedBillingAddress' => 1 + 'formattedBillingAddress' => 1, + 'order_data' => [ + 'customer_name' => $customerName, + 'frontend_status_label' => $frontendStatusLabel + ] ] ) ); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php index 3315ec8eb4196..00a1855055a84 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/InvoiceSenderTest.php @@ -90,6 +90,9 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema $comment = 'comment_test'; $address = 'address_test'; $configPath = 'sales_email/general/async_sending'; + $customerName = 'Test Customer'; + $isNotVirtual = true; + $frontendStatusLabel = 'Processing'; $this->invoiceMock->expects($this->once()) ->method('setSendEmail') @@ -116,6 +119,22 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema ->method('getShippingAddress') ->willReturn($addressMock); + $this->orderMock->expects($this->any()) + ->method('getCustomerName') + ->willReturn($customerName); + + $this->orderMock->expects($this->once()) + ->method('getIsNotVirtual') + ->willReturn($isNotVirtual); + + $this->orderMock->expects($this->once()) + ->method('getEmailCustomerNote') + ->willReturn(''); + + $this->orderMock->expects($this->once()) + ->method('getFrontendStatusLabel') + ->willReturn($frontendStatusLabel); + $this->invoiceMock->expects($this->once()) ->method('getCustomerNoteNotify') ->willReturn($customerNoteNotify); @@ -135,7 +154,13 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema 'payment_html' => 'payment', 'store' => $this->storeMock, 'formattedShippingAddress' => $address, - 'formattedBillingAddress' => $address + 'formattedBillingAddress' => $address, + 'order_data' => [ + 'customer_name' => $customerName, + 'is_not_virtual' => $isNotVirtual, + 'email_customer_note' => '', + 'frontend_status_label' => $frontendStatusLabel + ] ] ); @@ -216,6 +241,9 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte { $billingAddress = 'address_test'; $this->orderMock->setData(\Magento\Sales\Api\Data\OrderInterface::IS_VIRTUAL, $isVirtualOrder); + $customerName = 'Test Customer'; + $frontendStatusLabel = 'Complete'; + $isNotVirtual = false; $this->invoiceMock->expects($this->once()) ->method('setSendEmail') @@ -238,6 +266,21 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte $this->invoiceMock->expects($this->once()) ->method('getCustomerNoteNotify') ->willReturn(false); + $this->orderMock->expects($this->any()) + ->method('getCustomerName') + ->willReturn($customerName); + + $this->orderMock->expects($this->once()) + ->method('getIsNotVirtual') + ->willReturn($isNotVirtual); + + $this->orderMock->expects($this->once()) + ->method('getEmailCustomerNote') + ->willReturn(''); + + $this->orderMock->expects($this->once()) + ->method('getFrontendStatusLabel') + ->willReturn($frontendStatusLabel); $this->templateContainerMock->expects($this->once()) ->method('setTemplateVars') @@ -250,7 +293,13 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte 'payment_html' => 'payment', 'store' => $this->storeMock, 'formattedShippingAddress' => $expectedShippingAddress, - 'formattedBillingAddress' => $billingAddress + 'formattedBillingAddress' => $billingAddress, + 'order_data' => [ + 'customer_name' => $customerName, + 'is_not_virtual' => false, + 'email_customer_note' => '', + 'frontend_status_label' => $frontendStatusLabel + ] ] ); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderCommentSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderCommentSenderTest.php index e5d6cacb25637..049cc75d3e42c 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderCommentSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderCommentSenderTest.php @@ -40,10 +40,18 @@ public function testSendTrue() { $billingAddress = $this->addressMock; $comment = 'comment_test'; + $customerName='Test Customer'; + $frontendStatusLabel='Processing'; $this->stepAddressFormat($billingAddress); $this->orderMock->expects($this->once()) ->method('getCustomerIsGuest') ->will($this->returnValue(false)); + $this->orderMock->expects($this->any()) + ->method('getCustomerName') + ->willReturn($customerName); + $this->orderMock->expects($this->once()) + ->method('getFrontendStatusLabel') + ->willReturn($frontendStatusLabel); $this->identityContainerMock->expects($this->once()) ->method('isEnabled') @@ -58,7 +66,11 @@ public function testSendTrue() 'comment' => $comment, 'store' => $this->storeMock, 'formattedShippingAddress' => 1, - 'formattedBillingAddress' => 1 + 'formattedBillingAddress' => 1, + 'order_data' => [ + 'customer_name' => $customerName, + 'frontend_status_label' => $frontendStatusLabel + ] ] ) ); @@ -72,10 +84,18 @@ public function testSendVirtualOrder() $isVirtualOrder = true; $this->orderMock->setData(\Magento\Sales\Api\Data\OrderInterface::IS_VIRTUAL, $isVirtualOrder); $this->stepAddressFormat($this->addressMock, $isVirtualOrder); + $customerName='Test Customer'; + $frontendStatusLabel='Complete'; $this->identityContainerMock->expects($this->once()) ->method('isEnabled') ->will($this->returnValue(false)); + $this->orderMock->expects($this->any()) + ->method('getCustomerName') + ->willReturn($customerName); + $this->orderMock->expects($this->once()) + ->method('getFrontendStatusLabel') + ->willReturn($frontendStatusLabel); $this->templateContainerMock->expects($this->once()) ->method('setTemplateVars') ->with( @@ -86,7 +106,11 @@ public function testSendVirtualOrder() 'billing' => $this->addressMock, 'store' => $this->storeMock, 'formattedShippingAddress' => null, - 'formattedBillingAddress' => 1 + 'formattedBillingAddress' => 1, + 'order_data' => [ + 'customer_name' => $customerName, + 'frontend_status_label' => $frontendStatusLabel + ] ] ) ); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderSenderTest.php index bfea2d63ef1bb..a033e41dd8e8b 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/OrderSenderTest.php @@ -56,11 +56,16 @@ protected function setUp() * @param $senderSendException * @return void * @dataProvider sendDataProvider + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function testSend($configValue, $forceSyncMode, $emailSendingResult, $senderSendException) { $address = 'address_test'; $configPath = 'sales_email/general/async_sending'; + $createdAtFormatted='Oct 14, 2019, 4:11:58 PM'; + $customerName = 'test customer'; + $frontendStatusLabel = 'Processing'; + $isNotVirtual = true; $this->orderMock->expects($this->once()) ->method('setSendEmail') @@ -96,6 +101,27 @@ public function testSend($configValue, $forceSyncMode, $emailSendingResult, $sen ->method('getShippingAddress') ->willReturn($addressMock); + $this->orderMock->expects($this->once()) + ->method('getCreatedAtFormatted') + ->with(2) + ->willReturn($createdAtFormatted); + + $this->orderMock->expects($this->any()) + ->method('getCustomerName') + ->willReturn($customerName); + + $this->orderMock->expects($this->once()) + ->method('getIsNotVirtual') + ->willReturn($isNotVirtual); + + $this->orderMock->expects($this->once()) + ->method('getEmailCustomerNote') + ->willReturn(''); + + $this->orderMock->expects($this->once()) + ->method('getFrontendStatusLabel') + ->willReturn($frontendStatusLabel); + $this->templateContainerMock->expects($this->once()) ->method('setTemplateVars') ->with( @@ -105,7 +131,15 @@ public function testSend($configValue, $forceSyncMode, $emailSendingResult, $sen 'payment_html' => 'payment', 'store' => $this->storeMock, 'formattedShippingAddress' => $address, - 'formattedBillingAddress' => $address + 'formattedBillingAddress' => $address, + 'created_at_formatted'=>$createdAtFormatted, + 'order_data' => [ + 'customer_name' => $customerName, + 'is_not_virtual' => $isNotVirtual, + 'email_customer_note' => '', + 'frontend_status_label' => $frontendStatusLabel + ] + ] ); @@ -204,6 +238,10 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte { $address = 'address_test'; $this->orderMock->setData(\Magento\Sales\Api\Data\OrderInterface::IS_VIRTUAL, $isVirtualOrder); + $createdAtFormatted='Oct 14, 2019, 4:11:58 PM'; + $customerName = 'test customer'; + $frontendStatusLabel = 'Complete'; + $isNotVirtual = false; $this->orderMock->expects($this->once()) ->method('setSendEmail') @@ -231,6 +269,27 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte $this->stepAddressFormat($addressMock, $isVirtualOrder); + $this->orderMock->expects($this->once()) + ->method('getCreatedAtFormatted') + ->with(2) + ->willReturn($createdAtFormatted); + + $this->orderMock->expects($this->any()) + ->method('getCustomerName') + ->willReturn($customerName); + + $this->orderMock->expects($this->once()) + ->method('getIsNotVirtual') + ->willReturn($isNotVirtual); + + $this->orderMock->expects($this->once()) + ->method('getEmailCustomerNote') + ->willReturn(''); + + $this->orderMock->expects($this->once()) + ->method('getFrontendStatusLabel') + ->willReturn($frontendStatusLabel); + $this->templateContainerMock->expects($this->once()) ->method('setTemplateVars') ->with( @@ -240,7 +299,14 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte 'payment_html' => 'payment', 'store' => $this->storeMock, 'formattedShippingAddress' => $expectedShippingAddress, - 'formattedBillingAddress' => $address + 'formattedBillingAddress' => $address, + 'created_at_formatted'=>$createdAtFormatted, + 'order_data' => [ + 'customer_name' => $customerName, + 'is_not_virtual' => $isNotVirtual, + 'email_customer_note' => '', + 'frontend_status_label' => $frontendStatusLabel + ] ] ); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentCommentSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentCommentSenderTest.php index f5a2e4d0148cd..90664216e87bc 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentCommentSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentCommentSenderTest.php @@ -56,6 +56,8 @@ public function testSendTrueWithCustomerCopy() { $billingAddress = $this->addressMock; $comment = 'comment_test'; + $customerName='Test Customer'; + $frontendStatusLabel='Processing'; $this->orderMock->expects($this->once()) ->method('getCustomerIsGuest') @@ -65,6 +67,12 @@ public function testSendTrueWithCustomerCopy() $this->identityContainerMock->expects($this->once()) ->method('isEnabled') ->will($this->returnValue(true)); + $this->orderMock->expects($this->any()) + ->method('getCustomerName') + ->willReturn($customerName); + $this->orderMock->expects($this->once()) + ->method('getFrontendStatusLabel') + ->willReturn($frontendStatusLabel); $this->templateContainerMock->expects($this->once()) ->method('setTemplateVars') ->with( @@ -76,7 +84,11 @@ public function testSendTrueWithCustomerCopy() 'comment' => $comment, 'store' => $this->storeMock, 'formattedShippingAddress' => 1, - 'formattedBillingAddress' => 1 + 'formattedBillingAddress' => 1, + 'order_data' => [ + 'customer_name' => $customerName, + 'frontend_status_label' => $frontendStatusLabel + ] ] ) ); @@ -89,6 +101,8 @@ public function testSendTrueWithoutCustomerCopy() { $billingAddress = $this->addressMock; $comment = 'comment_test'; + $customerName='Test Customer'; + $frontendStatusLabel='Processing'; $this->orderMock->expects($this->once()) ->method('getCustomerIsGuest') @@ -98,6 +112,12 @@ public function testSendTrueWithoutCustomerCopy() $this->identityContainerMock->expects($this->once()) ->method('isEnabled') ->will($this->returnValue(true)); + $this->orderMock->expects($this->any()) + ->method('getCustomerName') + ->willReturn($customerName); + $this->orderMock->expects($this->once()) + ->method('getFrontendStatusLabel') + ->willReturn($frontendStatusLabel); $this->templateContainerMock->expects($this->once()) ->method('setTemplateVars') ->with( @@ -109,7 +129,11 @@ public function testSendTrueWithoutCustomerCopy() 'comment' => $comment, 'store' => $this->storeMock, 'formattedShippingAddress' => 1, - 'formattedBillingAddress' => 1 + 'formattedBillingAddress' => 1, + 'order_data' => [ + 'customer_name' => $customerName, + 'frontend_status_label' => $frontendStatusLabel + ] ] ) ); @@ -123,10 +147,18 @@ public function testSendVirtualOrder() $isVirtualOrder = true; $this->orderMock->setData(\Magento\Sales\Api\Data\OrderInterface::IS_VIRTUAL, $isVirtualOrder); $this->stepAddressFormat($this->addressMock, $isVirtualOrder); + $customerName='Test Customer'; + $frontendStatusLabel='Complete'; $this->identityContainerMock->expects($this->once()) ->method('isEnabled') ->will($this->returnValue(false)); + $this->orderMock->expects($this->any()) + ->method('getCustomerName') + ->willReturn($customerName); + $this->orderMock->expects($this->once()) + ->method('getFrontendStatusLabel') + ->willReturn($frontendStatusLabel); $this->templateContainerMock->expects($this->once()) ->method('setTemplateVars') ->with( @@ -138,7 +170,12 @@ public function testSendVirtualOrder() 'comment' => '', 'store' => $this->storeMock, 'formattedShippingAddress' => null, - 'formattedBillingAddress' => 1 + 'formattedBillingAddress' => 1, + 'order_data' => [ + 'customer_name' => $customerName, + 'frontend_status_label' => $frontendStatusLabel + ] + ] ) ); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php index 96bbb1aea7abd..dc6fc53e5ec43 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php @@ -90,6 +90,9 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema $comment = 'comment_test'; $address = 'address_test'; $configPath = 'sales_email/general/async_sending'; + $customerName = 'Test Customer'; + $isNotVirtual = true; + $frontendStatusLabel = 'Processing'; $this->shipmentMock->expects($this->once()) ->method('setSendEmail') @@ -124,6 +127,22 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema ->method('getCustomerNote') ->willReturn($comment); + $this->orderMock->expects($this->any()) + ->method('getCustomerName') + ->willReturn($customerName); + + $this->orderMock->expects($this->once()) + ->method('getIsNotVirtual') + ->willReturn($isNotVirtual); + + $this->orderMock->expects($this->once()) + ->method('getEmailCustomerNote') + ->willReturn(''); + + $this->orderMock->expects($this->once()) + ->method('getFrontendStatusLabel') + ->willReturn($frontendStatusLabel); + $this->templateContainerMock->expects($this->once()) ->method('setTemplateVars') ->with( @@ -135,7 +154,13 @@ public function testSend($configValue, $forceSyncMode, $customerNoteNotify, $ema 'payment_html' => 'payment', 'store' => $this->storeMock, 'formattedShippingAddress' => $address, - 'formattedBillingAddress' => $address + 'formattedBillingAddress' => $address, + 'order_data' => [ + 'customer_name' => $customerName, + 'is_not_virtual' => $isNotVirtual, + 'email_customer_note' => '', + 'frontend_status_label' => $frontendStatusLabel + ] ] ); @@ -216,6 +241,9 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte { $address = 'address_test'; $this->orderMock->setData(\Magento\Sales\Api\Data\OrderInterface::IS_VIRTUAL, $isVirtualOrder); + $customerName = 'Test Customer'; + $frontendStatusLabel = 'Complete'; + $isNotVirtual = false; $this->shipmentMock->expects($this->once()) ->method('setSendEmail') @@ -239,6 +267,22 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte ->method('getCustomerNoteNotify') ->willReturn(false); + $this->orderMock->expects($this->any()) + ->method('getCustomerName') + ->willReturn($customerName); + + $this->orderMock->expects($this->once()) + ->method('getIsNotVirtual') + ->willReturn($isNotVirtual); + + $this->orderMock->expects($this->once()) + ->method('getEmailCustomerNote') + ->willReturn(''); + + $this->orderMock->expects($this->once()) + ->method('getFrontendStatusLabel') + ->willReturn($frontendStatusLabel); + $this->templateContainerMock->expects($this->once()) ->method('setTemplateVars') ->with( @@ -250,7 +294,13 @@ public function testSendVirtualOrder($isVirtualOrder, $formatCallCount, $expecte 'payment_html' => 'payment', 'store' => $this->storeMock, 'formattedShippingAddress' => $expectedShippingAddress, - 'formattedBillingAddress' => $address + 'formattedBillingAddress' => $address, + 'order_data' => [ + 'customer_name' => $customerName, + 'is_not_virtual' => false, + 'email_customer_note' => '', + 'frontend_status_label' => $frontendStatusLabel + ] ] ); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/RegisterCaptureNotificationCommandTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/RegisterCaptureNotificationCommandTest.php index 1b762fafe0b71..c5c333e55a551 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/RegisterCaptureNotificationCommandTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Payment/State/RegisterCaptureNotificationCommandTest.php @@ -80,6 +80,22 @@ public function commandResultDataProvider() $this->newOrderStatus, 'Registered notification about captured amount of %1.', ], + [ + false, + false, + Order::STATE_NEW, + Order::STATE_PROCESSING, + $this->newOrderStatus, + 'Registered notification about captured amount of %1.', + ], + [ + false, + false, + Order::STATE_PENDING_PAYMENT, + Order::STATE_PROCESSING, + $this->newOrderStatus, + 'Registered notification about captured amount of %1.', + ], [ true, false, diff --git a/app/code/Magento/Sales/ViewModel/CreditMemo/Create/UpdateTotalsButton.php b/app/code/Magento/Sales/ViewModel/CreditMemo/Create/UpdateTotalsButton.php new file mode 100644 index 0000000000000..707f5ef363f66 --- /dev/null +++ b/app/code/Magento/Sales/ViewModel/CreditMemo/Create/UpdateTotalsButton.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Sales\ViewModel\CreditMemo\Create; + +use Magento\Backend\Block\Widget\Button; +use Magento\Framework\View\Element\BlockInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\Sales\Block\Adminhtml\Order\Creditmemo\Create\Items; + +/** + * View model to add Update Totals button for new Credit Memo + */ +class UpdateTotalsButton implements \Magento\Framework\View\Element\Block\ArgumentInterface +{ + /** + * @var LayoutInterface + */ + private $layout; + + /** + * @var Items + */ + private $items; + + /** + * @param LayoutInterface $layout + * @param Items $items + */ + public function __construct( + LayoutInterface $layout, + Items $items + ) { + $this->layout = $layout; + $this->items = $items; + } + + /** + * Get Update Totals block html. + * + * @return string + */ + public function getUpdateTotalsButton(): string + { + $block = $this->createUpdateTotalsBlock(); + + return $block->toHtml(); + } + + /** + * Create Update Totals block. + * + * @return BlockInterface + */ + private function createUpdateTotalsBlock(): BlockInterface + { + $onclick = "submitAndReloadArea($('creditmemo_item_container'),'" . $this->items->getUpdateUrl() . "')"; + $block = $this->layout->addBlock(Button::class, 'update_totals_button', 'order_items'); + $block->setData( + [ + 'label' => __('Update Totals'), + 'class' => 'update-totals-button secondary', + 'onclick' => $onclick, + ] + ); + + return $block; + } +} diff --git a/app/code/Magento/Sales/etc/adminhtml/system.xml b/app/code/Magento/Sales/etc/adminhtml/system.xml index d1e5680b883c2..473d3068d69c7 100644 --- a/app/code/Magento/Sales/etc/adminhtml/system.xml +++ b/app/code/Magento/Sales/etc/adminhtml/system.xml @@ -93,12 +93,12 @@ <field id="amount" translate="label comment" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Minimum Amount</label> <validate>validate-number validate-greater-than-zero</validate> - <comment>Subtotal after discount</comment> + <comment>Subtotal after discount.</comment> </field> <field id="include_discount_amount" translate="label" sortOrder="12" type="select" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Include Discount Amount</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <comment>Choosing yes will be used subtotal after discount, otherwise only subtotal will be used</comment> + <comment>Choosing yes will be used subtotal after discount, otherwise only subtotal will be used.</comment> </field> <field id="tax_including" translate="label" sortOrder="15" type="select" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Include Tax to Amount</label> @@ -182,7 +182,7 @@ </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Order Email Copy To</label> - <comment>Comma-separated</comment> + <comment>Comma-separated.</comment> <validate>validate-emails</validate> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> @@ -212,7 +212,7 @@ </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Order Comment Email Copy To</label> - <comment>Comma-separated</comment> + <comment>Comma-separated.</comment> <validate>validate-emails</validate> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> @@ -242,7 +242,7 @@ </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Invoice Email Copy To</label> - <comment>Comma-separated</comment> + <comment>Comma-separated.</comment> <validate>validate-emails</validate> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> @@ -272,7 +272,7 @@ </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Invoice Comment Email Copy To</label> - <comment>Comma-separated</comment> + <comment>Comma-separated.</comment> <validate>validate-emails</validate> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> @@ -302,7 +302,7 @@ </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Shipment Email Copy To</label> - <comment>Comma-separated</comment> + <comment>Comma-separated.</comment> <validate>validate-emails</validate> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> @@ -332,7 +332,7 @@ </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Shipment Comment Email Copy To</label> - <comment>Comma-separated</comment> + <comment>Comma-separated.</comment> <validate>validate-emails</validate> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> @@ -362,7 +362,7 @@ </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Credit Memo Email Copy To</label> - <comment>Comma-separated</comment> + <comment>Comma-separated.</comment> <validate>validate-emails</validate> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> @@ -392,7 +392,7 @@ </field> <field id="copy_to" translate="label comment" type="text" sortOrder="4" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Send Credit Memo Comment Email Copy To</label> - <comment>Comma-separated</comment> + <comment>Comma-separated.</comment> <validate>validate-emails</validate> </field> <field id="copy_method" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> diff --git a/app/code/Magento/Sales/etc/db_schema.xml b/app/code/Magento/Sales/etc/db_schema.xml index eb508af8daf25..ea7c768b0a786 100644 --- a/app/code/Magento/Sales/etc/db_schema.xml +++ b/app/code/Magento/Sales/etc/db_schema.xml @@ -1386,8 +1386,6 @@ comment="Adjustment Negative"/> <column xsi:type="decimal" name="order_base_grand_total" scale="4" precision="20" unsigned="false" nullable="true" comment="Order Grand Total"/> - <column xsi:type="varchar" name="order_currency_code" nullable="true" length="3" comment="Order Currency Code"/> - <column xsi:type="varchar" name="base_currency_code" nullable="true" length="3" comment="Base Currency Code"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> </constraint> diff --git a/app/code/Magento/Sales/etc/db_schema_whitelist.json b/app/code/Magento/Sales/etc/db_schema_whitelist.json index a7215d08c1f10..087fe6c9eb5ac 100644 --- a/app/code/Magento/Sales/etc/db_schema_whitelist.json +++ b/app/code/Magento/Sales/etc/db_schema_whitelist.json @@ -815,9 +815,7 @@ "shipping_and_handling": true, "adjustment_positive": true, "adjustment_negative": true, - "order_base_grand_total": true, - "order_currency_code": true, - "base_currency_code": true + "order_base_grand_total": true }, "index": { "SALES_CREDITMEMO_GRID_ORDER_INCREMENT_ID": true, @@ -1247,4 +1245,4 @@ "SALES_ORDER_STATUS_LABEL_STORE_ID_STORE_STORE_ID": true } } -} \ No newline at end of file +} diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index a0dbb0488acb3..f6618c9884d60 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -641,8 +641,6 @@ <item name="adjustment_positive" xsi:type="string">sales_creditmemo.adjustment_positive</item> <item name="adjustment_negative" xsi:type="string">sales_creditmemo.adjustment_negative</item> <item name="order_base_grand_total" xsi:type="string">sales_order.base_grand_total</item> - <item name="order_currency_code" xsi:type="string">sales_order.order_currency_code</item> - <item name="base_currency_code" xsi:type="string">sales_order.base_currency_code</item> </argument> </arguments> </virtualType> diff --git a/app/code/Magento/Sales/etc/module.xml b/app/code/Magento/Sales/etc/module.xml index e8af6b761a8a4..11eebaa3d5a3d 100644 --- a/app/code/Magento/Sales/etc/module.xml +++ b/app/code/Magento/Sales/etc/module.xml @@ -6,7 +6,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_Sales"> + <module name="Magento_Sales" > <sequence> <module name="Magento_Rule"/> <module name="Magento_Catalog"/> diff --git a/app/code/Magento/Sales/etc/webapi.xml b/app/code/Magento/Sales/etc/webapi.xml index 492dff8057039..63d7c24ecd16f 100644 --- a/app/code/Magento/Sales/etc/webapi.xml +++ b/app/code/Magento/Sales/etc/webapi.xml @@ -253,7 +253,7 @@ <resource ref="Magento_Sales::ship" /> </resources> </route> - <route url="/V1/orders/" method="POST"> + <route url="/V1/orders" method="POST"> <service class="Magento\Sales\Api\OrderRepositoryInterface" method="save"/> <resources> <resource ref="Magento_Sales::create" /> diff --git a/app/code/Magento/Sales/i18n/en_US.csv b/app/code/Magento/Sales/i18n/en_US.csv index d09ee8376b2ed..f30315437533f 100644 --- a/app/code/Magento/Sales/i18n/en_US.csv +++ b/app/code/Magento/Sales/i18n/en_US.csv @@ -797,5 +797,5 @@ Created,Created Refunds,Refunds "Allow Zero GrandTotal for Creditmemo","Allow Zero GrandTotal for Creditmemo" "Allow Zero GrandTotal","Allow Zero GrandTotal" +"Could not save the shipment tracking","Could not save the shipment tracking" "Please enter a coupon code!","Please enter a coupon code!" - diff --git a/app/code/Magento/Sales/registration.php b/app/code/Magento/Sales/registration.php index db3b2aebce0bb..6945521f30abe 100644 --- a/app/code/Magento/Sales/registration.php +++ b/app/code/Magento/Sales/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Sales', __DIR__); diff --git a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_new.xml b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_new.xml index 71490553aff17..cd7ca1d7e0d42 100644 --- a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_new.xml +++ b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_new.xml @@ -18,6 +18,9 @@ </block> <block class="Magento\Sales\Block\Adminhtml\Order\Payment" name="order_payment"/> <block class="Magento\Sales\Block\Adminhtml\Order\Creditmemo\Create\Items" name="order_items" template="Magento_Sales::order/creditmemo/create/items.phtml"> + <arguments> + <argument name="viewModel" xsi:type="object">Magento\Sales\ViewModel\CreditMemo\Create\UpdateTotalsButton</argument> + </arguments> <block class="Magento\Sales\Block\Adminhtml\Items\Renderer\DefaultRenderer" name="order_items.default" as="default" template="Magento_Sales::order/creditmemo/create/items/renderer/default.phtml"/> <block class="Magento\Sales\Block\Adminhtml\Items\Column\Qty" name="column_qty" template="Magento_Sales::items/column/qty.phtml" group="column"/> <block class="Magento\Sales\Block\Adminhtml\Items\Column\Name" name="column_name" template="Magento_Sales::items/column/name.phtml" group="column"/> diff --git a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_updateqty.xml b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_updateqty.xml index 8375bec965794..94ef0bf9d7a03 100644 --- a/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_updateqty.xml +++ b/app/code/Magento/Sales/view/adminhtml/layout/sales_order_creditmemo_updateqty.xml @@ -9,6 +9,9 @@ <update handle="sales_order_item_price"/> <body> <block class="Magento\Sales\Block\Adminhtml\Order\Creditmemo\Create\Items" name="order_items" template="Magento_Sales::order/creditmemo/create/items.phtml"> + <arguments> + <argument name="viewModel" xsi:type="object">Magento\Sales\ViewModel\CreditMemo\Create\UpdateTotalsButton</argument> + </arguments> <block class="Magento\Sales\Block\Adminhtml\Items\Renderer\DefaultRenderer" name="order_items.default" as="default" template="Magento_Sales::order/creditmemo/create/items/renderer/default.phtml"/> <block class="Magento\Sales\Block\Adminhtml\Items\Column\Qty" name="column_qty" template="Magento_Sales::items/column/qty.phtml" group="column"/> <block class="Magento\Sales\Block\Adminhtml\Items\Column\Name" name="column_name" template="Magento_Sales::items/column/name.phtml" group="column"/> diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/address/form.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/address/form.phtml index a7f3b3c1cc8f5..0cc23056b3c2f 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/address/form.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/address/form.phtml @@ -4,13 +4,19 @@ * See COPYING.txt for license details. */ ?> -<div class="message message-notice"> - <div class="message-inner"> - <div class="message-content"><?= $block->escapeHtml(__('Changing address information will not recalculate shipping, tax or other order amount.')) ?></div> +<div class="messages"> + <div class="message message-notice"> + <div class="message-inner"> + <div class="message-content"> + <?= $block->escapeHtml( + __('Changing address information will not recalculate shipping, tax or other order amount.') + ) ?> + </div> + </div> </div> </div> -<fieldset class="fieldset admin__fieldset-wrapper"> +<div class="fieldset admin__fieldset-wrapper"> <legend class="legend admin__legend"> <span><?= $block->escapeHtml($block->getHeaderText()) ?></span> </legend> @@ -18,4 +24,4 @@ <div class="form-inline" data-mage-init='{"Magento_Sales/order/edit/address/form":{}}'> <?= $block->getForm()->toHtml() ?> </div> -</fieldset> +</div> diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/items.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/items.phtml index 9e0d203cd56bf..81dc778cff2df 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/items.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/creditmemo/create/items.phtml @@ -6,7 +6,11 @@ /* @var \Magento\Sales\Block\Adminhtml\Order\Creditmemo\Create\Items $block */ ?> -<?php $_items = $block->getCreditmemo()->getAllItems() ?> +<?php +/** @var Magento\Sales\ViewModel\CreditMemo\Create\UpdateTotalsButton $viewModel */ +$viewModel = $block->getData('viewModel'); +$_items = $block->getCreditmemo()->getAllItems(); +?> <section class="admin__page-section"> <div class="admin__page-section-title"> @@ -100,6 +104,7 @@ <span class="title"><?= $block->escapeHtml(__('Refund Totals')) ?></span> </div> <?= $block->getChildHtml('creditmemo_totals') ?> + <div class="totals-actions"><?= /* @noEscape */ $viewModel->getUpdateTotalsButton() ?></div> <div class="order-totals-actions"> <div class="field choice admin__field admin__field-option field-append-comments"> <input id="notify_customer" @@ -139,8 +144,8 @@ require(['jquery'], function(jQuery){ //<![CDATA[ var submitButtons = jQuery('.submit-button'); -var updateButtons = jQuery('.update-button'); -var fields = jQuery('.qty-input'); +var updateButtons = jQuery('.update-button, .update-totals-button'); +var fields = jQuery('.qty-input, .order-subtotal-table input[type="text"]'); function enableButtons(buttons) { buttons.removeClass('disabled').prop('disabled', false); } diff --git a/app/code/Magento/Sales/view/adminhtml/ui_component/sales_order_creditmemo_grid.xml b/app/code/Magento/Sales/view/adminhtml/ui_component/sales_order_creditmemo_grid.xml index 98f8b1edecf34..e0b7dae8fdb1a 100644 --- a/app/code/Magento/Sales/view/adminhtml/ui_component/sales_order_creditmemo_grid.xml +++ b/app/code/Magento/Sales/view/adminhtml/ui_component/sales_order_creditmemo_grid.xml @@ -119,7 +119,7 @@ <column name="base_grand_total" class="Magento\Sales\Ui\Component\Listing\Column\Price"> <settings> <filter>textRange</filter> - <label translate="true">Refunded (Base)</label> + <label translate="true">Refunded</label> </settings> </column> <column name="order_status" component="Magento_Ui/js/grid/columns/select"> @@ -194,7 +194,7 @@ <visible>false</visible> </settings> </column> - <column name="subtotal" class="Magento\Sales\Ui\Component\Listing\Column\PurchasedPrice"> + <column name="subtotal" class="Magento\Sales\Ui\Component\Listing\Column\Price"> <settings> <filter>textRange</filter> <label translate="true">Subtotal</label> diff --git a/app/code/Magento/Sales/view/adminhtml/ui_component/sales_order_view_invoice_grid.xml b/app/code/Magento/Sales/view/adminhtml/ui_component/sales_order_view_invoice_grid.xml index c0ed1e01460bc..ac1233c5e4961 100644 --- a/app/code/Magento/Sales/view/adminhtml/ui_component/sales_order_view_invoice_grid.xml +++ b/app/code/Magento/Sales/view/adminhtml/ui_component/sales_order_view_invoice_grid.xml @@ -130,7 +130,7 @@ <label translate="true">Status</label> </settings> </column> - <column name="base_grand_total" class="Magento\Sales\Ui\Component\Listing\Column\Price"> + <column name="grand_total" class="Magento\Sales\Ui\Component\Listing\Column\PurchasedPrice"> <settings> <filter>textRange</filter> <label translate="true">Amount</label> diff --git a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js index 4e07414510748..e138112ac3f5a 100644 --- a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js +++ b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js @@ -19,17 +19,17 @@ define([ window.AdminOrder = new Class.create(); AdminOrder.prototype = { - initialize : function(data){ - if(!data) data = {}; - this.loadBaseUrl = false; - this.customerId = data.customer_id ? data.customer_id : false; - this.storeId = data.store_id ? data.store_id : false; - this.quoteId = data['quote_id'] ? data['quote_id'] : false; - this.currencyId = false; + initialize: function (data) { + if (!data) data = {}; + this.loadBaseUrl = false; + this.customerId = data.customer_id ? data.customer_id : false; + this.storeId = data.store_id ? data.store_id : false; + this.quoteId = data['quote_id'] ? data['quote_id'] : false; + this.currencyId = false; this.currencySymbol = data.currency_symbol ? data.currency_symbol : ''; - this.addresses = data.addresses ? data.addresses : $H({}); + this.addresses = data.addresses ? data.addresses : $H({}); this.shippingAsBilling = data.shippingAsBilling ? data.shippingAsBilling : false; - this.gridProducts = $H({}); + this.gridProducts = $H({}); this.gridProductsGift = $H({}); this.billingAddressContainer = ''; this.shippingAddressContainer = ''; @@ -56,10 +56,10 @@ define([ } }); - jQuery.async('#order-items', (function(){ + jQuery.async('#order-items', (function () { this.dataArea = new OrderFormArea('data', $(this.getAreaId('data')), this); this.itemsArea = Object.extend(new OrderFormArea('items', $(this.getAreaId('items')), this), { - addControlButton: function(button){ + addControlButton: function (button) { var controlButtonArea = $(this.node).select('.actions')[0]; if (typeof controlButtonArea != 'undefined') { var buttons = controlButtonArea.childElements(); @@ -76,7 +76,7 @@ define([ var searchButtonId = 'add_products', searchButton = new ControlButton(jQuery.mage.__('Add Products'), searchButtonId), searchAreaId = this.getAreaId('search'); - searchButton.onClick = function() { + searchButton.onClick = function () { $(searchAreaId).show(); var el = this; window.setTimeout(function () { @@ -85,13 +85,13 @@ define([ }; if (jQuery('#' + this.getAreaId('items')).is(':visible')) { - this.dataArea.onLoad = this.dataArea.onLoad.wrap(function(proceed) { + this.dataArea.onLoad = this.dataArea.onLoad.wrap(function (proceed) { proceed(); this._parent.itemsArea.setNode($(this._parent.getAreaId('items'))); this._parent.itemsArea.onLoad(); }); - this.itemsArea.onLoad = this.itemsArea.onLoad.wrap(function(proceed) { + this.itemsArea.onLoad = this.itemsArea.onLoad.wrap(function (proceed) { proceed(); if ($(searchAreaId) && !$(searchAreaId).visible() && !$(searchButtonId)) { this.addControlButton(searchButton); @@ -103,35 +103,35 @@ define([ }).bind(this)); jQuery('#edit_form') - .on('submitOrder', function(){ + .on('submitOrder', function () { jQuery(this).trigger('realOrder'); }) .on('realOrder', this._realSubmit.bind(this)); }, - areasLoaded: function(){ + areasLoaded: function () { }, - itemsLoaded: function(){ + itemsLoaded: function () { }, - dataLoaded: function(){ + dataLoaded: function () { this.dataShow(); }, - setLoadBaseUrl : function(url){ + setLoadBaseUrl: function (url) { this.loadBaseUrl = url; }, - setAddresses : function(addresses){ + setAddresses: function (addresses) { this.addresses = addresses; }, - addExcludedPaymentMethod : function(method){ + addExcludedPaymentMethod: function (method) { this.excludedPaymentMethods.push(method); }, - setCustomerId : function(id){ + setCustomerId: function (id) { this.customerId = id; this.loadArea('header', true); $(this.getAreaId('header')).callback = 'setCustomerAfter'; @@ -139,18 +139,17 @@ define([ $('reset_order_top_button').show(); }, - setCustomerAfter : function () { + setCustomerAfter: function () { this.customerSelectorHide(); if (this.storeId) { $(this.getAreaId('data')).callback = 'dataLoaded'; this.loadArea(['data'], true); - } - else { + } else { this.storeSelectorShow(); } }, - setStoreId : function(id){ + setStoreId: function (id) { this.storeId = id; this.storeSelectorHide(); this.sidebarShow(); @@ -159,19 +158,18 @@ define([ this.loadArea(['header', 'data'], true); }, - setCurrencyId : function(id){ + setCurrencyId: function (id) { this.currencyId = id; //this.loadArea(['sidebar', 'data'], true); this.loadArea(['data'], true); }, - setCurrencySymbol : function(symbol){ + setCurrencySymbol: function (symbol) { this.currencySymbol = symbol; }, - selectAddress : function(el, container){ - - id = el.value; + selectAddress: function (el, container) { + var id = el.value; if (id.length == 0) { id = '0'; } @@ -190,10 +188,9 @@ define([ this.resetPaymentMethod(); if (this.isShippingField(container) && !this.isShippingMethodReseted) { this.resetShippingMethod(data); - } else{ + } else { this.saveData(data); } - }, /** @@ -325,13 +322,13 @@ define([ }); }, - fillAddressFields: function(container, data){ + fillAddressFields: function (container, data) { var regionIdElem = false; var regionIdElemValue = false; var fields = $(container).select('input', 'select', 'textarea'); var re = /[^\[]*\[[^\]]*\]\[([^\]]*)\](\[(\d)\])?/; - for(var i=0;i<fields.length;i++){ + for (var i = 0; i < fields.length; i++) { // skip input type file @Security error code: 1000 if (fields[i].tagName.toLowerCase() == 'input' && fields[i].type.toLowerCase() == 'file') { continue; @@ -343,9 +340,9 @@ define([ var name = matchRes[1]; var index = matchRes[3]; - if (index){ + if (index) { // multiply line - if (data[name]){ + if (data[name]) { var values = data[name].split("\n"); fields[i].value = values[index] ? values[index] : ''; } else { @@ -370,7 +367,7 @@ define([ fields[i].changeUpdater(); } - if (name == 'region' && data['region_id'] && !data['region']){ + if (name == 'region' && data['region_id'] && !data['region']) { fields[i].value = data['region_id']; } @@ -378,7 +375,7 @@ define([ } }, - disableShippingAddress : function(flag) { + disableShippingAddress: function (flag) { this.shippingAsBilling = flag; if ($('order-shipping_address_customer_address_id')) { $('order-shipping_address_customer_address_id').disabled = flag; @@ -388,7 +385,7 @@ define([ for (var i = 0; i < dataFields.length; i++) { dataFields[i].disabled = flag; - if(this.isOnlyVirtualProduct) { + if (this.isOnlyVirtualProduct) { dataFields[i].setValue(''); } } @@ -456,7 +453,7 @@ define([ return false; }, - setShippingMethod: function(method) { + setShippingMethod: function (method) { var data = {}; data['order[shipping_method]'] = method; @@ -473,20 +470,15 @@ define([ * * @return boolean */ - loadPaymentMethods: function() { + loadPaymentMethods: function () { var data = this.serializeData(this.billingAddressContainer).toObject(); - this.loadArea(['billing_method','totals'], true, data); + this.loadArea(['billing_method', 'totals'], true, data); return false; }, switchPaymentMethod: function(method){ - jQuery('#edit_form') - .off('submitOrder') - .on('submitOrder', function(){ - jQuery(this).trigger('realOrder'); - }); jQuery('#edit_form').trigger('changePaymentMethod', [method]); this.setPaymentMethod(method); var data = {}; @@ -494,35 +486,35 @@ define([ this.loadArea(['card_validation'], true, data); }, - setPaymentMethod : function(method){ - if (this.paymentMethod && $('payment_form_'+this.paymentMethod)) { - var form = 'payment_form_'+this.paymentMethod; - [form + '_before', form, form + '_after'].each(function(el) { + setPaymentMethod: function (method) { + if (this.paymentMethod && $('payment_form_' + this.paymentMethod)) { + var form = 'payment_form_' + this.paymentMethod; + [form + '_before', form, form + '_after'].each(function (el) { var block = $(el); if (block) { block.hide(); - block.select('input', 'select', 'textarea').each(function(field) { + block.select('input', 'select', 'textarea').each(function (field) { field.disabled = true; }); } }); } - if(!this.paymentMethod || method){ - $('order-billing_method_form').select('input', 'select', 'textarea').each(function(elem){ - if(elem.type != 'radio') elem.disabled = true; + if (!this.paymentMethod || method) { + $('order-billing_method_form').select('input', 'select', 'textarea').each(function (elem) { + if (elem.type != 'radio') elem.disabled = true; }) } - if ($('payment_form_'+method)){ + if ($('payment_form_' + method)) { jQuery('#' + this.getAreaId('billing_method')).trigger('contentUpdated'); this.paymentMethod = method; - var form = 'payment_form_'+method; - [form + '_before', form, form + '_after'].each(function(el) { + var form = 'payment_form_' + method; + [form + '_before', form, form + '_after'].each(function (el) { var block = $(el); if (block) { block.show(); - block.select('input', 'select', 'textarea').each(function(field) { + block.select('input', 'select', 'textarea').each(function (field) { field.disabled = false; if (!el.include('_before') && !el.include('_after') && !field.bindChange) { field.bindChange = true; @@ -530,15 +522,15 @@ define([ field.method = method; field.observe('change', this.changePaymentData.bind(this)) } - },this); + }, this); } - },this); + }, this); } }, - changePaymentData : function(event){ + changePaymentData: function (event) { var elem = Event.element(event); - if(elem && elem.method){ + if (elem && elem.method) { var data = this.getPaymentData(elem.method); if (data) { this.loadArea(['card_validation'], true, data); @@ -548,8 +540,8 @@ define([ } }, - getPaymentData : function(currentMethod){ - if (typeof(currentMethod) == 'undefined') { + getPaymentData: function (currentMethod) { + if (typeof (currentMethod) == 'undefined') { if (this.paymentMethod) { currentMethod = this.paymentMethod; } else { @@ -561,7 +553,7 @@ define([ } var data = {}; var fields = $('payment_form_' + currentMethod).select('input', 'select'); - for(var i=0;i<fields.length;i++){ + for (var i = 0; i < fields.length; i++) { data[fields[i].name] = fields[i].getValue(); } if ((typeof data['payment[cc_type]']) != 'undefined' && (!data['payment[cc_type]'] || !data['payment[cc_number]'])) { @@ -570,35 +562,41 @@ define([ return data; }, - applyCoupon : function(code){ - this.loadArea(['items', 'shipping_method', 'totals', 'billing_method'], true, {'order[coupon][code]':code, reset_shipping: 0}); + applyCoupon: function (code) { + this.loadArea(['items', 'shipping_method', 'totals', 'billing_method'], true, { + 'order[coupon][code]': code, + reset_shipping: 0 + }); this.orderItemChanged = false; jQuery('html, body').animate({ scrollTop: 0 }); }, - addProduct : function(id){ - this.loadArea(['items', 'shipping_method', 'totals', 'billing_method'], true, {add_product:id, reset_shipping: true}); + addProduct: function (id) { + this.loadArea(['items', 'shipping_method', 'totals', 'billing_method'], true, { + add_product: id, + reset_shipping: true + }); }, - removeQuoteItem : function(id){ + removeQuoteItem: function (id) { this.loadArea(['items', 'shipping_method', 'totals', 'billing_method'], true, - {remove_item:id, from:'quote',reset_shipping: true}); + {remove_item: id, from: 'quote', reset_shipping: true}); }, - moveQuoteItem : function(id, to){ - this.loadArea(['sidebar_'+to, 'items', 'shipping_method', 'totals', 'billing_method'], this.getAreaId('items'), - {move_item:id, to:to, reset_shipping: true}); + moveQuoteItem: function (id, to) { + this.loadArea(['sidebar_' + to, 'items', 'shipping_method', 'totals', 'billing_method'], this.getAreaId('items'), + {move_item: id, to: to, reset_shipping: true}); }, - productGridShow : function(buttonElement){ + productGridShow: function (buttonElement) { this.productGridShowButton = buttonElement; Element.hide(buttonElement); this.showArea('search'); }, - productGridRowInit : function(grid, row){ + productGridRowInit: function (grid, row) { var checkbox = $(row).select('.checkbox')[0]; var inputs = $(row).select('.input-text'); if (checkbox && inputs.length > 0) { @@ -621,29 +619,39 @@ define([ input.disabled = !checkbox.checked || input.hasClassName('input-inactive'); - Event.observe(input,'keyup', this.productGridRowInputChange.bind(this)); - Event.observe(input,'change',this.productGridRowInputChange.bind(this)); + Event.observe(input, 'keyup', this.productGridRowInputChange.bind(this)); + Event.observe(input, 'change', this.productGridRowInputChange.bind(this)); } } }, - productGridRowInputChange : function(event){ + productGridRowInputChange: function (event) { var element = Event.element(event); - if (element && element.checkboxElement && element.checkboxElement.checked){ - if (element.name!='giftmessage' || element.checked) { + if (element && element.checkboxElement && element.checkboxElement.checked) { + if (element.name != 'giftmessage' || element.checked) { this.gridProducts.get(element.checkboxElement.value)[element.name] = element.value; - } else if (element.name=='giftmessage' && this.gridProducts.get(element.checkboxElement.value)[element.name]) { - delete(this.gridProducts.get(element.checkboxElement.value)[element.name]); + } else if (element.name == 'giftmessage' && this.gridProducts.get(element.checkboxElement.value)[element.name]) { + delete (this.gridProducts.get(element.checkboxElement.value)[element.name]); } } }, - productGridRowClick : function(grid, event){ + productGridRowClick: function (grid, event) { var trElement = Event.findElement(event, 'tr'); var qtyElement = trElement.select('input[name="qty"]')[0]; var eventElement = Event.element(event); - var isInputCheckbox = eventElement.tagName == 'INPUT' && eventElement.type == 'checkbox'; - var isInputQty = eventElement.tagName == 'INPUT' && eventElement.name == 'qty'; + + if (eventElement.tagName === 'LABEL' + && trElement.querySelector('#' + eventElement.htmlFor) + && trElement.querySelector('#' + eventElement.htmlFor).type === 'checkbox' + ) { + event.stopPropagation(); + trElement.querySelector('#' + eventElement.htmlFor).trigger('click'); + return; + } + + var isInputCheckbox = (eventElement.tagName === 'INPUT' && eventElement.type === 'checkbox'); + var isInputQty = grid.targetElement && grid.targetElement.tagName === 'INPUT' && grid.targetElement.name === 'qty'; if (trElement && !isInputQty) { var checkbox = Element.select(trElement, 'input[type="checkbox"]')[0]; var confLink = Element.select(trElement, 'a')[0]; @@ -665,10 +673,10 @@ define([ if (!priceBase) { this.productPriceBase[productId] = 0; } else { - this.productPriceBase[productId] = parseFloat(priceBase[1].replace(/,/g,'')); + this.productPriceBase[productId] = parseFloat(priceBase[1].replace(/,/g, '')); } } - productConfigure.setConfirmCallback(listType, function() { + productConfigure.setConfirmCallback(listType, function () { // sync qty of popup and qty of grid var confirmedCurrentQty = productConfigure.getCurrentConfirmedQtyElement(); if (qtyElement && confirmedCurrentQty && !isNaN(confirmedCurrentQty.value)) { @@ -684,12 +692,12 @@ define([ // and set checkbox checked grid.setCheckboxChecked(checkbox, true); }.bind(this)); - productConfigure.setCancelCallback(listType, function() { + productConfigure.setCancelCallback(listType, function () { if (!$(productConfigure.confirmedCurrentId) || !$(productConfigure.confirmedCurrentId).innerHTML) { grid.setCheckboxChecked(checkbox, false); } }); - productConfigure.setShowWindowCallback(listType, function() { + productConfigure.setShowWindowCallback(listType, function () { // sync qty of grid and qty of popup var formCurrentQty = productConfigure.getCurrentFormQtyElement(); if (formCurrentQty && qtyElement && !isNaN(qtyElement.value)) { @@ -705,7 +713,7 @@ define([ /** * Is need to summarize price */ - _isSummarizePrice: function(elm) { + _isSummarizePrice: function (elm) { if (elm && elm.hasAttribute('summarizePrice')) { this.summarizePrice = parseInt(elm.readAttribute('summarizePrice')); } @@ -732,9 +740,9 @@ define([ } return 0; }; - for(var i = 0; i < elms.length; i++) { + for (var i = 0; i < elms.length; i++) { if (elms[i].type == 'select-one' || elms[i].type == 'select-multiple') { - for(var ii = 0; ii < elms[i].options.length; ii++) { + for (var ii = 0; ii < elms[i].options.length; ii++) { if (elms[i].options[ii].selected) { if (this._isSummarizePrice(elms[i].options[ii])) { productPrice += getPrice(elms[i].options[ii]); @@ -743,8 +751,7 @@ define([ } } } - } - else if (((elms[i].type == 'checkbox' || elms[i].type == 'radio') && elms[i].checked) + } else if (((elms[i].type == 'checkbox' || elms[i].type == 'radio') && elms[i].checked) || ((elms[i].type == 'file' || elms[i].type == 'text' || elms[i].type == 'textarea' || elms[i].type == 'hidden') && Form.Element.getValue(elms[i])) ) { @@ -763,9 +770,9 @@ define([ return productPrice; }, - productGridCheckboxCheck : function(grid, element, checked){ + productGridCheckboxCheck: function (grid, element, checked) { if (checked) { - if(element.inputElements) { + if (element.inputElements) { this.gridProducts.set(element.value, {}); var product = this.gridProducts.get(element.value); for (var i = 0; i < element.inputElements.length; i++) { @@ -780,19 +787,19 @@ define([ if (input.checked || input.name != 'giftmessage') { product[input.name] = input.value; } else if (product[input.name]) { - delete(product[input.name]); + delete (product[input.name]); } } } } else { - if(element.inputElements){ - for(var i = 0; i < element.inputElements.length; i++) { + if (element.inputElements) { + for (var i = 0; i < element.inputElements.length; i++) { element.inputElements[i].disabled = true; } } this.gridProducts.unset(element.value); } - grid.reloadParams = {'products[]':this.gridProducts.keys()}; + grid.reloadParams = {'products[]': this.gridProducts.keys()}; }, productGridFilterKeyPress: function (grid, event) { @@ -812,18 +819,18 @@ define([ /** * Submit configured products to quote */ - productGridAddSelected : function(){ - if(this.productGridShowButton) Element.show(this.productGridShowButton); - var area = ['search', 'items', 'shipping_method', 'totals', 'giftmessage','billing_method']; + productGridAddSelected: function () { + if (this.productGridShowButton) Element.show(this.productGridShowButton); + var area = ['search', 'items', 'shipping_method', 'totals', 'giftmessage', 'billing_method']; // prepare additional fields and filtered items of products var fieldsPrepare = {}; var itemsFilter = []; var products = this.gridProducts.toObject(); for (var productId in products) { itemsFilter.push(productId); - var paramKey = 'item['+productId+']'; + var paramKey = 'item[' + productId + ']'; for (var productParamKey in products[productId]) { - paramKey += '['+productParamKey+']'; + paramKey += '[' + productParamKey + ']'; fieldsPrepare[paramKey] = products[productId][productParamKey]; } } @@ -833,47 +840,47 @@ define([ this.gridProducts = $H({}); }, - selectCustomer : function(grid, event){ + selectCustomer: function (grid, event) { var element = Event.findElement(event, 'tr'); - if (element.title){ + if (element.title) { this.setCustomerId(element.title); } }, - customerSelectorHide : function(){ + customerSelectorHide: function () { this.hideArea('customer-selector'); }, - customerSelectorShow : function(){ + customerSelectorShow: function () { this.showArea('customer-selector'); }, - storeSelectorHide : function(){ + storeSelectorHide: function () { this.hideArea('store-selector'); }, - storeSelectorShow : function(){ + storeSelectorShow: function () { this.showArea('store-selector'); }, - dataHide : function(){ + dataHide: function () { this.hideArea('data'); }, - dataShow : function(){ + dataShow: function () { if ($('submit_order_top_button')) { $('submit_order_top_button').show(); } this.showArea('data'); }, - clearShoppingCart : function(confirmMessage){ + clearShoppingCart: function (confirmMessage) { var self = this; confirm({ content: confirmMessage, actions: { - confirm: function() { + confirm: function () { self.collectElementsValue = false; order.sidebarApplyChanges({'sidebar[empty_customer_cart]': 1}); self.collectElementsValue = true; @@ -882,12 +889,12 @@ define([ }); }, - sidebarApplyChanges : function(auxiliaryParams) { + sidebarApplyChanges: function (auxiliaryParams) { if ($(this.getAreaId('sidebar'))) { var data = {}; if (this.collectElementsValue) { var elems = $(this.getAreaId('sidebar')).select('input'); - for (var i=0; i < elems.length; i++) { + for (var i = 0; i < elems.length; i++) { if (elems[i].getValue()) { data[elems[i].name] = elems[i].getValue(); } @@ -899,20 +906,20 @@ define([ } } data.reset_shipping = true; - this.loadArea(['sidebar', 'items', 'shipping_method', 'billing_method','totals', 'giftmessage'], true, data); + this.loadArea(['sidebar', 'items', 'shipping_method', 'billing_method', 'totals', 'giftmessage'], true, data); } }, - sidebarHide : function(){ - if(this.storeId === false && $('page:left') && $('page:container')){ + sidebarHide: function () { + if (this.storeId === false && $('page:left') && $('page:container')) { $('page:left').hide(); $('page:container').removeClassName('container'); $('page:container').addClassName('container-collapsed'); } }, - sidebarShow : function(){ - if($('page:left') && $('page:container')){ + sidebarShow: function () { + if ($('page:left') && $('page:container')) { $('page:left').show(); $('page:container').removeClassName('container-collapsed'); $('page:container').addClassName('container'); @@ -933,7 +940,7 @@ define([ for (var i in params) { if (params[i] === null) { unset(params[i]); - } else if (typeof(params[i]) == 'boolean') { + } else if (typeof (params[i]) == 'boolean') { params[i] = params[i] ? 1 : 0; } } @@ -942,15 +949,15 @@ define([ fields.push(new Element('input', {type: 'hidden', name: name, value: params[name]})); } // add additional fields before triggered submit - productConfigure.setBeforeSubmitCallback(listType, function() { + productConfigure.setBeforeSubmitCallback(listType, function () { productConfigure.addFields(fields); }.bind(this)); // response handler - productConfigure.setOnLoadIFrameCallback(listType, function(response) { + productConfigure.setOnLoadIFrameCallback(listType, function (response) { if (!response.ok) { return; } - this.loadArea(['items', 'shipping_method', 'billing_method','totals', 'giftmessage'], true); + this.loadArea(['items', 'shipping_method', 'billing_method', 'totals', 'giftmessage'], true); }.bind(this)); // show item configuration itemId = itemId ? itemId : productId; @@ -958,17 +965,17 @@ define([ return false; }, - removeSidebarItem : function(id, from){ - this.loadArea(['sidebar_'+from], 'sidebar_data_'+from, {remove_item:id, from:from}); + removeSidebarItem: function (id, from) { + this.loadArea(['sidebar_' + from], 'sidebar_data_' + from, {remove_item: id, from: from}); }, - itemsUpdate : function(){ - var area = ['sidebar', 'items', 'shipping_method', 'billing_method','totals', 'giftmessage']; + itemsUpdate: function () { + var area = ['sidebar', 'items', 'shipping_method', 'billing_method', 'totals', 'giftmessage']; // prepare additional fields var fieldsPrepare = {update_items: 1}; var info = $('order-items_grid').select('input', 'select', 'textarea'); - for(var i=0; i<info.length; i++){ - if(!info[i].disabled && (info[i].type != 'checkbox' || info[i].checked)) { + for (var i = 0; i < info.length; i++) { + if (!info[i].disabled && (info[i].type != 'checkbox' || info[i].checked)) { fieldsPrepare[info[i].name] = info[i].getValue(); } } @@ -977,17 +984,17 @@ define([ this.orderItemChanged = false; }, - itemsOnchangeBind : function(){ + itemsOnchangeBind: function () { var elems = $('order-items_grid').select('input', 'select', 'textarea'); - for(var i=0; i<elems.length; i++){ - if(!elems[i].bindOnchange){ + for (var i = 0; i < elems.length; i++) { + if (!elems[i].bindOnchange) { elems[i].bindOnchange = true; elems[i].observe('change', this.itemChange.bind(this)) } } }, - itemChange : function(event){ + itemChange: function (event) { this.giftmessageOnItemChange(event); this.orderItemChanged = true; }, @@ -1000,7 +1007,7 @@ define([ * @param fieldsPrepare * @param itemsFilter */ - productConfigureSubmit : function(listType, area, fieldsPrepare, itemsFilter) { + productConfigureSubmit: function (listType, area, fieldsPrepare, itemsFilter) { // prepare loading areas and build url area = this.prepareArea(area); this.loadingAreas = area; @@ -1025,7 +1032,7 @@ define([ // prepare and do submit productConfigure.addListType(listType, {urlSubmit: url}); - productConfigure.setOnLoadIFrameCallback(listType, function(response){ + productConfigure.setOnLoadIFrameCallback(listType, function (response) { this.loadAreaResponseHandler(response); }.bind(this)); productConfigure.submit(listType); @@ -1038,20 +1045,20 @@ define([ * * @param itemId */ - showQuoteItemConfiguration: function(itemId){ + showQuoteItemConfiguration: function (itemId) { var listType = 'quote_items'; - var qtyElement = $('order-items_grid').select('input[name="item\['+itemId+'\]\[qty\]"]')[0]; - productConfigure.setConfirmCallback(listType, function() { + var qtyElement = $('order-items_grid').select('input[name="item\[' + itemId + '\]\[qty\]"]')[0]; + productConfigure.setConfirmCallback(listType, function () { // sync qty of popup and qty of grid var confirmedCurrentQty = productConfigure.getCurrentConfirmedQtyElement(); if (qtyElement && confirmedCurrentQty && !isNaN(confirmedCurrentQty.value)) { qtyElement.value = confirmedCurrentQty.value; } - this.productConfigureAddFields['item['+itemId+'][configured]'] = 1; + this.productConfigureAddFields['item[' + itemId + '][configured]'] = 1; this.itemsUpdate(); }.bind(this)); - productConfigure.setShowWindowCallback(listType, function() { + productConfigure.setShowWindowCallback(listType, function () { // sync qty of grid and qty of popup var formCurrentQty = productConfigure.getCurrentFormQtyElement(); if (formCurrentQty && qtyElement && !isNaN(qtyElement.value)) { @@ -1061,60 +1068,59 @@ define([ productConfigure.showItemConfiguration(listType, itemId); }, - accountFieldsBind : function(container){ - if($(container)){ + accountFieldsBind: function (container) { + if ($(container)) { var fields = $(container).select('input', 'select', 'textarea'); - for(var i=0; i<fields.length; i++){ - if(fields[i].id == 'group_id'){ + for (var i = 0; i < fields.length; i++) { + if (fields[i].id == 'group_id') { fields[i].observe('change', this.accountGroupChange.bind(this)) - } - else{ + } else { fields[i].observe('change', this.accountFieldChange.bind(this)) } } } }, - accountGroupChange : function(){ + accountGroupChange: function () { this.loadArea(['data'], true, this.serializeData('order-form_account').toObject()); }, - accountFieldChange : function(){ + accountFieldChange: function () { this.saveData(this.serializeData('order-form_account')); }, - commentFieldsBind : function(container){ - if($(container)){ + commentFieldsBind: function (container) { + if ($(container)) { var fields = $(container).select('input', 'textarea'); - for(var i=0; i<fields.length; i++) + for (var i = 0; i < fields.length; i++) fields[i].observe('change', this.commentFieldChange.bind(this)) } }, - commentFieldChange : function(){ + commentFieldChange: function () { this.saveData(this.serializeData('order-comment')); }, - giftmessageFieldsBind : function(container){ - if($(container)){ + giftmessageFieldsBind: function (container) { + if ($(container)) { var fields = $(container).select('input', 'textarea'); - for(var i=0; i<fields.length; i++) + for (var i = 0; i < fields.length; i++) fields[i].observe('change', this.giftmessageFieldChange.bind(this)) } }, - giftmessageFieldChange : function(){ + giftmessageFieldChange: function () { this.giftMessageDataChanged = true; }, - giftmessageOnItemChange : function(event) { + giftmessageOnItemChange: function (event) { var element = Event.element(event); - if(element.name.indexOf("giftmessage") != -1 && element.type == "checkbox" && !element.checked) { + if (element.name.indexOf("giftmessage") != -1 && element.type == "checkbox" && !element.checked) { var messages = $("order-giftmessage").select('textarea'); var name; - for(var i=0; i<messages.length; i++) { + for (var i = 0; i < messages.length; i++) { name = messages[i].id.split("_"); - if(name.length < 2) continue; + if (name.length < 2) continue; if (element.name.indexOf("[" + name[1] + "]") != -1 && messages[i].value != "") { alert({ content: "First, clean the Message field in Gift Message form" @@ -1125,7 +1131,7 @@ define([ } }, - loadArea : function(area, indicator, params){ + loadArea: function (area, indicator, params) { var deferred = new jQuery.Deferred(); var url = this.loadBaseUrl; if (area) { @@ -1139,20 +1145,19 @@ define([ if (indicator) { this.loadingAreas = area; new Ajax.Request(url, { - parameters:params, + parameters: params, loaderArea: indicator, - onSuccess: function(transport) { + onSuccess: function (transport) { var response = transport.responseText.evalJSON(); this.loadAreaResponseHandler(response); deferred.resolve(); }.bind(this) }); - } - else { + } else { new Ajax.Request(url, { - parameters:params, + parameters: params, loaderArea: indicator, - onSuccess: function(transport) { + onSuccess: function (transport) { deferred.resolve(); } }); @@ -1163,7 +1168,7 @@ define([ return deferred.promise(); }, - loadAreaResponseHandler : function (response) { + loadAreaResponseHandler: function (response) { if (response.error) { alert({ content: response.message @@ -1198,45 +1203,44 @@ define([ } }, - prepareArea : function(area) { + prepareArea: function (area) { if (this.giftMessageDataChanged) { return area.without('giftmessage'); } return area; }, - saveData : function(data){ + saveData: function (data) { this.loadArea(false, false, data); }, - showArea : function(area){ + showArea: function (area) { var id = this.getAreaId(area); - if($(id)) { + if ($(id)) { $(id).show(); this.areaOverlay(); } }, - hideArea : function(area){ + hideArea: function (area) { var id = this.getAreaId(area); - if($(id)) { + if ($(id)) { $(id).hide(); this.areaOverlay(); } }, - areaOverlay : function() - { - $H(order.overlayData).each(function(e){ + areaOverlay: function () { + $H(order.overlayData).each(function (e) { e.value.fx(); }); }, - getAreaId : function(area){ - return 'order-'+area; + getAreaId: function (area) { + return 'order-' + area; }, - prepareParams : function(params){ + prepareParams: function (params) { if (!params) { params = {}; } @@ -1256,7 +1260,7 @@ define([ if (this.isPaymentValidationAvailable()) { var data = this.serializeData('order-billing_method'); if (data) { - data.each(function(value) { + data.each(function (value) { params[value[0]] = value[1]; }); } @@ -1271,7 +1275,7 @@ define([ * * @returns {boolean} */ - isPaymentValidationAvailable : function(){ + isPaymentValidationAvailable: function () { return ((typeof this.paymentMethod) == 'undefined' || this.excludedPaymentMethods.indexOf(this.paymentMethod) == -1); }, @@ -1289,21 +1293,19 @@ define([ return $H(data); }, - toggleCustomPrice: function(checkbox, elemId, tierBlock) { + toggleCustomPrice: function (checkbox, elemId, tierBlock) { if (checkbox.checked) { $(elemId).disabled = false; $(elemId).show(); - if($(tierBlock)) $(tierBlock).hide(); - } - else { + if ($(tierBlock)) $(tierBlock).hide(); + } else { $(elemId).disabled = true; $(elemId).hide(); - if($(tierBlock)) $(tierBlock).show(); + if ($(tierBlock)) $(tierBlock).show(); } }, - submit : function() - { + submit: function () { var $editForm = jQuery('#edit_form'); if ($editForm.valid()) { @@ -1313,9 +1315,9 @@ define([ }, _realSubmit: function () { - var disableAndSave = function() { + var disableAndSave = function () { disableElements('save'); - jQuery('#edit_form').on('invalid-form.validate', function() { + jQuery('#edit_form').on('invalid-form.validate', function () { enableElements('save'); jQuery('#edit_form').trigger('processStop'); jQuery('#edit_form').off('invalid-form.validate'); @@ -1330,11 +1332,11 @@ define([ confirm({ content: jQuery.mage.__('You have item changes'), actions: { - confirm: function() { + confirm: function () { jQuery('#edit_form').trigger('processStart'); disableAndSave(); }, - cancel: function() { + cancel: function () { self.itemsUpdate(); } } @@ -1344,8 +1346,10 @@ define([ } }, - overlay : function(elId, show, observe) { - if (typeof(show) == 'undefined') { show = true; } + overlay: function (elId, show, observe) { + if (typeof (show) == 'undefined') { + show = true; + } var orderObj = this; var obj = this.overlayData.get(elId); @@ -1354,7 +1358,7 @@ define([ show: show, el: elId, order: orderObj, - fx: function(event) { + fx: function (event) { this.order.processOverlay(this.el, this.show); } }; @@ -1370,7 +1374,7 @@ define([ this.processOverlay(elId, show); }, - processOverlay : function(elId, show) { + processOverlay: function (elId, show) { var el = $(elId); if (!el) { @@ -1402,8 +1406,7 @@ define([ }); }, - validateVat: function(parameters) - { + validateVat: function (parameters) { var params = { country: $(parameters.countryElementId).value, vat: $(parameters.vatElementId).value @@ -1418,7 +1421,7 @@ define([ new Ajax.Request(parameters.validateUrl, { parameters: params, - onSuccess: function(response) { + onSuccess: function (response) { var message = ''; var groupActionRequired = null; try { @@ -1457,8 +1460,7 @@ define([ alert({ content: message }); - } - else { + } else { this.processCustomerGroupChange( parameters.groupIdHtmlId, message, @@ -1472,8 +1474,7 @@ define([ }); }, - processCustomerGroupChange: function(groupIdHtmlId, message, customerGroupMessage, errorMessage, groupId, action) - { + processCustomerGroupChange: function (groupIdHtmlId, message, customerGroupMessage, errorMessage, groupId, action) { var groupMessage = ''; try { var currentCustomerGroupId = $(groupIdHtmlId).value; @@ -1513,8 +1514,8 @@ define([ _parent: null, _callbackName: null, - initialize: function(name, node, parent){ - if(!node) + initialize: function (name, node, parent) { + if (!node) return; this._name = name; this._parent = parent; @@ -1523,7 +1524,7 @@ define([ this._callbackName = name + 'Loaded'; node.callback = this._callbackName; } - parent[this._callbackName] = parent[this._callbackName].wrap((function (proceed){ + parent[this._callbackName] = parent[this._callbackName].wrap((function (proceed) { proceed(); this.onLoad(); }).bind(this)); @@ -1531,14 +1532,14 @@ define([ this.setNode(node); }, - setNode: function(node){ + setNode: function (node) { if (!node.callback) { node.callback = this._callbackName; } this.node = node; }, - onLoad: function(){ + onLoad: function () { } }; @@ -1548,21 +1549,21 @@ define([ _label: '', _node: null, - initialize: function(label, id){ + initialize: function (label, id) { this._label = label; this._node = new Element('button', { 'class': 'action-secondary action-add', - 'type': 'button' + 'type': 'button' }); if (typeof id !== 'undefined') { this._node.setAttribute('id', id) } }, - onClick: function(){ + onClick: function () { }, - insertIn: function(element, position){ + insertIn: function (element, position) { var node = Object.extend(this._node), content = {}; node.observe('click', this.onClick); @@ -1571,7 +1572,7 @@ define([ Element.insert(element, content); }, - getLabel: function(){ + getLabel: function () { return this._label; } }; diff --git a/app/code/Magento/Sales/view/frontend/email/creditmemo_new.html b/app/code/Magento/Sales/view/frontend/email/creditmemo_new.html index ca89446a2f7c0..5ae6f5f9d82c7 100644 --- a/app/code/Magento/Sales/view/frontend/email/creditmemo_new.html +++ b/app/code/Magento/Sales/view/frontend/email/creditmemo_new.html @@ -4,28 +4,34 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Credit memo for your %store_name order" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Credit memo for your %store_name order" store_name=$store.frontend_name}} @--> <!--@vars { "var formattedBillingAddress|raw":"Billing Address", -"var comment":"Credit Memo Comment", +"var comment|escape|nl2br":"Credit Memo Comment", "var creditmemo.increment_id":"Credit Memo Id", "layout handle=\"sales_email_order_creditmemo_items\" creditmemo=$creditmemo order=$order":"Credit Memo Items Grid", -"var this.getUrl($store, 'customer/account/')":"Customer Account URL", -"var order.getCustomerName()":"Customer Name", +"var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL", +"var order_data.customer_name":"Customer Name", "var order.increment_id":"Order Id", "var payment_html|raw":"Payment Details", "var formattedShippingAddress|raw":"Shipping Address", -"var order.getShippingDescription()":"Shipping Description", -"var order.shipping_description":"Shipping Description" +"var order.shipping_description":"Shipping Description", +"var store.frontend_name":"Store Frontend Name", +"var store_email":"Store Email", +"var store_phone":"Store Phone", +"var store_hours":"Store Hours", +"var creditmemo":"Credit Memo", +"var order":"Order", +"var order_data.is_not_virtual":"Order Type" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$order.getCustomerName()}}</p> + <p class="greeting">{{trans "%name," name=$order_data.customer_name}}</p> <p> - {{trans "Thank you for your order from %store_name." store_name=$store.getFrontendName()}} + {{trans "Thank you for your order from %store_name." store_name=$store.frontend_name}} {{trans 'You can check the status of your order by <a href="%account_url">logging into your account</a>.' account_url=$this.getUrl($store,'customer/account/',[_nosid:1]) |raw}} {{trans 'If you have questions about your order, you can email us at <a href="mailto:%store_email">%store_email</a>' store_email=$store_email |raw}}{{depend store_phone}} {{trans 'or call us at <a href="tel:%store_phone">%store_phone</a>' store_phone=$store_phone |raw}}{{/depend}}. {{depend store_hours}} @@ -56,7 +62,7 @@ <h1>{{trans "Your Credit Memo #%creditmemo_id for Order #%order_id" creditmemo_i <h3>{{trans "Billing Info"}}</h3> <p>{{var formattedBillingAddress|raw}}</p> </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="address-details"> <h3>{{trans "Shipping Info"}}</h3> <p>{{var formattedShippingAddress|raw}}</p> @@ -68,10 +74,10 @@ <h3>{{trans "Shipping Info"}}</h3> <h3>{{trans "Payment Method"}}</h3> {{var payment_html|raw}} </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="method-info"> <h3>{{trans "Shipping Method"}}</h3> - <p>{{var order.getShippingDescription()}}</p> + <p>{{var order.shipping_description}}</p> </td> {{/depend}} </tr> diff --git a/app/code/Magento/Sales/view/frontend/email/creditmemo_new_guest.html b/app/code/Magento/Sales/view/frontend/email/creditmemo_new_guest.html index b21f659814368..657de2aae2045 100644 --- a/app/code/Magento/Sales/view/frontend/email/creditmemo_new_guest.html +++ b/app/code/Magento/Sales/view/frontend/email/creditmemo_new_guest.html @@ -4,27 +4,33 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Credit memo for your %store_name order" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Credit memo for your %store_name order" store_name=$store.frontend_name}} @--> <!--@vars { "var formattedBillingAddress|raw":"Billing Address", -"var comment":"Credit Memo Comment", +"var comment|escape|nl2br":"Credit Memo Comment", "var creditmemo.increment_id":"Credit Memo Id", "layout handle=\"sales_email_order_creditmemo_items\" creditmemo=$creditmemo order=$order":"Credit Memo Items Grid", -"var billing.getName()":"Guest Customer Name (Billing)", +"var billing.name":"Guest Customer Name (Billing)", "var order.increment_id":"Order Id", "var payment_html|raw":"Payment Details", "var formattedShippingAddress|raw":"Shipping Address", -"var order.getShippingDescription()":"Shipping Description", -"var order.shipping_description":"Shipping Description" +"var order.shipping_description":"Shipping Description", +"var store.frontend_name":"Store Frontend Name", +"var store_phone":"Store Phone", +"var store_email":"Store Email", +"var store_hours":"Store Hours", +"var creditmemo":"Credit Memo", +"var order":"Order", +"var order_data.is_not_virtual":"Order Type" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$billing.getName()}}</p> + <p class="greeting">{{trans "%name," name=$billing.name}}</p> <p> - {{trans "Thank you for your order from %store_name." store_name=$store.getFrontendName()}} + {{trans "Thank you for your order from %store_name." store_name=$store.frontend_name}} {{trans 'If you have questions about your order, you can email us at <a href="mailto:%store_email">%store_email</a>' store_email=$store_email |raw}}{{depend store_phone}} {{trans 'or call us at <a href="tel:%store_phone">%store_phone</a>' store_phone=$store_phone |raw}}{{/depend}}. {{depend store_hours}} {{trans 'Our hours are <span class="no-link">%store_hours</span>.' store_hours=$store_hours |raw}} @@ -54,7 +60,7 @@ <h1>{{trans "Your Credit Memo #%creditmemo_id for Order #%order_id" creditmemo_i <h3>{{trans "Billing Info"}}</h3> <p>{{var formattedBillingAddress|raw}}</p> </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="address-details"> <h3>{{trans "Shipping Info"}}</h3> <p>{{var formattedShippingAddress|raw}}</p> @@ -66,10 +72,10 @@ <h3>{{trans "Shipping Info"}}</h3> <h3>{{trans "Payment Method"}}</h3> {{var payment_html|raw}} </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="method-info"> <h3>{{trans "Shipping Method"}}</h3> - <p>{{var order.getShippingDescription()}}</p> + <p>{{var order.shipping_description}}</p> </td> {{/depend}} </tr> diff --git a/app/code/Magento/Sales/view/frontend/email/creditmemo_update.html b/app/code/Magento/Sales/view/frontend/email/creditmemo_update.html index a6a10fb49e3f5..7e7930f33f1b9 100644 --- a/app/code/Magento/Sales/view/frontend/email/creditmemo_update.html +++ b/app/code/Magento/Sales/view/frontend/email/creditmemo_update.html @@ -4,27 +4,31 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Update to your %store_name credit memo" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Update to your %store_name credit memo" store_name=$store.frontend_name}} @--> <!--@vars { -"var comment":"Credit Memo Comment", +"var comment|escape|nl2br":"Credit Memo Comment", "var creditmemo.increment_id":"Credit Memo Id", -"var this.getUrl($store, 'customer/account/')":"Customer Account URL", -"var order.getCustomerName()":"Customer Name", +"var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL", +"var order_data.customer_name":"Customer Name", "var order.increment_id":"Order Id", -"var order.getFrontendStatusLabel()":"Order Status" +"var order_data.frontend_status_label":"Order Status", +"var store.frontend_name":"Store Frontend Name", +"var store_phone":"Store Phone", +"var store_email":"Store Email", +"var store_hours":"Store Hours" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$order.getCustomerName()}}</p> + <p class="greeting">{{trans "%name," name=$order_data.customer_name}}</p> <p> {{trans "Your order #%increment_id has been updated with a status of <strong>%order_status</strong>." increment_id=$order.increment_id - order_status=$order.getFrontendStatusLabel() + order_status=$order_data.frontend_status_label |raw}} </p> <p>{{trans 'You can check the status of your order by <a href="%account_url">logging into your account</a>.' account_url=$this.getUrl($store,'customer/account/',[_nosid:1]) |raw}}</p> diff --git a/app/code/Magento/Sales/view/frontend/email/creditmemo_update_guest.html b/app/code/Magento/Sales/view/frontend/email/creditmemo_update_guest.html index b7411d80d2ba6..ed8f592b59638 100644 --- a/app/code/Magento/Sales/view/frontend/email/creditmemo_update_guest.html +++ b/app/code/Magento/Sales/view/frontend/email/creditmemo_update_guest.html @@ -4,26 +4,30 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Update to your %store_name credit memo" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Update to your %store_name credit memo" store_name=$store.frontend_name}} @--> <!--@vars { -"var comment":"Credit Memo Comment", +"var comment|escape|nl2br":"Credit Memo Comment", "var creditmemo.increment_id":"Credit Memo Id", -"var billing.getName()":"Guest Customer Name", +"var billing.name":"Guest Customer Name", "var order.increment_id":"Order Id", -"var order.getFrontendStatusLabel()":"Order Status" +"var order_data.frontend_status_label":"Order Status", +"var store.frontend_name":"Store Frontend Name", +"var store_phone":"Store Phone", +"var store_email":"Store Email", +"var store_hours":"Store Hours" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$billing.getName()}}</p> + <p class="greeting">{{trans "%name," name=$billing.name}}</p> <p> {{trans "Your order #%increment_id has been updated with a status of <strong>%order_status</strong>." increment_id=$order.increment_id - order_status=$order.getFrontendStatusLabel() + order_status=$order_data.frontend_status_label |raw}} </p> <p> diff --git a/app/code/Magento/Sales/view/frontend/email/invoice_new.html b/app/code/Magento/Sales/view/frontend/email/invoice_new.html index ca5f7ee632e22..68773ee9d7570 100644 --- a/app/code/Magento/Sales/view/frontend/email/invoice_new.html +++ b/app/code/Magento/Sales/view/frontend/email/invoice_new.html @@ -4,28 +4,34 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Invoice for your %store_name order" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Invoice for your %store_name order" store_name=$store.frontend_name}} @--> <!--@vars { "var formattedBillingAddress|raw":"Billing Address", -"var this.getUrl($store, 'customer/account/')":"Customer Account URL", -"var order.getCustomerName()":"Customer Name", -"var comment":"Invoice Comment", +"var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL", +"var order_data.customer_name":"Customer Name", +"var comment|escape|nl2br":"Invoice Comment", "var invoice.increment_id":"Invoice Id", "layout area=\"frontend\" handle=\"sales_email_order_invoice_items\" invoice=$invoice order=$order":"Invoice Items Grid", "var order.increment_id":"Order Id", "var payment_html|raw":"Payment Details", "var formattedShippingAddress|raw":"Shipping Address", "var order.shipping_description":"Shipping Description", -"var order.getShippingDescription()":"Shipping Description" +"var store.frontend_name":"Store Frontend Name", +"var store_phone":"Store Phone", +"var store_email":"Store Email", +"var store_hours":"Store Hours", +"var invoice": "Invoice", +"var order": "Order", +"var order_data.is_not_virtual": "Order Type" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$order.getCustomerName()}}</p> + <p class="greeting">{{trans "%name," name=$order_data.customer_name}}</p> <p> - {{trans "Thank you for your order from %store_name." store_name=$store.getFrontendName()}} + {{trans "Thank you for your order from %store_name." store_name=$store.frontend_name}} {{trans 'You can check the status of your order by <a href="%account_url">logging into your account</a>.' account_url=$this.getUrl($store,'customer/account/',[_nosid:1]) |raw}} {{trans 'If you have questions about your order, you can email us at <a href="mailto:%store_email">%store_email</a>' store_email=$store_email |raw}}{{depend store_phone}} {{trans 'or call us at <a href="tel:%store_phone">%store_phone</a>' store_phone=$store_phone |raw}}{{/depend}}. {{depend store_hours}} @@ -56,7 +62,7 @@ <h1>{{trans "Your Invoice #%invoice_id for Order #%order_id" invoice_id=$invoice <h3>{{trans "Billing Info"}}</h3> <p>{{var formattedBillingAddress|raw}}</p> </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="address-details"> <h3>{{trans "Shipping Info"}}</h3> <p>{{var formattedShippingAddress|raw}}</p> @@ -68,10 +74,10 @@ <h3>{{trans "Shipping Info"}}</h3> <h3>{{trans "Payment Method"}}</h3> {{var payment_html|raw}} </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="method-info"> <h3>{{trans "Shipping Method"}}</h3> - <p>{{var order.getShippingDescription()}}</p> + <p>{{var order.shipping_description}}</p> </td> {{/depend}} </tr> diff --git a/app/code/Magento/Sales/view/frontend/email/invoice_new_guest.html b/app/code/Magento/Sales/view/frontend/email/invoice_new_guest.html index c93df9f9e8efb..5053ccc2ac635 100644 --- a/app/code/Magento/Sales/view/frontend/email/invoice_new_guest.html +++ b/app/code/Magento/Sales/view/frontend/email/invoice_new_guest.html @@ -4,27 +4,33 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Invoice for your %store_name order" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Invoice for your %store_name order" store_name=$store.frontend_name}} @--> <!--@vars { "var formattedBillingAddress|raw":"Billing Address", -"var billing.getName()":"Guest Customer Name", -"var comment":"Invoice Comment", +"var billing.name":"Guest Customer Name", +"var comment|escape|nl2br":"Invoice Comment", "var invoice.increment_id":"Invoice Id", "layout handle=\"sales_email_order_invoice_items\" invoice=$invoice order=$order":"Invoice Items Grid", "var order.increment_id":"Order Id", "var payment_html|raw":"Payment Details", "var formattedShippingAddress|raw":"Shipping Address", -"var order.getShippingDescription()":"Shipping Description", -"var order.shipping_description":"Shipping Description" +"var order.shipping_description":"Shipping Description", +"var store.frontend_name":"Store Frontend Name", +"var store_phone":"Store Phone", +"var store_email":"Store Email", +"var store_hours":"Store Hours", +"var invoice": "Invoice", +"var order": "Order", +"var order_data.is_not_virtual": "Order Type" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$billing.getName()}}</p> + <p class="greeting">{{trans "%name," name=$billing.name}}</p> <p> - {{trans "Thank you for your order from %store_name." store_name=$store.getFrontendName()}} + {{trans "Thank you for your order from %store_name." store_name=$store.frontend_name}} {{trans 'If you have questions about your order, you can email us at <a href="mailto:%store_email">%store_email</a>' store_email=$store_email |raw}}{{depend store_phone}} {{trans 'or call us at <a href="tel:%store_phone">%store_phone</a>' store_phone=$store_phone |raw}}{{/depend}}. {{depend store_hours}} {{trans 'Our hours are <span class="no-link">%store_hours</span>.' store_hours=$store_hours |raw}} @@ -54,7 +60,7 @@ <h1>{{trans "Your Invoice #%invoice_id for Order #%order_id" invoice_id=$invoice <h3>{{trans "Billing Info"}}</h3> <p>{{var formattedBillingAddress|raw}}</p> </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="address-details"> <h3>{{trans "Shipping Info"}}</h3> <p>{{var formattedShippingAddress|raw}}</p> @@ -66,10 +72,10 @@ <h3>{{trans "Shipping Info"}}</h3> <h3>{{trans "Payment Method"}}</h3> {{var payment_html|raw}} </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="method-info"> <h3>{{trans "Shipping Method"}}</h3> - <p>{{var order.getShippingDescription()}}</p> + <p>{{var order.shipping_description}}</p> </td> {{/depend}} </tr> diff --git a/app/code/Magento/Sales/view/frontend/email/invoice_update.html b/app/code/Magento/Sales/view/frontend/email/invoice_update.html index 4043e59f9d7d6..a8f98a238e314 100644 --- a/app/code/Magento/Sales/view/frontend/email/invoice_update.html +++ b/app/code/Magento/Sales/view/frontend/email/invoice_update.html @@ -4,27 +4,31 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Update to your %store_name invoice" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Update to your %store_name invoice" store_name=$store.frontend_name}} @--> <!--@vars { -"var this.getUrl($store, 'customer/account/')":"Customer Account URL", -"var order.getCustomerName()":"Customer Name", -"var comment":"Invoice Comment", +"var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL", +"var order_data.customer_name":"Customer Name", +"var comment|escape|nl2br":"Invoice Comment", "var invoice.increment_id":"Invoice Id", "var order.increment_id":"Order Id", -"var order.getFrontendStatusLabel()":"Order Status" +"var order_data.frontend_status_label":"Order Status", +"var store.frontend_name":"Store Frontend Name", +"var store_phone":"Store Phone", +"var store_email":"Store Email", +"var store_hours":"Store Hours" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$order.getCustomerName()}}</p> + <p class="greeting">{{trans "%name," name=$order_data.customer_name}}</p> <p> {{trans "Your order #%increment_id has been updated with a status of <strong>%order_status</strong>." increment_id=$order.increment_id - order_status=$order.getFrontendStatusLabel() + order_status=$order_data.frontend_status_label |raw}} </p> <p>{{trans 'You can check the status of your order by <a href="%account_url">logging into your account</a>.' account_url=$this.getUrl($store,'customer/account/',[_nosid:1]) |raw}}</p> diff --git a/app/code/Magento/Sales/view/frontend/email/invoice_update_guest.html b/app/code/Magento/Sales/view/frontend/email/invoice_update_guest.html index 40cdec7fb4cab..289c5113fe285 100644 --- a/app/code/Magento/Sales/view/frontend/email/invoice_update_guest.html +++ b/app/code/Magento/Sales/view/frontend/email/invoice_update_guest.html @@ -4,26 +4,30 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Update to your %store_name invoice" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Update to your %store_name invoice" store_name=$store.frontend_name}} @--> <!--@vars { -"var billing.getName()":"Guest Customer Name", -"var comment":"Invoice Comment", +"var billing.name":"Guest Customer Name", +"var comment|escape|nl2br":"Invoice Comment", "var invoice.increment_id":"Invoice Id", "var order.increment_id":"Order Id", -"var order.getFrontendStatusLabel()":"Order Status" +"var order_data.frontend_status_label":"Order Status", +"var store.frontend_name":"Store Frontend Name", +"var store_phone":"Store Phone", +"var store_email":"Store Email", +"var store_hours":"Store Hours" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$billing.getName()}}</p> + <p class="greeting">{{trans "%name," name=$billing.name}}</p> <p> {{trans "Your order #%increment_id has been updated with a status of <strong>%order_status</strong>." increment_id=$order.increment_id - order_status=$order.getFrontendStatusLabel() + order_status=$order_data.frontend_status_label |raw}} </p> <p> diff --git a/app/code/Magento/Sales/view/frontend/email/order_new.html b/app/code/Magento/Sales/view/frontend/email/order_new.html index 370bdb0f2f336..13c436b131b82 100644 --- a/app/code/Magento/Sales/view/frontend/email/order_new.html +++ b/app/code/Magento/Sales/view/frontend/email/order_new.html @@ -4,16 +4,25 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Your %store_name order confirmation" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Your %store_name order confirmation" store_name=$store.frontend_name}} @--> <!--@vars { "var formattedBillingAddress|raw":"Billing Address", -"var order.getEmailCustomerNote()":"Email Order Note", +"var order_data.email_customer_note|escape|nl2br":"Email Order Note", "var order.increment_id":"Order Id", "layout handle=\"sales_email_order_items\" order=$order area=\"frontend\"":"Order Items Grid", "var payment_html|raw":"Payment Details", "var formattedShippingAddress|raw":"Shipping Address", -"var order.getShippingDescription()":"Shipping Description", -"var shipping_msg":"Shipping message" +"var order.shipping_description":"Shipping Description", +"var shipping_msg":"Shipping message", +"var created_at_formatted":"Order Created At (datetime)", +"var store.frontend_name":"Store Frontend Name", +"var store_phone":"Store Phone", +"var store_email":"Store Email", +"var store_hours":"Store Hours", +"var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL", +"var order_data.is_not_virtual":"Order Type", +"var order":"Order", +"var order_data.customer_name":"Customer Name" } @--> {{template config_path="design/email/header_template"}} @@ -21,9 +30,9 @@ <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%customer_name," customer_name=$order.getCustomerName()}}</p> + <p class="greeting">{{trans "%customer_name," customer_name=$order_data.customer_name}}</p> <p> - {{trans "Thank you for your order from %store_name." store_name=$store.getFrontendName()}} + {{trans "Thank you for your order from %store_name." store_name=$store.frontend_name}} {{trans "Once your package ships we will send you a tracking number."}} {{trans 'You can check the status of your order by <a href="%account_url">logging into your account</a>.' account_url=$this.getUrl($store,'customer/account/',[_nosid:1]) |raw}} </p> @@ -38,16 +47,16 @@ <tr class="email-summary"> <td> <h1>{{trans 'Your Order <span class="no-link">#%increment_id</span>' increment_id=$order.increment_id |raw}}</h1> - <p>{{trans 'Placed on <span class="no-link">%created_at</span>' created_at=$order.getCreatedAtFormatted(2) |raw}}</p> + <p>{{trans 'Placed on <span class="no-link">%created_at</span>' created_at=$created_at_formatted |raw}}</p> </td> </tr> <tr class="email-information"> <td> - {{depend order.getEmailCustomerNote()}} + {{depend order_data.email_customer_note}} <table class="message-info"> <tr> <td> - {{var order.getEmailCustomerNote()|escape|nl2br}} + {{var order_data.email_customer_note|escape|nl2br}} </td> </tr> </table> @@ -58,7 +67,7 @@ <h1>{{trans 'Your Order <span class="no-link">#%increment_id</span>' increment_i <h3>{{trans "Billing Info"}}</h3> <p>{{var formattedBillingAddress|raw}}</p> </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="address-details"> <h3>{{trans "Shipping Info"}}</h3> <p>{{var formattedShippingAddress|raw}}</p> @@ -70,10 +79,10 @@ <h3>{{trans "Shipping Info"}}</h3> <h3>{{trans "Payment Method"}}</h3> {{var payment_html|raw}} </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="method-info"> <h3>{{trans "Shipping Method"}}</h3> - <p>{{var order.getShippingDescription()}}</p> + <p>{{var order.shipping_description}}</p> {{if shipping_msg}} <p>{{var shipping_msg}}</p> {{/if}} diff --git a/app/code/Magento/Sales/view/frontend/email/order_new_guest.html b/app/code/Magento/Sales/view/frontend/email/order_new_guest.html index cfd99e5b0936e..866a1ad87f9b1 100644 --- a/app/code/Magento/Sales/view/frontend/email/order_new_guest.html +++ b/app/code/Magento/Sales/view/frontend/email/order_new_guest.html @@ -4,27 +4,33 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Your %store_name order confirmation" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Your %store_name order confirmation" store_name=$store.frontend_name}} @--> <!--@vars { "var formattedBillingAddress|raw":"Billing Address", -"var order.getEmailCustomerNote()":"Email Order Note", -"var order.getBillingAddress().getName()":"Guest Customer Name", -"var order.getCreatedAtFormatted(2)":"Order Created At (datetime)", +"var order_data.email_customer_note|escape|nl2br":"Email Order Note", +"var order.billing_address.name":"Guest Customer Name", +"var created_at_formatted":"Order Created At (datetime)", "var order.increment_id":"Order Id", "layout handle=\"sales_email_order_items\" order=$order":"Order Items Grid", "var payment_html|raw":"Payment Details", "var formattedShippingAddress|raw":"Shipping Address", -"var order.getShippingDescription()":"Shipping Description", -"var shipping_msg":"Shipping message" +"var order.shipping_description":"Shipping Description", +"var shipping_msg":"Shipping message", +"var store.frontend_name":"Store Frontend Name", +"var store_phone":"Store Phone", +"var store_email":"Store Email", +"var store_hours":"Store Hours", +"var order_data.is_not_virtual":"Order Type", +"var order":"Order" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$order.getBillingAddress().getName()}}</p> + <p class="greeting">{{trans "%name," name=$order.billing_address.name}}</p> <p> - {{trans "Thank you for your order from %store_name." store_name=$store.getFrontendName()}} + {{trans "Thank you for your order from %store_name." store_name=$store.frontend_name}} {{trans "Once your package ships we will send an email with a link to track your order."}} {{trans 'If you have questions about your order, you can email us at <a href="mailto:%store_email">%store_email</a>' store_email=$store_email |raw}}{{depend store_phone}} {{trans 'or call us at <a href="tel:%store_phone">%store_phone</a>' store_phone=$store_phone |raw}}{{/depend}}. {{depend store_hours}} @@ -36,16 +42,16 @@ <tr class="email-summary"> <td> <h1>{{trans 'Your Order <span class="no-link">#%increment_id</span>' increment_id=$order.increment_id |raw}}</h1> - <p>{{trans 'Placed on <span class="no-link">%created_at</span>' created_at=$order.getCreatedAtFormatted(2) |raw}}</p> + <p>{{trans 'Placed on <span class="no-link">%created_at</span>' created_at=$created_at_formatted |raw}}</p> </td> </tr> <tr class="email-information"> <td> - {{depend order.getEmailCustomerNote()}} + {{depend order_data.email_customer_note}} <table class="message-info"> <tr> <td> - {{var order.getEmailCustomerNote()|escape|nl2br}} + {{var order_data.email_customer_note|escape|nl2br}} </td> </tr> </table> @@ -56,7 +62,7 @@ <h1>{{trans 'Your Order <span class="no-link">#%increment_id</span>' increment_i <h3>{{trans "Billing Info"}}</h3> <p>{{var formattedBillingAddress|raw}}</p> </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="address-details"> <h3>{{trans "Shipping Info"}}</h3> <p>{{var formattedShippingAddress|raw}}</p> @@ -68,10 +74,10 @@ <h3>{{trans "Shipping Info"}}</h3> <h3>{{trans "Payment Method"}}</h3> {{var payment_html|raw}} </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="method-info"> <h3>{{trans "Shipping Method"}}</h3> - <p>{{var order.getShippingDescription()}}</p> + <p>{{var order.shipping_description}}</p> {{if shipping_msg}} <p>{{var shipping_msg}}</p> {{/if}} diff --git a/app/code/Magento/Sales/view/frontend/email/order_update.html b/app/code/Magento/Sales/view/frontend/email/order_update.html index a8f0068b70e87..b2c4e86654f6f 100644 --- a/app/code/Magento/Sales/view/frontend/email/order_update.html +++ b/app/code/Magento/Sales/view/frontend/email/order_update.html @@ -4,26 +4,30 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Update to your %store_name order" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Update to your %store_name order" store_name=$store.frontend_name}} @--> <!--@vars { -"var this.getUrl($store, 'customer/account/')":"Customer Account URL", -"var order.getCustomerName()":"Customer Name", -"var comment":"Order Comment", +"var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL", +"var order_data.customer_name":"Customer Name", +"var comment|escape|nl2br":"Order Comment", "var order.increment_id":"Order Id", -"var order.getFrontendStatusLabel()":"Order Status" +"var order_data.frontend_status_label":"Order Status", +"var store.frontend_name":"Store Frontend Name", +"var store_email":"Store Email", +"var store_phone":"Store Phone", +"var store_hours":"Store Hours" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$order.getCustomerName()}}</p> + <p class="greeting">{{trans "%name," name=$order_data.customer_name}}</p> <p> {{trans "Your order #%increment_id has been updated with a status of <strong>%order_status</strong>." increment_id=$order.increment_id - order_status=$order.getFrontendStatusLabel() + order_status=$order_data.frontend_status_label |raw}} </p> <p>{{trans 'You can check the status of your order by <a href="%account_url">logging into your account</a>.' account_url=$this.getUrl($store,'customer/account/',[_nosid:1]) |raw}}</p> diff --git a/app/code/Magento/Sales/view/frontend/email/order_update_guest.html b/app/code/Magento/Sales/view/frontend/email/order_update_guest.html index 749fa3b60ad59..1ce0d162ed76e 100644 --- a/app/code/Magento/Sales/view/frontend/email/order_update_guest.html +++ b/app/code/Magento/Sales/view/frontend/email/order_update_guest.html @@ -4,25 +4,29 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Update to your %store_name order" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Update to your %store_name order" store_name=$store.frontend_name}} @--> <!--@vars { -"var billing.getName()":"Guest Customer Name", -"var comment":"Order Comment", +"var billing.name":"Guest Customer Name", +"var comment|escape|nl2br":"Order Comment", "var order.increment_id":"Order Id", -"var order.getFrontendStatusLabel()":"Order Status" +"var order_data.frontend_status_label":"Order Status", +"var store.frontend_name":"Store Frontend Name", +"var store_email":"Store Email", +"var store_phone":"Store Phone", +"var store_hours":"Store Hours" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$billing.getName()}}</p> + <p class="greeting">{{trans "%name," name=$billing.name}}</p> <p> {{trans "Your order #%increment_id has been updated with a status of <strong>%order_status</strong>." increment_id=$order.increment_id - order_status=$order.getFrontendStatusLabel() + order_status=$order_data.frontend_status_label |raw}} </p> <p> diff --git a/app/code/Magento/Sales/view/frontend/email/shipment_new.html b/app/code/Magento/Sales/view/frontend/email/shipment_new.html index 84f5acb29ea3b..39823a0c9d80b 100644 --- a/app/code/Magento/Sales/view/frontend/email/shipment_new.html +++ b/app/code/Magento/Sales/view/frontend/email/shipment_new.html @@ -4,29 +4,35 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Your %store_name order has shipped" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Your %store_name order has shipped" store_name=$store.frontend_name}} @--> <!--@vars { "var formattedBillingAddress|raw":"Billing Address", -"var this.getUrl($store, 'customer/account/')":"Customer Account URL", -"var order.getCustomerName()":"Customer Name", +"var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL", +"var order_data.customer_name":"Customer Name", "var order.increment_id":"Order Id", "var payment_html|raw":"Payment Details", -"var comment":"Shipment Comment", +"var comment|escape|nl2br":"Shipment Comment", "var shipment.increment_id":"Shipment Id", "layout handle=\"sales_email_order_shipment_items\" shipment=$shipment order=$order":"Shipment Items Grid", "block class='Magento\\\\Framework\\\\View\\\\Element\\\\Template' area='frontend' template='Magento_Sales::email\/shipment\/track.phtml' shipment=$shipment order=$order":"Shipment Track Details", "var formattedShippingAddress|raw":"Shipping Address", "var order.shipping_description":"Shipping Description", -"var order.getShippingDescription()":"Shipping Description" +"var store.frontend_name":"Store Frontend Name", +"var store_phone":"Store Phone", +"var store_email":"Store Email", +"var store_hours":"Store Hours", +"var order_data.is_not_virtual": "Order Type", +"var shipment": "Shipment", +"var order": "Order" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$order.getCustomerName()}}</p> + <p class="greeting">{{trans "%name," name=$order_data.customer_name}}</p> <p> - {{trans "Thank you for your order from %store_name." store_name=$store.getFrontendName()}} + {{trans "Thank you for your order from %store_name." store_name=$store.frontend_name}} {{trans 'You can check the status of your order by <a href="%account_url">logging into your account</a>.' account_url=$this.getUrl($store,'customer/account/',[_nosid:1]) |raw}} {{trans 'If you have questions about your order, you can email us at <a href="mailto:%store_email">%store_email</a>' store_email=$store_email |raw}}{{depend store_phone}} {{trans 'or call us at <a href="tel:%store_phone">%store_phone</a>' store_phone=$store_phone |raw}}{{/depend}}. {{depend store_hours}} @@ -60,7 +66,7 @@ <h1>{{trans "Your Shipment #%shipment_id for Order #%order_id" shipment_id=$ship <h3>{{trans "Billing Info"}}</h3> <p>{{var formattedBillingAddress|raw}}</p> </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="address-details"> <h3>{{trans "Shipping Info"}}</h3> <p>{{var formattedShippingAddress|raw}}</p> @@ -72,10 +78,10 @@ <h3>{{trans "Shipping Info"}}</h3> <h3>{{trans "Payment Method"}}</h3> {{var payment_html|raw}} </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="method-info"> <h3>{{trans "Shipping Method"}}</h3> - <p>{{var order.getShippingDescription()}}</p> + <p>{{var order.shipping_description}}</p> </td> {{/depend}} </tr> diff --git a/app/code/Magento/Sales/view/frontend/email/shipment_new_guest.html b/app/code/Magento/Sales/view/frontend/email/shipment_new_guest.html index bb181126724da..ed2f52ed85066 100644 --- a/app/code/Magento/Sales/view/frontend/email/shipment_new_guest.html +++ b/app/code/Magento/Sales/view/frontend/email/shipment_new_guest.html @@ -4,28 +4,34 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Your %store_name order has shipped" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Your %store_name order has shipped" store_name=$store.frontend_name}} @--> <!--@vars { "var formattedBillingAddress|raw":"Billing Address", -"var billing.getName()":"Guest Customer Name", +"var billing.name":"Guest Customer Name", "var order.increment_id":"Order Id", "var payment_html|raw":"Payment Details", -"var comment":"Shipment Comment", +"var comment|escape|nl2br":"Shipment Comment", "var shipment.increment_id":"Shipment Id", "layout handle=\"sales_email_order_shipment_items\" shipment=$shipment order=$order":"Shipment Items Grid", "block class='Magento\\\\Framework\\\\View\\\\Element\\\\Template' area='frontend' template='Magento_Sales::email\/shipment\/track.phtml' shipment=$shipment order=$order":"Shipment Track Details", "var formattedShippingAddress|raw":"Shipping Address", "var order.shipping_description":"Shipping Description", -"var order.getShippingDescription()":"Shipping Description" +"var store.frontend_name":"Store Frontend Name", +"var store_phone":"Store Phone", +"var store_email":"Store Email", +"var store_hours":"Store Hours", +"var order_data.is_not_virtual": "Order Type", +"var shipment": "Shipment", +"var order": "Order" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$billing.getName()}}</p> + <p class="greeting">{{trans "%name," name=$billing.name}}</p> <p> - {{trans "Thank you for your order from %store_name." store_name=$store.getFrontendName()}} + {{trans "Thank you for your order from %store_name." store_name=$store.frontend_name}} {{trans 'If you have questions about your order, you can email us at <a href="mailto:%store_email">%store_email</a>' store_email=$store_email |raw}}{{depend store_phone}} {{trans 'or call us at <a href="tel:%store_phone">%store_phone</a>' store_phone=$store_phone |raw}}{{/depend}}. {{depend store_hours}} {{trans 'Our hours are <span class="no-link">%store_hours</span>.' store_hours=$store_hours |raw}} @@ -58,7 +64,7 @@ <h1>{{trans "Your Shipment #%shipment_id for Order #%order_id" shipment_id=$ship <h3>{{trans "Billing Info"}}</h3> <p>{{var formattedBillingAddress|raw}}</p> </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="address-details"> <h3>{{trans "Shipping Info"}}</h3> <p>{{var formattedShippingAddress|raw}}</p> @@ -70,10 +76,10 @@ <h3>{{trans "Shipping Info"}}</h3> <h3>{{trans "Payment Method"}}</h3> {{var payment_html|raw}} </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="method-info"> <h3>{{trans "Shipping Method"}}</h3> - <p>{{var order.getShippingDescription()}}</p> + <p>{{var order.shipping_description}}</p> </td> {{/depend}} </tr> diff --git a/app/code/Magento/Sales/view/frontend/email/shipment_update.html b/app/code/Magento/Sales/view/frontend/email/shipment_update.html index 9d1c93287549a..9d0057f78df7f 100644 --- a/app/code/Magento/Sales/view/frontend/email/shipment_update.html +++ b/app/code/Magento/Sales/view/frontend/email/shipment_update.html @@ -4,27 +4,31 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Update to your %store_name shipment" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Update to your %store_name shipment" store_name=$store.frontend_name}} @--> <!--@vars { -"var this.getUrl($store, 'customer/account/')":"Customer Account URL", -"var order.getCustomerName()":"Customer Name", -"var comment":"Order Comment", +"var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL", +"var order_data.customer_name":"Customer Name", +"var comment|escape|nl2br":"Order Comment", "var order.increment_id":"Order Id", -"var order.getFrontendStatusLabel()":"Order Status", -"var shipment.increment_id":"Shipment Id" +"var order_data.frontend_status_label":"Order Status", +"var shipment.increment_id":"Shipment Id", +"var store.frontend_name":"Store Frontend Name", +"var store_phone":"Store Phone", +"var store_email":"Store Email", +"var store_hours":"Store Hours" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$order.getCustomerName()}}</p> + <p class="greeting">{{trans "%name," name=$order_data.customer_name}}</p> <p> {{trans "Your order #%increment_id has been updated with a status of <strong>%order_status</strong>." increment_id=$order.increment_id - order_status=$order.getFrontendStatusLabel() + order_status=$order_data.frontend_status_label |raw}} </p> <p>{{trans 'You can check the status of your order by <a href="%account_url">logging into your account</a>.' account_url=$this.getUrl($store,'customer/account/',[_nosid:1]) |raw}}</p> diff --git a/app/code/Magento/Sales/view/frontend/email/shipment_update_guest.html b/app/code/Magento/Sales/view/frontend/email/shipment_update_guest.html index 0d2dccd3377d2..087cb0ddbf5bc 100644 --- a/app/code/Magento/Sales/view/frontend/email/shipment_update_guest.html +++ b/app/code/Magento/Sales/view/frontend/email/shipment_update_guest.html @@ -4,26 +4,30 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Update to your %store_name shipment" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Update to your %store_name shipment" store_name=$store.frontend_name}} @--> <!--@vars { -"var billing.getName()":"Guest Customer Name", -"var comment":"Order Comment", +"var billing.name":"Guest Customer Name", +"var comment|escape|nl2br":"Order Comment", "var order.increment_id":"Order Id", -"var order.getFrontendStatusLabel()":"Order Status", -"var shipment.increment_id":"Shipment Id" +"var order_data.frontend_status_label":"Order Status", +"var shipment.increment_id":"Shipment Id", +"var store.frontend_name":"Store Frontend Name", +"var store_phone":"Store Phone", +"var store_email":"Store Email", +"var store_hours":"Store Hours" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$billing.getName()}}</p> + <p class="greeting">{{trans "%name," name=$billing.name}}</p> <p> {{trans "Your order #%increment_id has been updated with a status of <strong>%order_status</strong>." increment_id=$order.increment_id - order_status=$order.getFrontendStatusLabel() + order_status=$order_data.frontend_status_label |raw}} </p> <p> diff --git a/app/code/Magento/Sales/view/frontend/templates/order/creditmemo/items/renderer/default.phtml b/app/code/Magento/Sales/view/frontend/templates/order/creditmemo/items/renderer/default.phtml index 9c0bf0182c62e..b2e84691a45cf 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/creditmemo/items/renderer/default.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/creditmemo/items/renderer/default.phtml @@ -17,7 +17,7 @@ <?php if (!$block->getPrintStatus()) : ?> <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> <dd<?= (isset($_formatedOptionValue['full_view']) ? ' class="tooltip wrapper"' : '') ?>> - <?= $block->escapeHtml($_formatedOptionValue['value']) ?> + <?= $block->escapeHtml($_formatedOptionValue['value'], ['a', 'img']) ?> <?php if (isset($_formatedOptionValue['full_view'])) : ?> <div class="tooltip content"> <dl class="item options"> diff --git a/app/code/Magento/Sales/view/frontend/templates/order/history.phtml b/app/code/Magento/Sales/view/frontend/templates/order/history.phtml index ef56ad69dcb1b..bc1887c7669ca 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/history.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/history.phtml @@ -5,6 +5,7 @@ */ // phpcs:disable Magento2.Templates.ThisInTemplate +// @codingStandardsIgnoreFile /** @var \Magento\Sales\Block\Order\History $block */ ?> @@ -19,7 +20,6 @@ <th scope="col" class="col id"><?= $block->escapeHtml(__('Order #')) ?></th> <th scope="col" class="col date"><?= $block->escapeHtml(__('Date')) ?></th> <?= $block->getChildHtml('extra.column.header') ?> - <th scope="col" class="col shipping"><?= $block->escapeHtml(__('Ship To')) ?></th> <th scope="col" class="col total"><?= $block->escapeHtml(__('Order Total')) ?></th> <th scope="col" class="col status"><?= $block->escapeHtml(__('Status')) ?></th> <th scope="col" class="col actions"><?= $block->escapeHtml(__('Action')) ?></th> @@ -35,7 +35,6 @@ <?php $extra->setOrder($_order); ?> <?= $extra->getChildHtml() ?> <?php endif; ?> - <td data-th="<?= $block->escapeHtml(__('Ship To')) ?>" class="col shipping"><?= $_order->getShippingAddress() ? $block->escapeHtml($_order->getShippingAddress()->getName()) : ' ' ?></td> <td data-th="<?= $block->escapeHtml(__('Order Total')) ?>" class="col total"><?= /* @noEscape */ $_order->formatPrice($_order->getGrandTotal()) ?></td> <td data-th="<?= $block->escapeHtml(__('Status')) ?>" class="col status"><?= $block->escapeHtml($_order->getStatusLabel()) ?></td> <td data-th="<?= $block->escapeHtml(__('Actions')) ?>" class="col actions"> @@ -60,5 +59,5 @@ <div class="order-products-toolbar toolbar bottom"><?= $block->getPagerHtml() ?></div> <?php endif ?> <?php else : ?> - <div class="message info empty"><span><?= $block->escapeHtml(__('You have placed no orders.')) ?></span></div> + <div class="message info empty"><span><?= $block->escapeHtml($block->getEmptyOrdersMessage()) ?></span></div> <?php endif ?> diff --git a/app/code/Magento/Sales/view/frontend/templates/order/invoice/items/renderer/default.phtml b/app/code/Magento/Sales/view/frontend/templates/order/invoice/items/renderer/default.phtml index 1c427e8b6d4e2..0176582f0fcd7 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/invoice/items/renderer/default.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/invoice/items/renderer/default.phtml @@ -17,7 +17,7 @@ <?php if (!$block->getPrintStatus()) : ?> <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> <dd<?= (isset($_formatedOptionValue['full_view']) ? ' class="tooltip wrapper"' : '') ?>> - <?= $block->escapeHtml($_formatedOptionValue['value']) ?> + <?= $block->escapeHtml($_formatedOptionValue['value'], ['a', 'img']) ?> <?php if (isset($_formatedOptionValue['full_view'])) : ?> <div class="tooltip content"> <dl class="item options"> diff --git a/app/code/Magento/Sales/view/frontend/templates/order/items/renderer/default.phtml b/app/code/Magento/Sales/view/frontend/templates/order/items/renderer/default.phtml index 4042fe52bb5a8..51e43476238be 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/items/renderer/default.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/items/renderer/default.phtml @@ -16,17 +16,19 @@ $_item = $block->getItem(); <dt><?= $block->escapeHtml($_option['label']) ?></dt> <?php if (!$block->getPrintStatus()) : ?> <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> - <dd> + <dd<?= (isset($_formatedOptionValue['full_view']) ? ' class="tooltip wrapper"' : '') ?>> + <?= $block->escapeHtml($_formatedOptionValue['value'], ['a', 'img']) ?> <?php if (isset($_formatedOptionValue['full_view'])) : ?> - <?= $block->escapeHtml($_formatedOptionValue['full_view'], ['a']) ?> - <?php else : ?> - <?=$block->escapeHtml($_formatedOptionValue['value'], ['a']) ?> + <div class="tooltip content"> + <dl class="item options"> + <dt><?= $block->escapeHtml($_option['label']) ?></dt> + <dd><?= $block->escapeHtml($_formatedOptionValue['full_view']) ?></dd> + </dl> + </div> <?php endif; ?> </dd> <?php else : ?> - <dd> - <?= /* @noEscape */ nl2br($block->escapeHtml($_option['print_value'] ?? $_option['value'])) ?> - </dd> + <dd><?= $block->escapeHtml((isset($_option['print_value']) ? $_option['print_value'] : $_option['value'])) ?></dd> <?php endif; ?> <?php endforeach; ?> </dl> diff --git a/app/code/Magento/Sales/view/frontend/templates/order/shipment/items/renderer/default.phtml b/app/code/Magento/Sales/view/frontend/templates/order/shipment/items/renderer/default.phtml index 57aeffb26f823..26fe74b0fc454 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/shipment/items/renderer/default.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/shipment/items/renderer/default.phtml @@ -16,7 +16,7 @@ <?php if (!$block->getPrintStatus()) : ?> <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> <dd<?= (isset($_formatedOptionValue['full_view']) ? ' class="tooltip wrapper"' : '') ?>> - <?= $block->escapeHtml($_formatedOptionValue['value']) ?> + <?= $block->escapeHtml($_formatedOptionValue['value'], ['a', 'img']) ?> <?php if (isset($_formatedOptionValue['full_view'])) : ?> <div class="tooltip content"> <dl class="item options"> diff --git a/app/code/Magento/SalesGraphQl/etc/schema.graphqls b/app/code/Magento/SalesGraphQl/etc/schema.graphqls index a7c30f582e752..a687ee59031ea 100644 --- a/app/code/Magento/SalesGraphQl/etc/schema.graphqls +++ b/app/code/Magento/SalesGraphQl/etc/schema.graphqls @@ -7,7 +7,7 @@ type Query { type CustomerOrder @doc(description: "Order mapping fields") { id: Int - increment_id: String @deprecated(reason: "Use the order_number instaed.") + increment_id: String @deprecated(reason: "Use the order_number instead.") order_number: String! @doc(description: "The order number") created_at: String grand_total: Float diff --git a/app/code/Magento/SalesInventory/registration.php b/app/code/Magento/SalesInventory/registration.php index a6c9a6bc3e669..f2c0adf20a039 100644 --- a/app/code/Magento/SalesInventory/registration.php +++ b/app/code/Magento/SalesInventory/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_SalesInventory', __DIR__); diff --git a/app/code/Magento/SalesRule/Api/Data/DiscountDataInterface.php b/app/code/Magento/SalesRule/Api/Data/DiscountDataInterface.php new file mode 100644 index 0000000000000..38ac93dc9762f --- /dev/null +++ b/app/code/Magento/SalesRule/Api/Data/DiscountDataInterface.php @@ -0,0 +1,42 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SalesRule\Api\Data; + +/** + * Discount Data Interface + */ +interface DiscountDataInterface +{ + /** + * Get Amount + * + * @return float + */ + public function getAmount(); + + /** + * Get Base Amount + * + * @return float + */ + public function getBaseAmount(); + + /** + * Get Original Amount + * + * @return float + */ + public function getOriginalAmount(); + + /** + * Get Base Original Amount + * + * @return float + */ + public function getBaseOriginalAmount(); +} diff --git a/app/code/Magento/SalesRule/Api/Data/RuleDiscountInterface.php b/app/code/Magento/SalesRule/Api/Data/RuleDiscountInterface.php new file mode 100644 index 0000000000000..8d2b73476af46 --- /dev/null +++ b/app/code/Magento/SalesRule/Api/Data/RuleDiscountInterface.php @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\SalesRule\Api\Data; + +/** + * Rule discount Interface + */ +interface RuleDiscountInterface +{ + /** + * Get Discount Data + * + * @return \Magento\SalesRule\Api\Data\DiscountDataInterface + */ + public function getDiscountData(); + + /** + * Get Rule Label + * + * @return string + */ + public function getRuleLabel(); + + /** + * Get Rule ID + * + * @return int + */ + public function getRuleID(); +} diff --git a/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Coupons/Grid.php b/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Coupons/Grid.php index bf92e21827a01..b021b3e539a9d 100644 --- a/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Coupons/Grid.php +++ b/app/code/Magento/SalesRule/Block/Adminhtml/Promo/Quote/Edit/Tab/Coupons/Grid.php @@ -100,7 +100,7 @@ protected function _prepareColumns() $this->addColumn( 'used', [ - 'header' => __('Uses'), + 'header' => __('Used'), 'index' => 'times_used', 'width' => '100', 'type' => 'options', diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsCsv.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsCsv.php index 0f073ab4d65ce..53459f2c3e52f 100644 --- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsCsv.php +++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsCsv.php @@ -1,33 +1,40 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\SalesRule\Controller\Adminhtml\Promo\Quote; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Controller\ResultFactory; +use Magento\SalesRule\Controller\Adminhtml\Promo\Quote; +use Magento\SalesRule\Block\Adminhtml\Promo\Quote\Edit\Tab\Coupons\Grid; +use Magento\Framework\View\Result\Layout; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\App\Action\HttpGetActionInterface; -class ExportCouponsCsv extends \Magento\SalesRule\Controller\Adminhtml\Promo\Quote +/** + * Export Coupons to csv file + * + * Class \Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCouponsCsv + */ +class ExportCouponsCsv extends Quote implements HttpGetActionInterface { /** * Export coupon codes as CSV file * - * @return \Magento\Framework\App\ResponseInterface|null + * @return ResponseInterface|null */ public function execute() { $this->_initRule(); - $rule = $this->_coreRegistry->registry(\Magento\SalesRule\Model\RegistryConstants::CURRENT_SALES_RULE); - if ($rule->getId()) { - $fileName = 'coupon_codes.csv'; - $content = $this->_view->getLayout()->createBlock( - \Magento\SalesRule\Block\Adminhtml\Promo\Quote\Edit\Tab\Coupons\Grid::class - )->getCsvFile(); - return $this->_fileFactory->create($fileName, $content, DirectoryList::VAR_DIR); - } else { - $this->_redirect('sales_rule/*/detail', ['_current' => true]); - return; - } + $fileName = 'coupon_codes.csv'; + /** @var Layout $resultLayout */ + $resultLayout = $this->resultFactory->create(ResultFactory::TYPE_LAYOUT); + $content = $resultLayout->getLayout()->createBlock(Grid::class)->getCsvFile(); + return $this->_fileFactory->create($fileName, $content, DirectoryList::VAR_DIR); } } diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsXml.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsXml.php index 35d81d3a971fc..fa3d4455410c4 100644 --- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsXml.php +++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsXml.php @@ -1,35 +1,40 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\SalesRule\Controller\Adminhtml\Promo\Quote; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Controller\ResultFactory; +use Magento\SalesRule\Controller\Adminhtml\Promo\Quote; +use Magento\SalesRule\Block\Adminhtml\Promo\Quote\Edit\Tab\Coupons\Grid; +use Magento\Framework\View\Result\Layout; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\App\Action\HttpGetActionInterface; -class ExportCouponsXml extends \Magento\SalesRule\Controller\Adminhtml\Promo\Quote +/** + * Export coupons to xml file + * + * Class \Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCouponsXml + */ +class ExportCouponsXml extends Quote implements HttpGetActionInterface { /** * Export coupon codes as excel xml file * - * @return \Magento\Framework\App\ResponseInterface|null + * @return ResponseInterface|null */ public function execute() { $this->_initRule(); - $rule = $this->_coreRegistry->registry(\Magento\SalesRule\Model\RegistryConstants::CURRENT_SALES_RULE); - if ($rule->getId()) { - $fileName = 'coupon_codes.xml'; - $content = $this->_view->getLayout()->createBlock( - \Magento\SalesRule\Block\Adminhtml\Promo\Quote\Edit\Tab\Coupons\Grid::class - )->getExcelFile( - $fileName - ); - return $this->_fileFactory->create($fileName, $content, DirectoryList::VAR_DIR); - } else { - $this->_redirect('sales_rule/*/detail', ['_current' => true]); - return; - } + $fileName = 'coupon_codes.xml'; + /** @var Layout $resultLayout */ + $resultLayout = $this->resultFactory->create(ResultFactory::TYPE_LAYOUT); + $content = $resultLayout->getLayout()->createBlock(Grid::class)->getExcelFile($fileName); + return $this->_fileFactory->create($fileName, $content, DirectoryList::VAR_DIR); } } diff --git a/app/code/Magento/SalesRule/Model/Coupon/UpdateCouponUsages.php b/app/code/Magento/SalesRule/Model/Coupon/UpdateCouponUsages.php index c4f7652e1a334..3236c80e1b7ed 100644 --- a/app/code/Magento/SalesRule/Model/Coupon/UpdateCouponUsages.php +++ b/app/code/Magento/SalesRule/Model/Coupon/UpdateCouponUsages.php @@ -7,7 +7,7 @@ namespace Magento\SalesRule\Model\Coupon; -use Magento\Sales\Model\Order; +use Magento\Sales\Api\Data\OrderInterface; use Magento\SalesRule\Model\Coupon; use Magento\SalesRule\Model\ResourceModel\Coupon\Usage; use Magento\SalesRule\Model\Rule\CustomerFactory; @@ -59,11 +59,11 @@ public function __construct( /** * Executes the current command. * - * @param Order $subject + * @param OrderInterface $subject * @param bool $increment - * @return Order + * @return OrderInterface */ - public function execute(Order $subject, bool $increment): Order + public function execute(OrderInterface $subject, bool $increment): OrderInterface { if (!$subject || !$subject->getAppliedRuleIds()) { return $subject; @@ -133,11 +133,11 @@ private function updateCustomerRuleUsages(bool $increment, int $ruleId, int $cus /** * Update the number of coupon usages. * - * @param Order $subject + * @param OrderInterface $subject * @param bool $increment * @param int $customerId */ - private function updateCouponUsages(Order $subject, bool $increment, int $customerId): void + private function updateCouponUsages(OrderInterface $subject, bool $increment, int $customerId): void { $this->coupon->load($subject->getCouponCode(), 'code'); if ($this->coupon->getId()) { diff --git a/app/code/Magento/SalesRule/Model/Data/DiscountData.php b/app/code/Magento/SalesRule/Model/Data/DiscountData.php new file mode 100644 index 0000000000000..cfad4b5c09c55 --- /dev/null +++ b/app/code/Magento/SalesRule/Model/Data/DiscountData.php @@ -0,0 +1,129 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SalesRule\Model\Data; + +use Magento\SalesRule\Api\Data\DiscountDataInterface; +use Magento\Framework\Api\ExtensionAttributesInterface; + +/** + * Discount Data Model + */ +class DiscountData extends \Magento\Framework\Api\AbstractExtensibleObject implements DiscountDataInterface +{ + + const AMOUNT = 'amount'; + const BASE_AMOUNT = 'base_amount'; + const ORIGINAL_AMOUNT = 'original_amount'; + const BASE_ORIGINAL_AMOUNT = 'base_original_amount'; + + /** + * Get Amount + * + * @return float + */ + public function getAmount() + { + return $this->_get(self::AMOUNT); + } + + /** + * Set Amount + * + * @param float $amount + * @return $this + */ + public function setAmount(float $amount) + { + return $this->setData(self::AMOUNT, $amount); + } + + /** + * Get Base Amount + * + * @return float + */ + public function getBaseAmount() + { + return $this->_get(self::BASE_AMOUNT); + } + + /** + * Set Base Amount + * + * @param float $amount + * @return $this + */ + public function setBaseAmount(float $amount) + { + return $this->setData(self::BASE_AMOUNT, $amount); + } + + /** + * Get Original Amount + * + * @return float + */ + public function getOriginalAmount() + { + return $this->_get(self::ORIGINAL_AMOUNT); + } + + /** + * Set Original Amount + * + * @param float $amount + * @return $this + */ + public function setOriginalAmount(float $amount) + { + return $this->setData(self::ORIGINAL_AMOUNT, $amount); + } + + /** + * Get Base Original Amount + * + * @return float + */ + public function getBaseOriginalAmount() + { + return $this->_get(self::BASE_ORIGINAL_AMOUNT); + } + + /** + * Set Base Original Amount + * + * @param float $amount + * @return $this + */ + public function setBaseOriginalAmount(float $amount) + { + return $this->setData(self::BASE_ORIGINAL_AMOUNT, $amount); + } + + /** + * Retrieve existing extension attributes object or create a new one. + * + * @return ExtensionAttributesInterface|null + */ + public function getExtensionAttributes() + { + return $this->_getExtensionAttributes(); + } + + /** + * Set an extension attributes object. + * + * @param ExtensionAttributesInterface $extensionAttributes + * @return $this + */ + public function setExtensionAttributes( + ExtensionAttributesInterface $extensionAttributes + ) { + return $this->_setExtensionAttributes($extensionAttributes); + } +} diff --git a/app/code/Magento/SalesRule/Model/Data/RuleDiscount.php b/app/code/Magento/SalesRule/Model/Data/RuleDiscount.php new file mode 100644 index 0000000000000..aaf657c06b4fc --- /dev/null +++ b/app/code/Magento/SalesRule/Model/Data/RuleDiscount.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SalesRule\Model\Data; + +use Magento\Framework\Api\ExtensionAttributesInterface; +use Magento\SalesRule\Api\Data\RuleDiscountInterface; +use Magento\Framework\Api\AbstractExtensibleObject; + +/** + * Data Model for Rule Discount + */ +class RuleDiscount extends AbstractExtensibleObject implements RuleDiscountInterface +{ + const KEY_DISCOUNT_DATA = 'discount'; + const KEY_RULE_LABEL = 'rule'; + const KEY_RULE_ID = 'rule_id'; + + /** + * Get Discount Data + * + * @return \Magento\SalesRule\Model\Rule\Action\Discount\Data + */ + public function getDiscountData() + { + return $this->_get(self::KEY_DISCOUNT_DATA); + } + + /** + * Get Rule Label + * + * @return string + */ + public function getRuleLabel() + { + return $this->_get(self::KEY_RULE_LABEL); + } + + /** + * Get Rule ID + * + * @return int + */ + public function getRuleID() + { + return $this->_get(self::KEY_RULE_ID); + } + + /** + * Retrieve existing extension attributes object or create a new one. + * + * @return ExtensionAttributesInterface|null + */ + public function getExtensionAttributes() + { + return $this->_getExtensionAttributes(); + } + + /** + * Set an extension attributes object. + * + * @param ExtensionAttributesInterface $extensionAttributes + * @return $this + */ + public function setExtensionAttributes( + ExtensionAttributesInterface $extensionAttributes + ) { + return $this->_setExtensionAttributes($extensionAttributes); + } +} diff --git a/app/code/Magento/SalesRule/Model/Quote/Discount.php b/app/code/Magento/SalesRule/Model/Quote/Discount.php index 315ce874513a3..69abac8309f90 100644 --- a/app/code/Magento/SalesRule/Model/Quote/Discount.php +++ b/app/code/Magento/SalesRule/Model/Quote/Discount.php @@ -5,6 +5,10 @@ */ namespace Magento\SalesRule\Model\Quote; +use Magento\Framework\App\ObjectManager; +use Magento\SalesRule\Api\Data\RuleDiscountInterfaceFactory; +use Magento\SalesRule\Api\Data\DiscountDataInterfaceFactory; + /** * Discount totals calculation model. */ @@ -36,23 +40,41 @@ class Discount extends \Magento\Quote\Model\Quote\Address\Total\AbstractTotal */ protected $priceCurrency; + /** + * @var RuleDiscountInterfaceFactory + */ + private $discountInterfaceFactory; + + /** + * @var DiscountDataInterfaceFactory + */ + private $discountDataInterfaceFactory; + /** * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\SalesRule\Model\Validator $validator * @param \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency + * @param RuleDiscountInterfaceFactory|null $discountInterfaceFactory + * @param DiscountDataInterfaceFactory|null $discountDataInterfaceFactory */ public function __construct( \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\SalesRule\Model\Validator $validator, - \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency + \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency, + RuleDiscountInterfaceFactory $discountInterfaceFactory = null, + DiscountDataInterfaceFactory $discountDataInterfaceFactory = null ) { $this->setCode(self::COLLECTOR_TYPE_CODE); $this->eventManager = $eventManager; $this->calculator = $validator; $this->storeManager = $storeManager; $this->priceCurrency = $priceCurrency; + $this->discountInterfaceFactory = $discountInterfaceFactory + ?: ObjectManager::getInstance()->get(RuleDiscountInterfaceFactory::class); + $this->discountDataInterfaceFactory = $discountDataInterfaceFactory + ?: ObjectManager::getInstance()->get(DiscountDataInterfaceFactory::class); } /** @@ -91,6 +113,8 @@ public function collect( $address->setDiscountDescription([]); $items = $this->calculator->sortItemsByPriority($items, $address); + $address->getExtensionAttributes()->setDiscounts([]); + $addressDiscountAggregator = []; /** @var \Magento\Quote\Model\Quote\Item $item */ foreach ($items as $item) { @@ -127,13 +151,15 @@ public function collect( $this->calculator->process($item); $this->aggregateItemDiscount($item, $total); } + if ($item->getExtensionAttributes()) { + $this->aggregateDiscountPerRule($item, $address, $addressDiscountAggregator); + } } $this->calculator->prepareDescription($address); $total->setDiscountDescription($address->getDiscountDescription()); $total->setSubtotalWithDiscount($total->getSubtotal() + $total->getDiscountAmount()); $total->setBaseSubtotalWithDiscount($total->getBaseSubtotal() + $total->getBaseDiscountAmount()); - $address->setDiscountAmount($total->getDiscountAmount()); $address->setBaseDiscountAmount($total->getBaseDiscountAmount()); @@ -218,4 +244,56 @@ public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Qu } return $result; } + + /** + * Aggregates discount per rule + * + * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item + * @param \Magento\Quote\Api\Data\AddressInterface $address + * @param array $addressDiscountAggregator + * @return void + */ + private function aggregateDiscountPerRule( + \Magento\Quote\Model\Quote\Item\AbstractItem $item, + \Magento\Quote\Api\Data\AddressInterface $address, + array &$addressDiscountAggregator + ) { + $discountBreakdown = $item->getExtensionAttributes()->getDiscounts(); + if ($discountBreakdown) { + foreach ($discountBreakdown as $value) { + /* @var \Magento\SalesRule\Api\Data\DiscountDataInterface $discount */ + $discount = $value->getDiscountData(); + $ruleLabel = $value->getRuleLabel(); + $ruleID = $value->getRuleID(); + if (isset($addressDiscountAggregator[$ruleID])) { + /** @var \Magento\SalesRule\Model\Data\RuleDiscount $cartDiscount */ + $cartDiscount = $addressDiscountAggregator[$ruleID]; + $discountData = $cartDiscount->getDiscountData(); + $discountData->setBaseAmount($discountData->getBaseAmount()+$discount->getBaseAmount()); + $discountData->setAmount($discountData->getAmount()+$discount->getAmount()); + $discountData->setOriginalAmount($discountData->getOriginalAmount()+$discount->getOriginalAmount()); + $discountData->setBaseOriginalAmount( + $discountData->getBaseOriginalAmount()+$discount->getBaseOriginalAmount() + ); + } else { + $data = [ + 'amount' => $discount->getAmount(), + 'base_amount' => $discount->getBaseAmount(), + 'original_amount' => $discount->getOriginalAmount(), + 'base_original_amount' => $discount->getBaseOriginalAmount() + ]; + $discountData = $this->discountDataInterfaceFactory->create(['data' => $data]); + $data = [ + 'discount' => $discountData, + 'rule' => $ruleLabel, + 'rule_id' => $ruleID, + ]; + /** @var \Magento\SalesRule\Model\Data\RuleDiscount $cartDiscount */ + $cartDiscount = $this->discountInterfaceFactory->create(['data' => $data]); + $addressDiscountAggregator[$ruleID] = $cartDiscount; + } + } + } + $address->getExtensionAttributes()->setDiscounts(array_values($addressDiscountAggregator)); + } } diff --git a/app/code/Magento/SalesRule/Model/Rule/Action/Discount/Data.php b/app/code/Magento/SalesRule/Model/Rule/Action/Discount/Data.php index fe564690e08b8..f9892fb48547c 100644 --- a/app/code/Magento/SalesRule/Model/Rule/Action/Discount/Data.php +++ b/app/code/Magento/SalesRule/Model/Rule/Action/Discount/Data.php @@ -6,6 +6,8 @@ namespace Magento\SalesRule\Model\Rule\Action\Discount; /** + * Discount Data + * * @api * @since 100.0.2 */ @@ -43,6 +45,8 @@ public function __construct() } /** + * Set Amount + * * @param float $amount * @return $this */ @@ -53,6 +57,8 @@ public function setAmount($amount) } /** + * Get Amount + * * @return float */ public function getAmount() @@ -61,6 +67,8 @@ public function getAmount() } /** + * Set Base Amount + * * @param float $baseAmount * @return $this */ @@ -71,6 +79,8 @@ public function setBaseAmount($baseAmount) } /** + * Get Base Amount + * * @return float */ public function getBaseAmount() @@ -79,6 +89,8 @@ public function getBaseAmount() } /** + * Set Original Amount + * * @param float $originalAmount * @return $this */ @@ -99,6 +111,8 @@ public function getOriginalAmount() } /** + * Set Base Original Amount + * * @param float $baseOriginalAmount * @return $this */ diff --git a/app/code/Magento/SalesRule/Model/RulesApplier.php b/app/code/Magento/SalesRule/Model/RulesApplier.php index a50046e38775a..270732c8e0278 100644 --- a/app/code/Magento/SalesRule/Model/RulesApplier.php +++ b/app/code/Magento/SalesRule/Model/RulesApplier.php @@ -12,6 +12,8 @@ use Magento\SalesRule\Model\ResourceModel\Rule\Collection; use Magento\SalesRule\Model\Rule\Action\Discount\CalculatorFactory; use Magento\SalesRule\Model\Rule\Action\Discount\DataFactory; +use Magento\SalesRule\Api\Data\RuleDiscountInterfaceFactory; +use Magento\SalesRule\Api\Data\DiscountDataInterfaceFactory; /** * Class RulesApplier @@ -48,18 +50,38 @@ class RulesApplier protected $discountFactory; /** + * @var RuleDiscountInterfaceFactory + */ + private $discountInterfaceFactory; + + /** + * @var DiscountDataInterfaceFactory + */ + private $discountDataInterfaceFactory; + + /** + * @var array + */ + private $discountAggregator; + + /** + * RulesApplier constructor. * @param CalculatorFactory $calculatorFactory * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param Utility $utility * @param ChildrenValidationLocator|null $childrenValidationLocator - * @param DataFactory $discountDataFactory + * @param DataFactory|null $discountDataFactory + * @param RuleDiscountInterfaceFactory|null $discountInterfaceFactory + * @param DiscountDataInterfaceFactory|null $discountDataInterfaceFactory */ public function __construct( \Magento\SalesRule\Model\Rule\Action\Discount\CalculatorFactory $calculatorFactory, \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\SalesRule\Model\Utility $utility, ChildrenValidationLocator $childrenValidationLocator = null, - DataFactory $discountDataFactory = null + DataFactory $discountDataFactory = null, + RuleDiscountInterfaceFactory $discountInterfaceFactory = null, + DiscountDataInterfaceFactory $discountDataInterfaceFactory = null ) { $this->calculatorFactory = $calculatorFactory; $this->validatorUtility = $utility; @@ -67,6 +89,10 @@ public function __construct( $this->childrenValidationLocator = $childrenValidationLocator ?: ObjectManager::getInstance()->get(ChildrenValidationLocator::class); $this->discountFactory = $discountDataFactory ?: ObjectManager::getInstance()->get(DataFactory::class); + $this->discountInterfaceFactory = $discountInterfaceFactory + ?: ObjectManager::getInstance()->get(RuleDiscountInterfaceFactory::class); + $this->discountDataInterfaceFactory = $discountDataInterfaceFactory + ?: ObjectManager::getInstance()->get(DiscountDataInterfaceFactory::class); } /** @@ -83,6 +109,7 @@ public function applyRules($item, $rules, $skipValidation, $couponCode) { $address = $item->getAddress(); $appliedRuleIds = []; + $this->discountAggregator = []; /* @var $rule Rule */ foreach ($rules as $rule) { if (!$this->validatorUtility->canProcessRule($rule, $address)) { @@ -162,7 +189,7 @@ public function addDiscountDescription($address, $rule) */ protected function applyRule($item, $rule, $address, $couponCode) { - $discountData = $this->getDiscountData($item, $rule); + $discountData = $this->getDiscountData($item, $rule, $address); $this->setDiscountData($discountData, $item); $this->maintainAddressCouponCode($address, $rule, $couponCode); @@ -175,10 +202,11 @@ protected function applyRule($item, $rule, $address, $couponCode) * Get discount Data * * @param AbstractItem $item - * @param Rule $rule + * @param \Magento\SalesRule\Model\Rule $rule + * @param \Magento\Quote\Model\Quote\Address $address * @return \Magento\SalesRule\Model\Rule\Action\Discount\Data */ - protected function getDiscountData($item, $rule) + protected function getDiscountData($item, $rule, $address) { $qty = $this->validatorUtility->getItemQty($item, $rule); @@ -187,7 +215,7 @@ protected function getDiscountData($item, $rule) $discountData = $discountCalculator->calculate($rule, $item, $qty); $this->eventFix($discountData, $item, $rule, $qty); $this->validatorUtility->deltaRoundingFix($discountData, $item); - $this->setDiscountBreakdown($discountData, $item, $rule); + $this->setDiscountBreakdown($discountData, $item, $rule, $address); /** * We can't use row total here because row total not include tax @@ -205,21 +233,29 @@ protected function getDiscountData($item, $rule) * @param \Magento\SalesRule\Model\Rule\Action\Discount\Data $discountData * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item * @param \Magento\SalesRule\Model\Rule $rule + * @param \Magento\Quote\Model\Quote\Address $address * @return $this */ - private function setDiscountBreakdown($discountData, $item, $rule) + private function setDiscountBreakdown($discountData, $item, $rule, $address) { if ($discountData->getAmount() > 0 && $item->getExtensionAttributes()) { - /** @var \Magento\SalesRule\Model\Rule\Action\Discount\Data $discount */ - $discount = $this->discountFactory->create(); - $discount->setBaseOriginalAmount($discountData->getBaseOriginalAmount()); - $discount->setAmount($discountData->getAmount()); - $discount->setBaseAmount($discountData->getBaseAmount()); - $discount->setOriginalAmount($discountData->getOriginalAmount()); - $discountBreakdown = $item->getExtensionAttributes()->getDiscounts() ?? []; - $discountBreakdown[$rule->getId()]['discount'] = $discount; - $discountBreakdown[$rule->getId()]['rule'] = $rule; - $item->getExtensionAttributes()->setDiscounts($discountBreakdown); + $data = [ + 'amount' => $discountData->getAmount(), + 'base_amount' => $discountData->getBaseAmount(), + 'original_amount' => $discountData->getOriginalAmount(), + 'base_original_amount' => $discountData->getBaseOriginalAmount() + ]; + $itemDiscount = $this->discountDataInterfaceFactory->create(['data' => $data]); + $ruleLabel = $rule->getStoreLabel($address->getQuote()->getStore()) ?: __('Discount'); + $data = [ + 'discount' => $itemDiscount, + 'rule' => $ruleLabel, + 'rule_id' => $rule->getId(), + ]; + /** @var \Magento\SalesRule\Model\Data\RuleDiscount $itemDiscount */ + $ruleDiscount = $this->discountInterfaceFactory->create(['data' => $data]); + $this->discountAggregator[] = $ruleDiscount; + $item->getExtensionAttributes()->setDiscounts($this->discountAggregator); } return $this; } diff --git a/app/code/Magento/SalesRule/Plugin/CouponUsagesDecrement.php b/app/code/Magento/SalesRule/Plugin/CouponUsagesDecrement.php index 5f28632a54cea..87a7c2ed1bd38 100644 --- a/app/code/Magento/SalesRule/Plugin/CouponUsagesDecrement.php +++ b/app/code/Magento/SalesRule/Plugin/CouponUsagesDecrement.php @@ -7,7 +7,8 @@ namespace Magento\SalesRule\Plugin; -use Magento\Sales\Model\Order; +use Magento\Sales\Model\OrderRepository; +use Magento\Sales\Model\Service\OrderService; use Magento\SalesRule\Model\Coupon\UpdateCouponUsages; /** @@ -20,30 +21,39 @@ class CouponUsagesDecrement */ private $updateCouponUsages; + /** + * @var OrderRepository + */ + private $orderRepository; + /** * @param UpdateCouponUsages $updateCouponUsages + * @param OrderRepository $orderRepository */ public function __construct( - UpdateCouponUsages $updateCouponUsages + UpdateCouponUsages $updateCouponUsages, + OrderRepository $orderRepository ) { $this->updateCouponUsages = $updateCouponUsages; + $this->orderRepository = $orderRepository; } /** * Decrements number of coupon usages after cancelling order. * - * @param Order $subject - * @param callable $proceed - * @return Order + * @param OrderService $subject + * @param bool $result + * @param int $orderId + * @return bool + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function aroundCancel(Order $subject, callable $proceed): Order + public function afterCancel(OrderService $subject, bool $result, $orderId): bool { - $canCancel = $subject->canCancel(); - $returnValue = $proceed(); - if ($canCancel) { - $returnValue = $this->updateCouponUsages->execute($returnValue, false); + $order = $this->orderRepository->get($orderId); + if ($result) { + $this->updateCouponUsages->execute($order, false); } - return $returnValue; + return $result; } } diff --git a/app/code/Magento/SalesRule/Plugin/CouponUsagesIncrement.php b/app/code/Magento/SalesRule/Plugin/CouponUsagesIncrement.php index 473a368afb25a..14bbb5fce02a5 100644 --- a/app/code/Magento/SalesRule/Plugin/CouponUsagesIncrement.php +++ b/app/code/Magento/SalesRule/Plugin/CouponUsagesIncrement.php @@ -7,7 +7,8 @@ namespace Magento\SalesRule\Plugin; -use Magento\Sales\Model\Order; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\Service\OrderService; use Magento\SalesRule\Model\Coupon\UpdateCouponUsages; /** @@ -32,15 +33,15 @@ public function __construct( /** * Increments number of coupon usages after placing order. * - * @param Order $subject - * @param Order $result - * @return Order + * @param OrderService $subject + * @param OrderInterface $result + * @return OrderInterface * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterPlace(Order $subject, Order $result): Order + public function afterPlace(OrderService $subject, OrderInterface $result): OrderInterface { - $this->updateCouponUsages->execute($subject, true); + $this->updateCouponUsages->execute($result, true); - return $subject; + return $result; } } diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml deleted file mode 100644 index 1bd4d743acdc5..0000000000000 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml +++ /dev/null @@ -1,80 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="selectNotLoggedInCustomerGroup"> - <annotations> - <description>Selects 'NOT LOGGED IN' from the 'Customer Groups' list (Magento 2 B2B). PLEASE NOTE: The value is Hardcoded.</description> - </annotations> - - <!-- This actionGroup was created to be merged from B2B because B2B has a very different form control here --> - <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> - </actionGroup> - - <actionGroup name="selectRetailerCustomerGroup"> - <annotations> - <description>Selects 'Retailer' from the 'Customer Groups' list (Magento 2 B2B). PLEASE NOTE: The value is Hardcoded.</description> - </annotations> - - <!-- This actionGroup was created to be merged from B2B. Retailer Customer Group --> - <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="Retailer" stepKey="selectRetailerCustomerGroup"/> - </actionGroup> - - <!--Set Subtotal condition for Customer Segment--> - <actionGroup name="SetCartAttributeConditionForCartPriceRuleActionGroup"> - <annotations> - <description>Sets the provided Cart Attribute Condition (Attribute Name, Operator Type and Value) on the Admin Cart Price Rule creation/edit page.</description> - </annotations> - <arguments> - <argument name="attributeName" type="string"/> - <argument name="operatorType" defaultValue="is" type="string"/> - <argument name="value" type="string"/> - </arguments> - - <scrollTo selector="{{AdminCartPriceRulesFormSection.conditionsHeader}}" stepKey="scrollToActionTab"/> - <conditionalClick selector="{{AdminCartPriceRulesFormSection.conditionsHeader}}" dependentSelector="{{AdminCartPriceRulesFormSection.conditionsHeaderOpen}}" visible="false" stepKey="openActionTab"/> - <click selector="{{AdminCartPriceRulesFormSection.conditions}}" stepKey="applyRuleForConditions"/> - <waitForPageLoad stepKey="waitForDropDownOpened"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.childAttribute}}" userInput="{{attributeName}}" stepKey="selectAttribute"/> - <waitForPageLoad stepKey="waitForOperatorOpened"/> - <click selector="{{AdminCartPriceRulesFormSection.condition('is')}}" stepKey="clickToChooseOption"/> - <selectOption userInput="{{operatorType}}" selector="{{AdminCartPriceRulesFormSection.conditionsOperator}}" stepKey="setOperatorType"/> - <click selector="{{AdminCartPriceRulesFormSection.condition('...')}}" stepKey="clickToChooseOption1"/> - <fillField userInput="{{value}}" selector="{{AdminCartPriceRulesFormSection.conditionsValue}}" stepKey="fillActionValue"/> - <click selector="{{AdminMainActionsSection.saveAndContinue}}" stepKey="clickSaveButton"/> - <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> - </actionGroup> - - <actionGroup name="SetConditionForActionsInCartPriceRuleActionGroup"> - <annotations> - <description>Sets the provided Condition (Actions Aggregator/Value, Child Attribute and Action Value) for Actions on the Admin Cart Price Rule creation/edit page.</description> - </annotations> - <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" type="string"/> - </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/ActionGroup/AdminCartPriceRuleNotInGridActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleNotInGridActionGroup.xml new file mode 100644 index 0000000000000..23d7daf03e9c3 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleNotInGridActionGroup.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="AdminCartPriceRuleNotInGridActionGroup" extends="AdminFilterCartPriceRuleActionGroup"> + <annotations> + <description>EXTENDS: AdminFilterCartPriceRuleActionGroup. Removes 'goToEditRulePage'. Validates that the Empty Grid message is present and correct.</description> + </annotations> + + <remove keyForRemoval="goToEditRulePage"/> + <waitForPageLoad stepKey="waitForStoreToLoad"/> + <see selector="{{AdminCartPriceRulesSection.emptyText}}" userInput="We couldn't find any records." stepKey="seeAssertCartPriceRuleIsNotPresentedInGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionGroup.xml index c840162f0d162..57a2c17419532 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionGroup.xml @@ -28,115 +28,4 @@ <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> <see selector="{{AdminCartPriceRulesFormSection.successMessage}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> </actionGroup> - - <actionGroup name="AdminCreateCartPriceRuleAndStayOnEditActionGroup" extends="AdminCreateCartPriceRuleActionGroup"> - <annotations> - <description>EXTENDS: AdminCreateCartPriceRuleActionGroup. Clicks on Save and Continue.</description> - </annotations> - - <click selector="{{AdminCartPriceRulesFormSection.saveAndContinue}}" stepKey="clickSaveButton"/> - </actionGroup> - - <actionGroup name="AdminCreateCartPriceRuleWithCouponCode" extends="AdminCreateCartPriceRuleActionGroup"> - <annotations> - <description>EXTENDS: AdminCreateCartPriceRuleActionGroup. Removes 'selectActionType' and 'fillDiscountAmount'. Adds the provided Coupon Code to a Cart Price Rule.</description> - </annotations> - <arguments> - <argument name="couponCode" defaultValue="_defaultCoupon.code"/> - </arguments> - - <remove keyForRemoval="selectActionType"/> - <remove keyForRemoval="fillDiscountAmount"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.coupon}}" userInput="Specific Coupon" stepKey="selectCouponType" after="fillRuleName"/> - <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.couponCode}}" stepKey="waitForElementVisible" after="selectCouponType"/> - <fillField selector="{{AdminCartPriceRulesFormSection.couponCode}}" userInput="{{couponCode}}" stepKey="fillCouponCode" after="waitForElementVisible"/> - <fillField selector="{{AdminCartPriceRulesFormSection.userPerCoupon}}" userInput="99" stepKey="fillUserPerCoupon" after="fillCouponCode"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionTypeToFixed" after="clickToExpandActions"/> - <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="1" stepKey="fillDiscountAmount" after="selectActionTypeToFixed"/> - </actionGroup> - - <!--Delete Cart price Rule for Retailer customer--> - <actionGroup name="AdminDeleteCartPriceRuleForRetailerActionGroup"> - <annotations> - <description>Goes to the Admin Cart Price Rules grid page. Removes the 1st Cart Price Rule in the Grid.</description> - </annotations> - - <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="goToCartPriceRules"/> - <waitForPageLoad stepKey="waitForCartPriceRules"/> - <fillField selector="{{AdminCartPriceRulesSection.filterByNameInput}}" userInput="{{SimpleSalesRule.name}}" stepKey="filterByName"/> - <click selector="{{AdminCartPriceRulesSection.searchButton}}" stepKey="doFilter"/> - <click selector="{{AdminCartPriceRulesSection.rowByIndex('1')}}" stepKey="goToEditRulePage"/> - <click selector="{{AdminCartPriceRulesFormSection.delete}}" stepKey="clickDeleteButton"/> - <click selector="{{AdminCartPriceRulesFormSection.modalAcceptButton}}" stepKey="confirmDelete"/> - </actionGroup> - - <actionGroup name="AdminCreateCartPriceRuleWithConditions" extends="AdminCreateCartPriceRuleActionGroup"> - <annotations> - <description>EXTENDS: AdminCreateCartPriceRuleActionGroup. Removes 'fillDiscountAmount'. Adds the 2 provided Conditions (Name, Rule, Rule to Change and Category Name) to a Cart Price Rule.</description> - </annotations> - <arguments> - <argument name="condition1" type="string" defaultValue="Products subselection"/> - <argument name="condition2" type="string" defaultValue="Category"/> - <argument name="ruleToChange1" type="string" defaultValue="is"/> - <argument name="rule1" type="string" defaultValue="equals or greater than"/> - <argument name="ruleToChange2" type="string" defaultValue="..."/> - <argument name="rule2" type="string" defaultValue="2"/> - <argument name="categoryName" type="string" defaultValue="_defaultCategory.name"/> - </arguments> - - <remove keyForRemoval="fillDiscountAmount"/> - - <!--Go to Conditions section--> - <click selector="{{AdminCartPriceRulesFormSection.conditionsHeader}}" stepKey="openConditionsSection" after="selectActionType"/> - <click selector="{{AdminCartPriceRulesFormSection.addCondition('1')}}" stepKey="addFirstCondition" after="openConditionsSection"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.ruleCondition('1')}}" userInput="{{condition1}}" stepKey="selectRule" after="addFirstCondition"/> - <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange1)}}" stepKey="waitForFirstRuleElement" after="selectRule"/> - <click selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange1)}}" stepKey="clickToChangeRule" after="waitForFirstRuleElement"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.ruleParameterSelect('1--1')}}" userInput="{{rule1}}" stepKey="selectRule1" after="clickToChangeRule"/> - <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange2)}}" stepKey="waitForSecondRuleElement" after="selectRule1"/> - <click selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange2)}}" stepKey="clickToChangeRule1" after="waitForSecondRuleElement"/> - <fillField selector="{{AdminCartPriceRulesFormSection.ruleParameterInput('1--1')}}" userInput="{{rule2}}" stepKey="fillRule" after="clickToChangeRule1"/> - <click selector="{{AdminCartPriceRulesFormSection.addCondition('1--1')}}" stepKey="addSecondCondition" after="fillRule"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.ruleCondition('1--1')}}" userInput="{{condition2}}" stepKey="selectSecondCondition" after="addSecondCondition"/> - <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange2)}}" stepKey="waitForThirdRuleElement" after="selectSecondCondition"/> - <click selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange2)}}" stepKey="addThirdCondition" after="waitForThirdRuleElement"/> - <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.openChooser('1--1--1')}}" stepKey="waitForForthRuleElement" after="addThirdCondition"/> - <click selector="{{AdminCartPriceRulesFormSection.openChooser('1--1--1')}}" stepKey="openChooser" after="waitForForthRuleElement"/> - <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.categoryCheckbox(categoryName)}}" stepKey="waitForCategoryVisible" after="openChooser"/> - <checkOption selector="{{AdminCartPriceRulesFormSection.categoryCheckbox(categoryName)}}" stepKey="checkCategoryName" after="waitForCategoryVisible"/> - </actionGroup> - <actionGroup name="AdminCreateMultiWebsiteCartPriceRuleActionGroup" extends="AdminCreateCartPriceRuleActionGroup"> - <annotations> - <description>EXTENDS: AdminCreateCartPriceRuleActionGroup. Removes 'clickSaveButton' for the next data changing. Assign cart price rule to 2 websites instead of 1.</description> - </annotations> - <arguments> - <argument name="ruleName"/> - </arguments> - <remove keyForRemoval="clickSaveButton"/> - <remove keyForRemoval="seeSuccessMessage"/> - <remove keyForRemoval="selectWebsites"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" parameterArray="['FirstWebsite', 'SecondWebsite']" stepKey="selectWebsites" after="fillRuleName"/> - </actionGroup> - <actionGroup name="CreateCartPriceRuleSecondWebsiteActionGroup"> - <annotations> - <description>Goes to the Admin Cart Price Rule grid page. Clicks on Add New Rule. Fills the provided Rule (Name). Selects 'Second Website' from the 'Websites' menu.</description> - </annotations> - <arguments> - <argument name="ruleName"/> - </arguments> - - <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> - <waitForPageLoad stepKey="waitForPriceList"/> - <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> - <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{ruleName.name}}" stepKey="fillRuleName"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Second Website" stepKey="selectWebsites"/> - </actionGroup> - - <actionGroup name="AdminInactiveCartPriceRuleActionGroup" extends="AdminCreateCartPriceRuleActionGroup"> - <annotations> - <description>EXTENDS: AdminCreateCartPriceRuleActionGroup. Clicks on 'Active'.</description> - </annotations> - - <click selector="{{AdminCartPriceRulesFormSection.active}}" stepKey="clickActiveToDisable" after="fillRuleName"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionsSectionActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionsSectionActionGroup.xml deleted file mode 100644 index b30d2cfbb9204..0000000000000 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionsSectionActionGroup.xml +++ /dev/null @@ -1,52 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminCreateCartPriceRuleActionsSectionDiscountFieldsActionGroup"> - <annotations> - <description>Clicks on the 'Actions' section on the Admin Cart Price Rule creation/edit page. Fills in the provided Cart Price Rule details.</description> - </annotations> - <arguments> - <argument name="rule" type="entity"/> - </arguments> - - <conditionalClick selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" dependentSelector="{{AdminCartPriceRulesFormSection.actionsHeader}}" visible="true" stepKey="clickToExpandActions"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="{{rule.simple_action}}" stepKey="selectActionType"/> - <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="{{rule.discount_amount}}" stepKey="fillDiscountAmount"/> - <fillField selector="{{AdminCartPriceRulesFormSection.maximumQtyDiscount}}" userInput="{{rule.maximumQtyDiscount}}" stepKey="fillMaximumQtyDiscount"/> - <fillField selector="{{AdminCartPriceRulesFormSection.discountStep}}" userInput="{{rule.discount_step}}" stepKey="fillDiscountStep"/> - </actionGroup> - - <actionGroup name="AdminCreateCartPriceRuleActionsSectionShippingAmountActionGroup"> - <annotations> - <description>Clicks on the 'Apply to Shipping Amount' toggle.</description> - </annotations> - - <click selector="{{AdminCartPriceRulesFormSection.applyToShippingAmount}}" stepKey="clickApplyToShipping"/> - </actionGroup> - - <actionGroup name="AdminCreateCartPriceRuleActionsSectionSubsequentRulesActionGroup"> - <annotations> - <description>Clicks on the 'Discard subsequent rules' toggle.</description> - </annotations> - - <click selector="{{AdminCartPriceRulesFormSection.discardSubsequentRules}}" stepKey="clickApplyToShipping"/> - </actionGroup> - - <actionGroup name="AdminCreateCartPriceRuleActionsSectionFreeShippingActionGroup"> - <annotations> - <description>Selects the provided option in the 'Free Shipping' dropdown menu.</description> - </annotations> - <arguments> - <argument name="freeShippingOption" type="string"/> - </arguments> - - <selectOption selector="{{AdminCartPriceRulesFormSection.freeShipping}}" userInput="{{freeShippingOption}}" stepKey="selectForMatchingItemsOnly"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionsSectionDiscountFieldsActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionsSectionDiscountFieldsActionGroup.xml new file mode 100644 index 0000000000000..00edcfc6f19ed --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionsSectionDiscountFieldsActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateCartPriceRuleActionsSectionDiscountFieldsActionGroup"> + <annotations> + <description>Clicks on the 'Actions' section on the Admin Cart Price Rule creation/edit page. Fills in the provided Cart Price Rule details.</description> + </annotations> + <arguments> + <argument name="rule" type="entity"/> + </arguments> + + <conditionalClick selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" dependentSelector="{{AdminCartPriceRulesFormSection.actionsHeader}}" visible="true" stepKey="clickToExpandActions"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="{{rule.simple_action}}" stepKey="selectActionType"/> + <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="{{rule.discount_amount}}" stepKey="fillDiscountAmount"/> + <fillField selector="{{AdminCartPriceRulesFormSection.maximumQtyDiscount}}" userInput="{{rule.maximumQtyDiscount}}" stepKey="fillMaximumQtyDiscount"/> + <fillField selector="{{AdminCartPriceRulesFormSection.discountStep}}" userInput="{{rule.discount_step}}" stepKey="fillDiscountStep"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionsSectionFreeShippingActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionsSectionFreeShippingActionGroup.xml new file mode 100644 index 0000000000000..d90261bf44fa9 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionsSectionFreeShippingActionGroup.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="AdminCreateCartPriceRuleActionsSectionFreeShippingActionGroup"> + <annotations> + <description>Selects the provided option in the 'Free Shipping' dropdown menu.</description> + </annotations> + <arguments> + <argument name="freeShippingOption" type="string"/> + </arguments> + + <selectOption selector="{{AdminCartPriceRulesFormSection.freeShipping}}" userInput="{{freeShippingOption}}" stepKey="selectForMatchingItemsOnly"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionsSectionShippingAmountActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionsSectionShippingAmountActionGroup.xml new file mode 100644 index 0000000000000..3af8a82601a08 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionsSectionShippingAmountActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateCartPriceRuleActionsSectionShippingAmountActionGroup"> + <annotations> + <description>Clicks on the 'Apply to Shipping Amount' toggle.</description> + </annotations> + + <click selector="{{AdminCartPriceRulesFormSection.applyToShippingAmount}}" stepKey="clickApplyToShipping"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionsSectionSubsequentRulesActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionsSectionSubsequentRulesActionGroup.xml new file mode 100644 index 0000000000000..15dc5af206572 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionsSectionSubsequentRulesActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateCartPriceRuleActionsSectionSubsequentRulesActionGroup"> + <annotations> + <description>Clicks on the 'Discard subsequent rules' toggle.</description> + </annotations> + + <click selector="{{AdminCartPriceRulesFormSection.discardSubsequentRules}}" stepKey="clickApplyToShipping"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionsWithSubtotalActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionsWithSubtotalActionGroup.xml new file mode 100644 index 0000000000000..b6836ab63fb10 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionsWithSubtotalActionGroup.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateCartPriceRuleActionsWithSubtotalActionGroup" extends="AdminCreateCartPriceRuleActionGroup"> + <annotations> + <description>EXTENDS: AdminCreateCartPriceRuleActionGroup. Removes 'fillDiscountAmount'. Adds sub total conditions for free shipping to a Cart Price Rule.</description> + </annotations> + <arguments> + <argument name="ruleName"/> + </arguments> + <remove keyForRemoval="fillDiscountAmount"/> + <!-- Expand the conditions section --> + <grabTextFrom selector="{{AdminCartPriceRulesFormSection.ruleName}}" after="fillRuleName" stepKey="getSubtotalRule"/> + <click selector="{{AdminCartPriceRulesFormSection.conditionsHeader}}" stepKey="openConditionsSection" after="selectActionType"/> + <click selector="{{AdminCartPriceRulesFormSection.addCondition('1')}}" after="openConditionsSection" stepKey="addFirstCondition"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.conditionSelect}}" userInput="{{ruleName.condition1}}" after="addFirstCondition" stepKey="selectCondition1"/> + <waitForPageLoad after="selectCondition1" stepKey="waitForConditionLoad"/> + <click selector="{{AdminCartPriceRulesFormSection.condition(ruleName.ruleToChange1)}}" after="waitForConditionLoad" stepKey="clickToChooseOption"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.conditionsOperator}}" userInput="{{ruleName.rule1}}" after="clickToChooseOption" stepKey="setOperatorType"/> + <click selector="{{AdminCartPriceRulesFormSection.targetEllipsis}}" after="setOperatorType" stepKey="clickEllipsis"/> + <fillField selector="{{AdminCartPriceRulesFormSection.ruleFieldByIndex('1--1')}}" userInput="{{ruleName.subtotal}}" after="clickEllipsis" stepKey="fillSubtotalParameter"/> + <click selector="{{AdminCartPriceRulesFormSection.addNewCondition('1')}}" after="fillSubtotalParameter" stepKey="clickOnTheAddNewCondition"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.conditionSelectDropdown('1')}}" userInput="{{ruleName.condition2}}" after="clickOnTheAddNewCondition" stepKey="selectSecondCondition"/> + <waitForPageLoad after="selectSecondCondition" stepKey="waitForConditionLoad2"/> + <click selector="{{AdminCartPriceRulesFormSection.targetEllipsis}}" after="waitForConditionLoad2" stepKey="clickEllipsis2"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.ruleFieldByIndex('1--2')}}" userInput="{{ruleName.shippingMethod}}" after="clickEllipsis2" stepKey="selectShippingMethod"/> + <click selector="{{AdminCartPriceRulesFormSection.applyToShippingAmount}}" after="selectShippingMethod" stepKey="clickApplyToShipping"/> + <click selector="{{AdminCartPriceRulesFormSection.discardSubsequentRules}}" after="clickApplyToShipping" stepKey="clickDiscardSubsequentRules"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.freeShipping}}" userInput="{{ruleName.simple_free_shipping}}" after="clickDiscardSubsequentRules" stepKey="selectForMatchingItemsOnly"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleAndStayOnEditActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleAndStayOnEditActionGroup.xml new file mode 100644 index 0000000000000..b61988b630f03 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleAndStayOnEditActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateCartPriceRuleAndStayOnEditActionGroup" extends="AdminCreateCartPriceRuleActionGroup"> + <annotations> + <description>EXTENDS: AdminCreateCartPriceRuleActionGroup. Clicks on Save and Continue.</description> + </annotations> + + <click selector="{{AdminCartPriceRulesFormSection.saveAndContinue}}" stepKey="clickSaveButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleWithConditionIsCategoryActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleWithConditionIsCategoryActionGroup.xml new file mode 100644 index 0000000000000..7181ef586b2d8 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleWithConditionIsCategoryActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateCartPriceRuleWithConditionIsCategoryActionGroup" extends="AdminCreateCartPriceRuleActionGroup"> + <annotations> + <description>EXTENDS: AdminCreateCartPriceRuleActionGroup. Sets the provide Condition (Actions Aggregator/Value, Child Attribute and Action Value) for Actions on the Admin Cart Price Rule creation/edit page.</description> + </annotations> + <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" type="string" defaultValue="2"/> + </arguments> + <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" after="fillDiscountAmount" stepKey="clickOnActionTab"/> + <click selector="{{AdminCartPriceRulesFormSection.condition('ALL')}}" after="clickOnActionTab" stepKey="clickToChooseFirstRuleConditionValue"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.actionsAggregator}}" userInput="{{actionsAggregator}}" after="clickToChooseFirstRuleConditionValue" stepKey="changeFirstRuleConditionValue"/> + <click selector="{{AdminCartPriceRulesFormSection.condition('TRUE')}}" after="changeFirstRuleConditionValue" stepKey="clickToChooseSecondRuleConditionValue"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.actionsValue}}" userInput="{{actionsValue}}" after="clickToChooseSecondRuleConditionValue" stepKey="changeSecondRuleConditionValue"/> + <click selector="{{AdminCartPriceRulesFormSection.conditions}}" after="changeSecondRuleConditionValue" stepKey="clickConditionDropDownMenu"/> + <waitForPageLoad stepKey="waitForDropDownOpened"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.childAttribute}}" userInput="{{childAttribute}}" after="clickConditionDropDownMenu" stepKey="selectConditionAttributeIsCategory"/> + <waitForPageLoad after="selectConditionAttributeIsCategory" stepKey="waitForOperatorOpened"/> + <click selector="{{AdminCartPriceRulesFormSection.condition('...')}}" after="waitForOperatorOpened" stepKey="clickToChooserIcon"/> + <fillField selector="{{AdminCartPriceRulesFormSection.actionValue}}" userInput="{{actionValue}}" after="clickToChooserIcon" stepKey="choseNeededCategoryFromCategoryGrid"/> + <click selector="{{AdminCartPriceRulesFormSection.applyAction}}" after="choseNeededCategoryFromCategoryGrid" stepKey="applyAction"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleWithConditionsActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleWithConditionsActionGroup.xml new file mode 100644 index 0000000000000..db1bcde6677dc --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleWithConditionsActionGroup.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateCartPriceRuleWithConditionsActionGroup" extends="AdminCreateCartPriceRuleActionGroup"> + <annotations> + <description>EXTENDS: AdminCreateCartPriceRuleActionGroup. Removes 'fillDiscountAmount'. Adds the 2 provided Conditions (Name, Rule, Rule to Change and Category Name) to a Cart Price Rule.</description> + </annotations> + <arguments> + <argument name="condition1" type="string" defaultValue="Products subselection"/> + <argument name="condition2" type="string" defaultValue="Category"/> + <argument name="ruleToChange1" type="string" defaultValue="is"/> + <argument name="rule1" type="string" defaultValue="equals or greater than"/> + <argument name="ruleToChange2" type="string" defaultValue="..."/> + <argument name="rule2" type="string" defaultValue="2"/> + <argument name="categoryName" type="string" defaultValue="_defaultCategory.name"/> + </arguments> + + <remove keyForRemoval="fillDiscountAmount"/> + + <!--Go to Conditions section--> + <click selector="{{AdminCartPriceRulesFormSection.conditionsHeader}}" stepKey="openConditionsSection" after="selectActionType"/> + <click selector="{{AdminCartPriceRulesFormSection.addCondition('1')}}" stepKey="addFirstCondition" after="openConditionsSection"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.ruleCondition('1')}}" userInput="{{condition1}}" stepKey="selectRule" after="addFirstCondition"/> + <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange1)}}" stepKey="waitForFirstRuleElement" after="selectRule"/> + <click selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange1)}}" stepKey="clickToChangeRule" after="waitForFirstRuleElement"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.ruleParameterSelect('1--1')}}" userInput="{{rule1}}" stepKey="selectRule1" after="clickToChangeRule"/> + <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange2)}}" stepKey="waitForSecondRuleElement" after="selectRule1"/> + <click selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange2)}}" stepKey="clickToChangeRule1" after="waitForSecondRuleElement"/> + <fillField selector="{{AdminCartPriceRulesFormSection.ruleParameterInput('1--1')}}" userInput="{{rule2}}" stepKey="fillRule" after="clickToChangeRule1"/> + <click selector="{{AdminCartPriceRulesFormSection.addCondition('1--1')}}" stepKey="addSecondCondition" after="fillRule"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.ruleCondition('1--1')}}" userInput="{{condition2}}" stepKey="selectSecondCondition" after="addSecondCondition"/> + <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange2)}}" stepKey="waitForThirdRuleElement" after="selectSecondCondition"/> + <click selector="{{AdminCartPriceRulesFormSection.ruleParameter(ruleToChange2)}}" stepKey="addThirdCondition" after="waitForThirdRuleElement"/> + <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.openChooser('1--1--1')}}" stepKey="waitForForthRuleElement" after="addThirdCondition"/> + <click selector="{{AdminCartPriceRulesFormSection.openChooser('1--1--1')}}" stepKey="openChooser" after="waitForForthRuleElement"/> + <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.categoryCheckbox(categoryName)}}" stepKey="waitForCategoryVisible" after="openChooser"/> + <checkOption selector="{{AdminCartPriceRulesFormSection.categoryCheckbox(categoryName)}}" stepKey="checkCategoryName" after="waitForCategoryVisible"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleWithCouponCodeActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleWithCouponCodeActionGroup.xml new file mode 100644 index 0000000000000..51a45cf24784b --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleWithCouponCodeActionGroup.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="AdminCreateCartPriceRuleWithCouponCodeActionGroup" extends="AdminCreateCartPriceRuleActionGroup"> + <annotations> + <description>EXTENDS: AdminCreateCartPriceRuleActionGroup. Removes 'selectActionType' and 'fillDiscountAmount'. Adds the provided Coupon Code to a Cart Price Rule.</description> + </annotations> + <arguments> + <argument name="couponCode" defaultValue="_defaultCoupon.code"/> + </arguments> + + <remove keyForRemoval="selectActionType"/> + <remove keyForRemoval="fillDiscountAmount"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.coupon}}" userInput="Specific Coupon" stepKey="selectCouponType" after="fillRuleName"/> + <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.couponCode}}" stepKey="waitForElementVisible" after="selectCouponType"/> + <fillField selector="{{AdminCartPriceRulesFormSection.couponCode}}" userInput="{{couponCode}}" stepKey="fillCouponCode" after="waitForElementVisible"/> + <fillField selector="{{AdminCartPriceRulesFormSection.userPerCoupon}}" userInput="99" stepKey="fillUserPerCoupon" after="fillCouponCode"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionTypeToFixed" after="clickToExpandActions"/> + <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="1" stepKey="fillDiscountAmount" after="selectActionTypeToFixed"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateMultiWebsiteCartPriceRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateMultiWebsiteCartPriceRuleActionGroup.xml new file mode 100644 index 0000000000000..f04c76c190b81 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateMultiWebsiteCartPriceRuleActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateMultiWebsiteCartPriceRuleActionGroup" extends="AdminCreateCartPriceRuleActionGroup"> + <annotations> + <description>EXTENDS: AdminCreateCartPriceRuleActionGroup. Removes 'clickSaveButton' for the next data changing. Assign cart price rule to 2 websites instead of 1.</description> + </annotations> + <arguments> + <argument name="ruleName"/> + </arguments> + <remove keyForRemoval="clickSaveButton"/> + <remove keyForRemoval="seeSuccessMessage"/> + <remove keyForRemoval="selectWebsites"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" parameterArray="['FirstWebsite', 'SecondWebsite']" stepKey="selectWebsites" after="fillRuleName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminDeleteCartPriceRuleForRetailerActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminDeleteCartPriceRuleForRetailerActionGroup.xml new file mode 100644 index 0000000000000..3f5d511ebfbe7 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminDeleteCartPriceRuleForRetailerActionGroup.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="AdminDeleteCartPriceRuleForRetailerActionGroup"> + <annotations> + <description>Goes to the Admin Cart Price Rules grid page. Removes the 1st Cart Price Rule in the Grid.</description> + </annotations> + + <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="goToCartPriceRules"/> + <waitForPageLoad stepKey="waitForCartPriceRules"/> + <fillField selector="{{AdminCartPriceRulesSection.filterByNameInput}}" userInput="{{SimpleSalesRule.name}}" stepKey="filterByName"/> + <click selector="{{AdminCartPriceRulesSection.searchButton}}" stepKey="doFilter"/> + <click selector="{{AdminCartPriceRulesSection.rowByIndex('1')}}" stepKey="goToEditRulePage"/> + <click selector="{{AdminCartPriceRulesFormSection.delete}}" stepKey="clickDeleteButton"/> + <click selector="{{AdminCartPriceRulesFormSection.modalAcceptButton}}" stepKey="confirmDelete"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminFilterCartPriceRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminFilterCartPriceRuleActionGroup.xml index 0f307b2dfe591..f939f9b5a81be 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminFilterCartPriceRuleActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminFilterCartPriceRuleActionGroup.xml @@ -16,20 +16,10 @@ <arguments> <argument name="ruleName"/> </arguments> - + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> <fillField selector="{{AdminCartPriceRulesSection.filterByNameInput}}" userInput="{{ruleName}}" stepKey="filterByName"/> <click selector="{{AdminCartPriceRulesSection.searchButton}}" stepKey="doFilter"/> <click selector="{{AdminCartPriceRulesSection.rowByIndex('1')}}" stepKey="goToEditRulePage"/> </actionGroup> - - <actionGroup name="AdminCartPriceRuleNotInGridActionGroup" extends="AdminFilterCartPriceRuleActionGroup"> - <annotations> - <description>EXTENDS: AdminFilterCartPriceRuleActionGroup. Removes 'goToEditRulePage'. Validates that the Empty Grid message is present and correct.</description> - </annotations> - - <remove keyForRemoval="goToEditRulePage"/> - <waitForPageLoad stepKey="waitForStoreToLoad"/> - <see selector="{{AdminCartPriceRulesSection.emptyText}}" userInput="We couldn't find any records." stepKey="seeAssertCartPriceRuleIsNotPresentedInGrid"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminInactiveCartPriceRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminInactiveCartPriceRuleActionGroup.xml new file mode 100644 index 0000000000000..dad1090d3af27 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminInactiveCartPriceRuleActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminInactiveCartPriceRuleActionGroup" extends="AdminCreateCartPriceRuleActionGroup"> + <annotations> + <description>EXTENDS: AdminCreateCartPriceRuleActionGroup. Clicks on 'Active'.</description> + </annotations> + + <click selector="{{AdminCartPriceRulesFormSection.active}}" stepKey="clickActiveToDisable" after="fillRuleName"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/CreateCartPriceRuleSecondWebsiteActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/CreateCartPriceRuleSecondWebsiteActionGroup.xml new file mode 100644 index 0000000000000..d291f2fb4e63d --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/CreateCartPriceRuleSecondWebsiteActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CreateCartPriceRuleSecondWebsiteActionGroup"> + <annotations> + <description>Goes to the Admin Cart Price Rule grid page. Clicks on Add New Rule. Fills the provided Rule (Name). Selects 'Second Website' from the 'Websites' menu.</description> + </annotations> + <arguments> + <argument name="ruleName"/> + </arguments> + + <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> + <waitForPageLoad stepKey="waitForPriceList"/> + <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> + <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{ruleName.name}}" stepKey="fillRuleName"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Second Website" stepKey="selectWebsites"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/SelectNotLoggedInCustomerGroupActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/SelectNotLoggedInCustomerGroupActionGroup.xml new file mode 100644 index 0000000000000..4f5f50e128ae8 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/SelectNotLoggedInCustomerGroupActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SelectNotLoggedInCustomerGroupActionGroup"> + <annotations> + <description>Selects 'NOT LOGGED IN' from the 'Customer Groups' list (Magento 2 B2B). PLEASE NOTE: The value is Hardcoded.</description> + </annotations> + + <!-- This actionGroup was created to be merged from B2B because B2B has a very different form control here --> + <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/SelectRetailerCustomerGroupActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/SelectRetailerCustomerGroupActionGroup.xml new file mode 100644 index 0000000000000..8552189807470 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/SelectRetailerCustomerGroupActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SelectRetailerCustomerGroupActionGroup"> + <annotations> + <description>Selects 'Retailer' from the 'Customer Groups' list (Magento 2 B2B). PLEASE NOTE: The value is Hardcoded.</description> + </annotations> + + <!-- This actionGroup was created to be merged from B2B. Retailer Customer Group --> + <selectOption selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="Retailer" stepKey="selectRetailerCustomerGroup"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/SetCartAttributeConditionForCartPriceRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/SetCartAttributeConditionForCartPriceRuleActionGroup.xml new file mode 100644 index 0000000000000..466a02e607911 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/SetCartAttributeConditionForCartPriceRuleActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SetCartAttributeConditionForCartPriceRuleActionGroup"> + <annotations> + <description>Sets the provided Cart Attribute Condition (Attribute Name, Operator Type and Value) on the Admin Cart Price Rule creation/edit page.</description> + </annotations> + <arguments> + <argument name="attributeName" type="string"/> + <argument name="operatorType" defaultValue="is" type="string"/> + <argument name="value" type="string"/> + </arguments> + + <scrollTo selector="{{AdminCartPriceRulesFormSection.conditionsHeader}}" stepKey="scrollToActionTab"/> + <conditionalClick selector="{{AdminCartPriceRulesFormSection.conditionsHeader}}" dependentSelector="{{AdminCartPriceRulesFormSection.conditionsHeaderOpen}}" visible="false" stepKey="openActionTab"/> + <click selector="{{AdminCartPriceRulesFormSection.conditions}}" stepKey="applyRuleForConditions"/> + <waitForPageLoad stepKey="waitForDropDownOpened"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.childAttribute}}" userInput="{{attributeName}}" stepKey="selectAttribute"/> + <waitForPageLoad stepKey="waitForOperatorOpened"/> + <click selector="{{AdminCartPriceRulesFormSection.condition('is')}}" stepKey="clickToChooseOption"/> + <selectOption userInput="{{operatorType}}" selector="{{AdminCartPriceRulesFormSection.conditionsOperator}}" stepKey="setOperatorType"/> + <click selector="{{AdminCartPriceRulesFormSection.condition('...')}}" stepKey="clickToChooseOption1"/> + <fillField userInput="{{value}}" selector="{{AdminCartPriceRulesFormSection.conditionsValue}}" stepKey="fillActionValue"/> + <click selector="{{AdminMainActionsSection.saveAndContinue}}" stepKey="clickSaveButton"/> + <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/SetConditionForActionsInCartPriceRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/SetConditionForActionsInCartPriceRuleActionGroup.xml new file mode 100644 index 0000000000000..118e9c9961ddb --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/SetConditionForActionsInCartPriceRuleActionGroup.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SetConditionForActionsInCartPriceRuleActionGroup"> + <annotations> + <description>Sets the provided Condition (Actions Aggregator/Value, Child Attribute and Action Value) for Actions on the Admin Cart Price Rule creation/edit page.</description> + </annotations> + <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" type="string"/> + </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/ActionGroup/StorefrontApplyCouponActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontApplyCouponActionGroup.xml new file mode 100644 index 0000000000000..72f2fb2a2a386 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontApplyCouponActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- Apply Sales Rule Coupon to the cart --> + <actionGroup name="StorefrontApplyCouponActionGroup"> + <annotations> + <description>Applies the provided Coupon Code to the Storefront Shopping Cart.</description> + </annotations> + <arguments> + <argument name="coupon"/> + </arguments> + + <waitForElement selector="{{StorefrontSalesRuleCartCouponSection.couponHeader}}" time="30" stepKey="waitForCouponHeader"/> + <conditionalClick selector="{{StorefrontSalesRuleCartCouponSection.couponHeader}}" dependentSelector="{{StorefrontSalesRuleCartCouponSection.discountBlockActive}}" visible="false" stepKey="clickCouponHeader"/> + <waitForElementVisible selector="{{StorefrontSalesRuleCartCouponSection.couponField}}" stepKey="waitForCouponField"/> + <fillField userInput="{{coupon.code}}" selector="{{StorefrontSalesRuleCartCouponSection.couponField}}" stepKey="fillCouponField"/> + <click selector="{{StorefrontSalesRuleCartCouponSection.applyButton}}" stepKey="clickApplyButton"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontCancelCouponActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontCancelCouponActionGroup.xml new file mode 100644 index 0000000000000..a97d9e8cd6300 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontCancelCouponActionGroup.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="StorefrontCancelCouponActionGroup"> + <annotations> + <description>Cancels the Coupon that is applied to the Storefront Shopping Cart.</description> + </annotations> + + <waitForElement selector="{{StorefrontSalesRuleCartCouponSection.couponHeader}}" time="30" stepKey="waitForCouponHeader"/> + <conditionalClick selector="{{StorefrontSalesRuleCartCouponSection.couponHeader}}" dependentSelector="{{StorefrontSalesRuleCartCouponSection.discountBlockActive}}" visible="false" stepKey="clickCouponHeader"/> + <waitForElementVisible selector="{{StorefrontSalesRuleCartCouponSection.couponField}}" stepKey="waitForCouponField"/> + <click selector="{{StorefrontSalesRuleCartCouponSection.cancelButton}}" stepKey="clickCancelButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontCheckCouponAppliedActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontCheckCouponAppliedActionGroup.xml new file mode 100644 index 0000000000000..84c2dd2121ae3 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontCheckCouponAppliedActionGroup.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="StorefrontCheckCouponAppliedActionGroup"> + <annotations> + <description>Validates that the provided Rule and Discount Amount is present and correct on the Storefront Checkout page.</description> + </annotations> + <arguments> + <argument name="rule"/> + <argument name="discount" type="string"/> + </arguments> + + <waitForElementVisible selector="{{CheckoutCartSummarySection.discountTotal}}" stepKey="waitForDiscountTotal"/> + <see userInput="{{rule.store_labels[1][store_label]}}" selector="{{CheckoutCartSummarySection.discountLabel}}" stepKey="assertDiscountLabel"/> + <see userInput="-${{discount}}" selector="{{CheckoutCartSummarySection.discountTotal}}" stepKey="assertDiscountTotal"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontSalesRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontSalesRuleActionGroup.xml deleted file mode 100644 index f978c1e2a9262..0000000000000 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontSalesRuleActionGroup.xml +++ /dev/null @@ -1,76 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!-- Apply Sales Rule Coupon to the cart --> - <actionGroup name="StorefrontApplyCouponActionGroup"> - <annotations> - <description>Applies the provided Coupon Code to the Storefront Shopping Cart.</description> - </annotations> - <arguments> - <argument name="coupon"/> - </arguments> - - <waitForElement selector="{{StorefrontSalesRuleCartCouponSection.couponHeader}}" time="30" stepKey="waitForCouponHeader"/> - <conditionalClick selector="{{StorefrontSalesRuleCartCouponSection.couponHeader}}" dependentSelector="{{StorefrontSalesRuleCartCouponSection.discountBlockActive}}" visible="false" stepKey="clickCouponHeader"/> - <waitForElementVisible selector="{{StorefrontSalesRuleCartCouponSection.couponField}}" stepKey="waitForCouponField"/> - <fillField userInput="{{coupon.code}}" selector="{{StorefrontSalesRuleCartCouponSection.couponField}}" stepKey="fillCouponField"/> - <click selector="{{StorefrontSalesRuleCartCouponSection.applyButton}}" stepKey="clickApplyButton"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - </actionGroup> - - <!-- Cancel Sales Rule Coupon applied to the cart --> - <actionGroup name="StorefrontCancelCouponActionGroup"> - <annotations> - <description>Cancels the Coupon that is applied to the Storefront Shopping Cart.</description> - </annotations> - - <waitForElement selector="{{StorefrontSalesRuleCartCouponSection.couponHeader}}" time="30" stepKey="waitForCouponHeader"/> - <conditionalClick selector="{{StorefrontSalesRuleCartCouponSection.couponHeader}}" dependentSelector="{{StorefrontSalesRuleCartCouponSection.discountBlockActive}}" visible="false" stepKey="clickCouponHeader"/> - <waitForElementVisible selector="{{StorefrontSalesRuleCartCouponSection.couponField}}" stepKey="waitForCouponField"/> - <click selector="{{StorefrontSalesRuleCartCouponSection.cancelButton}}" stepKey="clickCancelButton"/> - </actionGroup> - - <!-- Check applied discount in cart summary --> - <actionGroup name="StorefrontCheckCouponAppliedActionGroup"> - <annotations> - <description>Validates that the provided Rule and Discount Amount is present and correct on the Storefront Checkout page.</description> - </annotations> - <arguments> - <argument name="rule"/> - <argument name="discount" type="string"/> - </arguments> - - <waitForElementVisible selector="{{CheckoutCartSummarySection.discountTotal}}" stepKey="waitForDiscountTotal"/> - <see userInput="{{rule.store_labels[1][store_label]}}" selector="{{CheckoutCartSummarySection.discountLabel}}" stepKey="assertDiscountLabel"/> - <see userInput="-${{discount}}" selector="{{CheckoutCartSummarySection.discountTotal}}" stepKey="assertDiscountTotal"/> - </actionGroup> - - <actionGroup name="VerifyDiscountAmount"> - <annotations> - <description>Goes to the provided Storefront Product URL. Fills in provided Quantity. Clicks Add to Cart. Goes to Checkout. Validates that the provided Discount Amount is present and correct.</description> - </annotations> - <arguments> - <argument name="productUrl" type="string"/> - <argument name="quantity" type="string"/> - <argument name="expectedDiscount" type="string"/> - </arguments> - - <amOnPage url="{{productUrl}}" stepKey="goToProductPage"/> - <waitForPageLoad stepKey="waitForProductPageLoad"/> - <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="{{quantity}}" stepKey="fillQuantity"/> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> - <waitForPageLoad stepKey="waitForAddToCart"/> - <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> - <waitForPageLoad stepKey="waitForCartPage"/> - <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForDiscountElement"/> - <see selector="{{CheckoutCartSummarySection.discountAmount}}" userInput="{{expectedDiscount}}" stepKey="seeDiscountTotal"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/VerifyDiscountAmountActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/VerifyDiscountAmountActionGroup.xml new file mode 100644 index 0000000000000..c4e8f9f6ce32e --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/VerifyDiscountAmountActionGroup.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="VerifyDiscountAmountActionGroup"> + <annotations> + <description>Goes to the provided Storefront Product URL. Fills in provided Quantity. Clicks Add to Cart. Goes to Checkout. Validates that the provided Discount Amount is present and correct.</description> + </annotations> + <arguments> + <argument name="productUrl" type="string"/> + <argument name="quantity" type="string"/> + <argument name="expectedDiscount" type="string"/> + </arguments> + + <amOnPage url="{{productUrl}}" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="{{quantity}}" stepKey="fillQuantity"/> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> + <waitForPageLoad stepKey="waitForAddToCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> + <waitForPageLoad stepKey="waitForCartPage"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForDiscountElement"/> + <see selector="{{CheckoutCartSummarySection.discountAmount}}" userInput="{{expectedDiscount}}" stepKey="seeDiscountTotal"/> + </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 c1ec728a6cfb9..4a39e1237841d 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml @@ -89,6 +89,13 @@ <data key="apply">Percent of product price discount</data> <data key="discountAmount">50</data> </entity> + <entity name="SalesRuleWithFullDiscount" type="SalesRule"> + <data key="name" unique="suffix">TestSalesRule</data> + <data key="websites">Main Website</data> + <data key="customerGroups">'NOT LOGGED IN', 'General', 'Wholesale', 'Retailer'</data> + <data key="apply">Percent of product price discount</data> + <data key="discountAmount">100</data> + </entity> <entity name="CatPriceRule" type="SalesRule"> <data key="name" unique="suffix">CartPriceRule</data> <data key="websites">Main Website</data> @@ -457,4 +464,13 @@ <requiredEntity type="SalesRuleLabel">SalesRuleLabelDefault</requiredEntity> <requiredEntity type="SalesRuleLabel">SalesRuleLabelStore1</requiredEntity> </entity> + + <entity name="TestSalesRuleWithInvalidData" type="SalesRule"> + <data key="userPerCustomer">one</data> + <data key="userPerCoupon">one</data> + <data key="priority">one</data> + <data key="discountStep">one</data> + <data key="discountAmount">one</data> + <data key="maximumQtyDiscount">one</data> + </entity> </entities> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml index 3849d153be465..b164cdde33248 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml @@ -98,5 +98,7 @@ <element name="couponQty" type="input" selector="#coupons_qty"/> <element name="generateCouponsButton" type="button" selector="#coupons_generate_button" timeout="30"/> <element name="generatedCouponByIndex" type="text" selector="#couponCodesGrid_table > tbody > tr:nth-child({{var}}) > td.col-code" parameterized="true"/> + <element name="couponGridUsedHeader" type="text" selector="#couponCodesGrid thead th[data-sort='used']"/> + <element name="fieldError" type="text" selector="//input[@name='{{fieldName}}']/following-sibling::label[@class='admin__field-error']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml index ab085dc5ae137..916416dcd9141 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml @@ -36,15 +36,15 @@ <deleteData createDataKey="defaultCategory" stepKey="deleteCategory"/> <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="deleteProductBySku" stepKey="deleteBundleProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteBundleProduct"> <argument name="sku" value="{{BundleProduct.sku}}"/> </actionGroup> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilters"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilters"/> <actionGroup ref="DeleteCartPriceRuleByName" stepKey="deleteCartPriceRule"> <argument name="ruleName" value="{{PriceRuleWithCondition.name}}"/> </actionGroup> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilters1"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilters1"/> <actionGroup ref="logout" stepKey="logout"/> </after> @@ -52,10 +52,10 @@ <!--Start creating a bundle product--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> <waitForPageLoad stepKey="waitForProductList"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProduct"> <argument name="product" value="BundleProduct"/> </actionGroup> - <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillNameAndSku"> + <actionGroup ref="FillProductNameAndSkuInProductFormActionGroup" stepKey="fillNameAndSku"> <argument name="product" value="BundleProduct"/> </actionGroup> <pressKey selector="{{AdminProductFormSection.productSku}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::ENTER]" stepKey="enter"/> @@ -73,7 +73,7 @@ <click selector="{{AdminProductFormBundleSection.categoriesLabel}}" stepKey="clickOnCategoriesLabelToCloseOptions"/> <!-- Add option, a "Radio Buttons" type option, with one product and set fixed price 200--> - <actionGroup ref="addBundleOptionWithOneProduct" stepKey="addBundleOptionWithOneProduct"> + <actionGroup ref="AddBundleOptionWithOneProductActionGroup" stepKey="addBundleOptionWithOneProduct"> <argument name="x" value="0"/> <argument name="n" value="1"/> <argument name="prodOneSku" value="$$simpleProduct.sku$$"/> @@ -83,10 +83,10 @@ </actionGroup> <selectOption selector="{{AdminProductFormBundleSection.bundlePriceType}}" userInput="Fixed" stepKey="selectPriceType"/> <fillField selector="{{AdminProductFormBundleSection.bundlePriceValue}}" userInput="200" stepKey="fillPriceValue"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!--Create cart price rule--> - <actionGroup ref="AdminCreateCartPriceRuleWithConditions" stepKey="createRule"> + <actionGroup ref="AdminCreateCartPriceRuleWithConditionsActionGroup" stepKey="createRule"> <argument name="ruleName" value="PriceRuleWithCondition"/> <argument name="condition1" value="Products subselection"/> <argument name="condition2" value="Category"/> @@ -99,6 +99,7 @@ <!--Go to Storefront and add product to cart and checkout from cart--> <amOnPage url="{{StorefrontProductPage.url($$simpleProduct.custom_attributes[url_key]$$)}}" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPage" /> <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="2" stepKey="setQuantity"/> <actionGroup ref="StorefrontAddToCartCustomOptionsProductPageActionGroup" stepKey="AddProductToCard"> <argument name="productName" value="$$simpleProduct.name$$"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml index 02078ff15ecc2..addb65a68fad3 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateBuyXGetYFreeTest.xml @@ -50,13 +50,13 @@ <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> <!-- Create a product to check the storefront --> - <actionGroup ref="FillAdminSimpleProductForm" stepKey="fillProductFieldsInAdmin"> + <actionGroup ref="FillAdminSimpleProductFormActionGroup" stepKey="fillProductFieldsInAdmin"> <argument name="category" value="$$createPreReqCategory$$"/> <argument name="simpleProduct" value="_defaultProduct"/> </actionGroup> <!-- Spot check the storefront --> - <actionGroup ref="VerifyDiscountAmount" stepKey="verifyStorefront"> + <actionGroup ref="VerifyDiscountAmountActionGroup" stepKey="verifyStorefront"> <argument name="productUrl" value="{{_defaultProduct.urlKey}}.html"/> <argument name="quantity" value="2"/> <argument name="expectedDiscount" value="-$123.00"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionAndFreeShippingIsAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionAndFreeShippingIsAppliedTest.xml index b51027d51fd53..0d23e0e035432 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionAndFreeShippingIsAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionAndFreeShippingIsAppliedTest.xml @@ -74,7 +74,7 @@ <seeInField selector="{{AdminCartPriceRulesFormSection.defaultStoreView}}" userInput="{{CartPriceRuleConditionAndFreeShippingApplied.defaultStoreView}}" stepKey="seeDefaultStoreView"/> <!--Go to storefront page and verify created product--> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPageAndVerifyProduct"> <argument name="product" value="$$initialSimpleProduct$$"/> </actionGroup> <!--Click on Add To Cart button--> @@ -91,7 +91,7 @@ </actionGroup> <!--Click on view and edit cart link--> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <waitForPageLoad stepKey="waitForViewAndEditCartToOpen"/> <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" visible="true" stepKey="clickEstimateShippingAndTaxToOpen"/> <waitForPageLoad stepKey="waitForEstimateShippingAndTaxToOpen"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionIsNotAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionIsNotAppliedTest.xml index 8969e9d9d4ceb..b41e139536223 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionIsNotAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionIsNotAppliedTest.xml @@ -85,7 +85,7 @@ <seeInField selector="{{AdminCartPriceRulesFormSection.defaultStoreView}}" userInput="{{CartPriceRuleConditionNotApplied.defaultStoreView}}" stepKey="seeDefaultStoreView"/> <!--Go to storefront page and verify created product--> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPageAndVerifyProduct"> <argument name="product" value="$$initialSimpleProduct$$"/> </actionGroup> <fillField selector="{{StorefrontProductInfoMainSection.qty}}" userInput="2" stepKey="fillProductQuantity"/> @@ -103,7 +103,7 @@ </actionGroup> <!--Click on view and edit cart link--> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <waitForPageLoad stepKey="waitForViewAndEditCartToOpen"/> <!--Verify AssertCartPriceRuleConditionIsNotApplied(Shopping cart subtotal equals to grand total - price rule has not been applied)--> <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssert"> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml index f5b285f2f0286..f0075a92a2b82 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml @@ -53,7 +53,7 @@ <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{_defaultCoupon.code}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> - <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectCustomerGroup"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectCustomerGroup"/> <selectOption selector="{{AdminCartPriceRulesFormSection.coupon}}" userInput="Specific Coupon" stepKey="selectCouponType"/> <fillField selector="{{AdminCartPriceRulesFormSection.couponCode}}" userInput="{{_defaultCoupon.code}}" stepKey="fillCouponCode"/> <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml index 712475186d5bf..6242c1f3d1baf 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml @@ -67,7 +67,7 @@ <seeInField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="5" stepKey="seeDiscountAmount"/> <!-- Create a product to check the storefront --> - <actionGroup ref="FillAdminSimpleProductForm" stepKey="fillProductFieldsInAdmin"> + <actionGroup ref="FillAdminSimpleProductFormActionGroup" stepKey="fillProductFieldsInAdmin"> <argument name="category" value="$$createPreReqCategory$$"/> <argument name="simpleProduct" value="_defaultProduct"/> </actionGroup> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml index 9d807de409a0c..78943a0648b51 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml @@ -62,11 +62,14 @@ <waitForPageLoad stepKey="waitFormToReload1"/> <click selector="{{AdminCartPriceRulesFormSection.manageCouponCodesHeader}}" stepKey="expandCouponSection2"/> + <!-- Assert coupon codes grid header is correct --> + <see selector="{{AdminCartPriceRulesFormSection.couponGridUsedHeader}}" userInput="Used" stepKey="seeCorrectUsedHeader"/> + <!-- Grab a coupon code and hold on to it for later --> <grabTextFrom selector="{{AdminCartPriceRulesFormSection.generatedCouponByIndex('1')}}" stepKey="grabCouponCode"/> <!-- Create a product to check the storefront --> - <actionGroup ref="FillAdminSimpleProductForm" stepKey="fillProductFieldsInAdmin"> + <actionGroup ref="FillAdminSimpleProductFormActionGroup" stepKey="fillProductFieldsInAdmin"> <argument name="category" value="$$createPreReqCategory$$"/> <argument name="simpleProduct" value="_defaultProduct"/> </actionGroup> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForMatchingSubtotalAndVerifyRuleConditionIsAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForMatchingSubtotalAndVerifyRuleConditionIsAppliedTest.xml index ab62e51414e85..385c9e35da393 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForMatchingSubtotalAndVerifyRuleConditionIsAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForMatchingSubtotalAndVerifyRuleConditionIsAppliedTest.xml @@ -84,7 +84,7 @@ <seeInField selector="{{AdminCartPriceRulesFormSection.defaultStoreView}}" userInput="{{CartPriceRuleConditionAppliedForSubtotal.defaultStoreView}}" stepKey="seeDefaultStoreView"/> <!--Go to storefront page and verify created product--> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPageAndVerifyProduct"> <argument name="product" value="$$initialSimpleProduct$$"/> </actionGroup> <!--Click on Add To Cart button--> @@ -101,7 +101,7 @@ </actionGroup> <!--Click on view and edit cart link--> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <waitForPageLoad stepKey="waitForViewAndEditCartToOpen"/> <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" visible="true" stepKey="clickEstimateShippingAndTaxToOpen"/> <waitForPageLoad stepKey="waitForEstimateShippingAndTaxToOpen"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingCategoryAndVerifyRuleConditionIsAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingCategoryAndVerifyRuleConditionIsAppliedTest.xml index 00f104885e0f0..a56e332ce1a85 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingCategoryAndVerifyRuleConditionIsAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingCategoryAndVerifyRuleConditionIsAppliedTest.xml @@ -21,14 +21,14 @@ <before> <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="_defaultCategory" stepKey="createCategory"/> - <actionGroup ref="FillAdminSimpleProductForm" stepKey="fillProductFieldsInAdmin"> + <actionGroup ref="FillAdminSimpleProductFormActionGroup" stepKey="fillProductFieldsInAdmin"> <argument name="category" value="$$createCategory$$"/> <argument name="simpleProduct" value="_defaultProduct"/> </actionGroup> </before> <after> <deleteData createDataKey="createCategory" stepKey="deletePreReqCategory"/> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteSimpleProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteSimpleProduct"> <argument name="product" value="_defaultProduct"/> </actionGroup> <actionGroup ref="DeleteCartPriceRuleByName" stepKey="deleteCreatedCartPriceRule"> @@ -94,7 +94,7 @@ <!--Go to storefront page and verify created product--> <amOnPage url="{{StorefrontProductPage.url(_defaultProduct.urlKey)}}" stepKey="onCategoryPage"/> <waitForPageLoad stepKey="waitForCategoryPageLoad"/> - <actionGroup ref="addToCartFromStorefrontProductPage" stepKey="openProductPageAndVerifyProduct"> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="openProductPageAndVerifyProduct"> <argument name="productName" value="_defaultProduct.name"/> </actionGroup> @@ -110,7 +110,7 @@ </actionGroup> <!--Click on view and edit cart link--> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <waitForPageLoad stepKey="waitForViewAndEditToOpen"/> <!--Verify AssertCartPriceRuleConditionIsApplied if condition category id is matching--> <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssert"> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingTotalWeightAndVerifyRuleConditionIsAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingTotalWeightAndVerifyRuleConditionIsAppliedTest.xml index 43b92ee938978..0541ee2dd4eba 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingTotalWeightAndVerifyRuleConditionIsAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingTotalWeightAndVerifyRuleConditionIsAppliedTest.xml @@ -81,7 +81,7 @@ <seeInField selector="{{AdminCartPriceRulesFormSection.defaultStoreView}}" userInput="{{CartPriceRuleConditionAppliedForWeight.defaultStoreView}}" stepKey="seeDefaultStoreView"/> <!--Go to storefront page and verify created product--> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPageAndVerifyProduct"> <argument name="product" value="$$initialSimpleProduct$$"/> </actionGroup> <!--Click on Add To Cart button--> @@ -98,7 +98,7 @@ </actionGroup> <!--Click on view and edit cart link--> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <waitForPageLoad stepKey="waitForViewAndEditCartToOpen"/> <!--Verify AssertCartPriceRuleConditionIsApplied if condition Total Weight equals 200 is true--> <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssert"> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml index 1681d910ccdb0..c65a5a865e779 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountDiscountTest.xml @@ -49,13 +49,13 @@ <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> <!-- Create a product to check the storefront --> - <actionGroup ref="FillAdminSimpleProductForm" stepKey="fillProductFieldsInAdmin"> + <actionGroup ref="FillAdminSimpleProductFormActionGroup" stepKey="fillProductFieldsInAdmin"> <argument name="category" value="$$createPreReqCategory$$"/> <argument name="simpleProduct" value="_defaultProduct"/> </actionGroup> <!-- Spot check the storefront --> - <actionGroup ref="VerifyDiscountAmount" stepKey="verifyStorefront"> + <actionGroup ref="VerifyDiscountAmountActionGroup" stepKey="verifyStorefront"> <argument name="productUrl" value="{{_defaultProduct.urlKey}}.html"/> <argument name="quantity" value="2"/> <argument name="expectedDiscount" value="-$20.00"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml index 69918bda8c426..69ba7aef393b5 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateFixedAmountWholeCartDiscountTest.xml @@ -49,13 +49,13 @@ <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> <!-- Create a product to check the storefront --> - <actionGroup ref="FillAdminSimpleProductForm" stepKey="fillProductFieldsInAdmin"> + <actionGroup ref="FillAdminSimpleProductFormActionGroup" stepKey="fillProductFieldsInAdmin"> <argument name="category" value="$$createPreReqCategory$$"/> <argument name="simpleProduct" value="_defaultProduct"/> </actionGroup> <!-- Spot check the storefront --> - <actionGroup ref="VerifyDiscountAmount" stepKey="verifyStorefront"> + <actionGroup ref="VerifyDiscountAmountActionGroup" stepKey="verifyStorefront"> <argument name="productUrl" value="{{_defaultProduct.urlKey}}.html"/> <argument name="quantity" value="2"/> <argument name="expectedDiscount" value="-$19.99"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateInvalidRuleTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateInvalidRuleTest.xml new file mode 100644 index 0000000000000..620112e323ff5 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateInvalidRuleTest.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="AdminCreateInvalidRuleTest"> + <annotations> + <features value="SalesRule"/> + <stories value="Create cart price rule with invalid data"/> + <title value="Admin can not create rule with invalid data"/> + <description value="Admin can not create rule with invalid data"/> + <severity value="MAJOR"/> + <group value="SalesRule"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + + <after> + <actionGroup ref="logout" stepKey="adminLogout"/> + </after> + + <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> + + <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickToExpandActions"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Buy X get Y free (discount amount is Y)" stepKey="selectActionType"/> + <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="{{TestSalesRuleWithInvalidData.discountAmount}}" stepKey="fillDiscountAmount"/> + <fillField selector="{{AdminCartPriceRulesFormSection.maximumQtyDiscount}}" userInput="{{TestSalesRuleWithInvalidData.maximumQtyDiscount}}" stepKey="fillDiscountQty"/> + <fillField selector="{{AdminCartPriceRulesFormSection.discountStep}}" userInput="{{TestSalesRuleWithInvalidData.discountStep}}" stepKey="fillDiscountStep"/> + + <fillField selector="{{AdminCartPriceRulesFormSection.userPerCustomer}}" userInput="{{TestSalesRuleWithInvalidData.userPerCustomer}}" stepKey="fillUsePerCustomer"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.coupon}}" userInput="Specific Coupon" stepKey="selectCouponType"/> + <fillField selector="{{AdminCartPriceRulesFormSection.userPerCoupon}}" userInput="{{TestSalesRuleWithInvalidData.userPerCoupon}}" stepKey="fillUsePerCoupon"/> + <fillField selector="{{AdminCartPriceRulesFormSection.priority}}" userInput="{{TestSalesRuleWithInvalidData.priority}}" stepKey="fillPriority"/> + + <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> + + <see selector="{{AdminNewCatalogPriceRule.fieldError('uses_per_coupon')}}" userInput="Please enter a valid number in this field." stepKey="seePerCouponError"/> + <see selector="{{AdminNewCatalogPriceRule.fieldError('uses_per_customer')}}" userInput="Please enter a valid number in this field." stepKey="seePerCustomerError"/> + <see selector="{{AdminNewCatalogPriceRule.fieldError('sort_order')}}" userInput="Please enter a valid number in this field." stepKey="seePriorityError"/> + <see selector="{{AdminNewCatalogPriceRule.fieldError('discount_amount')}}" userInput="Please enter a valid number in this field." stepKey="seeDiscountAmountError"/> + <see selector="{{AdminNewCatalogPriceRule.fieldError('discount_qty')}}" userInput="Please enter a valid number in this field." stepKey="seeMaximumQtyError"/> + <see selector="{{AdminNewCatalogPriceRule.fieldError('discount_step')}}" userInput="Please enter a valid number in this field." stepKey="seeDiscountStepError"/> + </test> +</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml index 898e5a07304b6..fd3ebd2ad11d7 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreatePercentOfProductPriceTest.xml @@ -49,13 +49,13 @@ <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> <!-- Create a product to check the storefront --> - <actionGroup ref="FillAdminSimpleProductForm" stepKey="fillProductFieldsInAdmin"> + <actionGroup ref="FillAdminSimpleProductFormActionGroup" stepKey="fillProductFieldsInAdmin"> <argument name="category" value="$$createPreReqCategory$$"/> <argument name="simpleProduct" value="_defaultProduct"/> </actionGroup> <!-- Spot check the storefront --> - <actionGroup ref="VerifyDiscountAmount" stepKey="verifyStorefront"> + <actionGroup ref="VerifyDiscountAmountActionGroup" stepKey="verifyStorefront"> <argument name="productUrl" value="{{_defaultProduct.urlKey}}.html"/> <argument name="quantity" value="1"/> <argument name="expectedDiscount" value="-$61.50"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithComplexConditionsAndVerifyDeleteMessageTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithComplexConditionsAndVerifyDeleteMessageTest.xml index d106c086a6065..9a71210aac1c6 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithComplexConditionsAndVerifyDeleteMessageTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithComplexConditionsAndVerifyDeleteMessageTest.xml @@ -16,9 +16,6 @@ <severity value="CRITICAL"/> <group value="salesRule"/> <group value="mtf_migrated"/> - <skip> - <issueId value="MC-17175"/> - </skip> </annotations> <before> @@ -60,6 +57,8 @@ <actionGroup ref="AdminCreateCartPriceRuleLabelsSectionActionGroup" stepKey="createActiveCartPriceRuleLabelsSection"> <argument name="rule" value="ActiveSalesRuleWithComplexConditions"/> </actionGroup> + <generateDate date="+1 minute" format="m/d/Y" stepKey="generateStartDate"/> + <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$generateStartDate}" stepKey="fillStartDate"/> <actionGroup ref="AssertCartPriceRuleSuccessSaveMessageActionGroup" stepKey="assertVerifyCartPriceRuleSuccessSaveMessage"/> </before> <after> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithPercentPriceAndVerifyDeleteMessageTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithPercentPriceAndVerifyDeleteMessageTest.xml index 41e4221a2c37f..34b8363e8b5ce 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithPercentPriceAndVerifyDeleteMessageTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithPercentPriceAndVerifyDeleteMessageTest.xml @@ -22,7 +22,7 @@ <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!--Create active cart price rule--> - <actionGroup ref="AdminCreateCartPriceRuleWithCouponCode" stepKey="createActiveCartPriceRule"> + <actionGroup ref="AdminCreateCartPriceRuleWithCouponCodeActionGroup" stepKey="createActiveCartPriceRule"> <argument name="ruleName" value="ActiveSalesRuleWithPercentPriceDiscountCoupon"/> <argument name="couponCode" value="ActiveSalesRuleWithPercentPriceDiscountCoupon.coupon_code"/> </actionGroup> @@ -41,4 +41,4 @@ <argument name="ruleName" value="ActiveSalesRuleWithPercentPriceDiscountCoupon.name"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteInactiveSalesRuleAndVerifyDeleteMessageTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteInactiveSalesRuleAndVerifyDeleteMessageTest.xml index 1570bfbdb7a23..39a5d0f6a7131 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteInactiveSalesRuleAndVerifyDeleteMessageTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteInactiveSalesRuleAndVerifyDeleteMessageTest.xml @@ -44,7 +44,7 @@ </actionGroup> <!--Verify customer don't see updated virtual product link on category page --> - <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKey" stepKey="openProductPageAndVerifyProduct"> + <actionGroup ref="AssertProductNameAndSkuInStorefrontProductPageByCustomAttributeUrlKeyActionGroup" stepKey="openProductPageAndVerifyProduct"> <argument name="product" value="$$initialSimpleProduct$$"/> </actionGroup> @@ -62,4 +62,4 @@ <argument name="qty" value="1"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml index 1eba06126ff6f..ec9246f7c33bd 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml @@ -88,14 +88,14 @@ <deleteData createDataKey="createCategory" stepKey="deleteApiCategory"/> <actionGroup ref="logout" stepKey="logout"/> </after> - + <!-- Create the rule --> <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceList"/> <waitForPageLoad stepKey="waitForRulesPage"/> <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> - <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> <selectOption selector="{{AdminCartPriceRulesFormSection.coupon}}" userInput="Specific Coupon" stepKey="selectCouponType"/> @@ -131,7 +131,7 @@ <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage2"/> <!--View and edit cart--> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="clickViewAndEditCartFromMiniCart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="clickViewAndEditCartFromMiniCart"/> <click selector="{{DiscountSection.DiscountTab}}" stepKey="scrollToDiscountTab" /> <fillField selector="{{DiscountSection.CouponInput}}" userInput="ABCD" stepKey="fillCouponCode" /> <click selector="{{DiscountSection.ApplyCodeBtn}}" stepKey="applyCode"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/PriceRuleCategoryNestingTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/PriceRuleCategoryNestingTest.xml index 091e09e32f1e6..f99b19f4a6289 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/PriceRuleCategoryNestingTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/PriceRuleCategoryNestingTest.xml @@ -32,7 +32,7 @@ </before> <after> <deleteData createDataKey="subcategory1" stepKey="deleteCategory1"/> - <amOnPage url="{{_ENV.MAGENTO_BACKEND_NAME}}/admin/auth/logout/" stepKey="amOnLogoutPage"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <!-- Login as admin and open page for creation new Price Rule --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml index ffd50e43a2344..832b9ef8bd4b4 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml @@ -42,7 +42,7 @@ <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> - <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml index 765f10e44d81b..9882b04bdc956 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml @@ -42,7 +42,7 @@ <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> - <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml index cde5e351e1b70..ad060c1412c8c 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml @@ -42,7 +42,7 @@ <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> - <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml index d6806e2a2966b..19ffb7c36f992 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml @@ -42,7 +42,7 @@ <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> - <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml index 8ff747607ea30..510bfabf303f5 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml @@ -42,7 +42,7 @@ <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{SimpleSalesRule.name}}" stepKey="fillRuleName"/> <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsites"/> - <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="selectNotLoggedInCustomerGroup"/> + <actionGroup ref="SelectNotLoggedInCustomerGroupActionGroup" stepKey="selectNotLoggedInCustomerGroup"/> <generateDate date="-1 day" format="m/d/Y" stepKey="yesterdayDate"/> <fillField selector="{{AdminCartPriceRulesFormSection.fromDate}}" userInput="{$yesterdayDate}" stepKey="fillFromDate"/> <click selector="{{PriceRuleConditionsSection.conditionsTab}}" stepKey="expandConditions"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartRuleCouponForFreeShippingTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartRuleCouponForFreeShippingTest.xml new file mode 100644 index 0000000000000..3526ab20aa6b4 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartRuleCouponForFreeShippingTest.xml @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCartRuleCouponForFreeShippingTest"> + <annotations> + <stories value="Create Sales Rule"/> + <title value="Create Cart Price Rule for Free Shipping And Verify Coupon Code will shown in Order's totals"/> + <description value="Test that Coupon Code of Cart Price Rule without discount for Product price but with Free shipping will shown in Order's totals"/> + <testCaseId value="MC-21923"/> + <useCaseId value="MC-20387"/> + <severity value="MAJOR"/> + <group value="SalesRule"/> + </annotations> + + <before> + <!-- Create Simple Product --> + <createData entity="defaultSimpleProduct" stepKey="createSimpleProduct"/> + <!-- Create Cart Price Rule without discount but with free shipping --> + <createData entity="ApiSalesRule" stepKey="createCartPriceRule"> + <field key="simple_free_shipping">1</field> + <field key="discount_amount">0</field> + </createData> + <!-- Create Coupon code for the Cart Price Rule --> + <createData entity="ApiSalesRuleCoupon" stepKey="createCartPriceRuleCoupon"> + <requiredEntity createDataKey="createCartPriceRule"/> + </createData> + <!-- Create Customer with filled Shipping & Billing Address --> + <createData entity="CustomerEntityOne" stepKey="createCustomer"/> + </before> + + <after> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutFromStorefront"/> + <deleteData createDataKey="createCartPriceRule" stepKey="deleteSalesRule"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <actionGroup ref="logout" stepKey="logoutFromBackend"/> + </after> + + <!-- Login with created Customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Add Simple Product to Cart --> + <actionGroup ref="StorefrontAddSimpleProductToShoppingCartActionGroup" stepKey="addProductToCart"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + + <!-- Go to Checkout --> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckout"/> + + <!-- Go to Order review --> + <actionGroup ref="StorefrontCheckoutForwardFromShippingStepActionGroup" stepKey="goToCheckoutReview"/> + + <!-- Apply Discount Coupon to the Order --> + <actionGroup ref="StorefrontApplyDiscountCodeActionGroup" stepKey="applyDiscountCoupon"> + <argument name="discountCode" value="$createCartPriceRuleCoupon.code$"/> + </actionGroup> + + <!-- Assert Coupon Code will shown in Shipping total --> + <actionGroup ref="AssertStorefrontShippingLabelDescriptionInOrderSummaryActionGroup" stepKey="assertCouponCodeInShippingLabel"> + <argument name="labelDescription" value="$createCartPriceRuleCoupon.code$"/> + </actionGroup> + + <!-- Select payment solution --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="clickCheckMoneyOrderPayment"/> + + <!-- Place Order --> + <actionGroup ref="ClickPlaceOrderActionGroup" stepKey="placeOrder"/> + + <!-- Go To Order View --> + <click selector="{{CheckoutSuccessMainSection.orderLink}}" stepKey="goToViewOrder"/> + + <!-- Assert Coupon Code will shown in Shipping total description in Order View page --> + <actionGroup ref="AssertStorefrontShippingDescriptionInOrderViewActionGroup" stepKey="assertCouponCodeInShippingTotalDescription"> + <argument name="description" value="$createCartPriceRuleCoupon.code$"/> + </actionGroup> + + <!-- Keep Order Id --> + <grabFromCurrentUrl regex="~/order_id/(\d+)/~" stepKey="grabOrderId"/> + + <!-- Login to admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Go to created Order --> + <amOnPage url="{{AdminOrderPage.url({$grabOrderId})}}" stepKey="goToAdminViewOrder"/> + <waitForPageLoad stepKey="waitForOrderPage"/> + + <!-- Assert Coupon Code will shown in Shipping total description --> + <actionGroup ref="AssertAdminShippingDescriptionInOrderViewActionGroup" stepKey="seeCouponInShippingDescription"> + <argument name="description" value="$createCartPriceRuleCoupon.code$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml new file mode 100644 index 0000000000000..f12b8ea7ca331 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest.xml @@ -0,0 +1,125 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCartTotalValueWithFullDiscountUsingCartRuleTest"> + <annotations> + <features value="SalesRule"/> + <stories value="Cart total with full discount"/> + <title value="Cart Total value when 100% discount applied through Cart Rule"/> + <description value="Cart Total value when 100% discount applied through Cart Rule"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-19524"/> + <useCaseId value="MC-17869"/> + <group value="SalesRule"/> + </annotations> + <before> + <!-- log in --> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <!-- Set configurations --> + <magentoCLI command="config:set carriers/tablerate/active 1" stepKey="setShippingMethodEnabled"/> + <magentoCLI command="config:set carriers/tablerate/condition_name package_value" stepKey="setShippingMethodConditionName"/> + <magentoCLI command="config:set tax/calculation/price_includes_tax 1" stepKey="setCatalogPrice"/> + <magentoCLI command="config:set tax/calculation/shipping_includes_tax 1" stepKey="setSippingPrice"/> + <magentoCLI command="config:set tax/calculation/cross_border_trade_enabled 0" stepKey="setCrossBorderTrade"/> + <magentoCLI command="config:set tax/calculation/discount_tax 1" stepKey="setDiscount"/> + <magentoCLI command="config:set tax/cart_display/price 2" stepKey="setPrice"/> + <magentoCLI command="config:set tax/cart_display/subtotal 2" stepKey="setSubtotal"/> + <magentoCLI command="config:set carriers/freeshipping/active 1" stepKey="setFreeShipping"/> + <createData entity="defaultTaxRule" stepKey="initialTaxRule"/> + <createData entity="defaultTaxRate" stepKey="initialTaxRate"/> + <!-- Go to tax rule page --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> + <!-- Add tax rule with 20% tax rate --> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> + <argument name="taxCode" value="SimpleTaxNYRate"/> + </actionGroup> + <click stepKey="clickSave" selector="{{AdminStoresMainActionsSection.saveButton}}"/> + <!-- Create cart price rule --> + <actionGroup ref="AdminCreateCartPriceRuleActionGroup" stepKey="createCartPriceRule"> + <argument name="ruleName" value="SalesRuleWithFullDiscount"/> + </actionGroup> + <!-- Create 3 simple product --> + <createData entity="SimpleProduct2" stepKey="createSimpleProductFirst"> + <field key="price">5.10</field> + </createData> + <createData entity="SimpleProduct2" stepKey="createSimpleProductSecond"> + <field key="price">5.10</field> + </createData> + <createData entity="SimpleProduct2" stepKey="createSimpleProductThird"> + <field key="price">5.50</field> + </createData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <!-- Removed created Data --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulesPage"/> + <waitForPageLoad stepKey="waitForRulesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteRule"> + <argument name="name" value="SampleRule"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + <!-- Delete the tax rate that were created --> + <amOnPage url="{{AdminTaxRateGridPage.url}}" stepKey="goToTaxRatesPage"/> + <waitForPageLoad stepKey="waitForRatesPage"/> + <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deleteNYRate"> + <argument name="name" value="{{SimpleTaxNYRate.state}}-{{SimpleTaxNYRate.rate}}"/> + <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> + </actionGroup> + <actionGroup ref="DeleteCartPriceRuleByName" stepKey="deleteCartPriceRule"> + <argument name="ruleName" value="{{SalesRuleWithFullDiscount.name}}"/> + </actionGroup> + <!-- Delete products --> + <deleteData createDataKey="createSimpleProductFirst" stepKey="deleteSimpleProductFirst"/> + <deleteData createDataKey="createSimpleProductSecond" stepKey="deleteSimpleProductSecond"/> + <deleteData createDataKey="createSimpleProductThird" stepKey="deleteSimpleProductThird"/> + <!-- Unset configuration --> + <magentoCLI command="config:set carriers/tablerate/active 0" stepKey="unsetShippingMethodEnabled"/> + <magentoCLI command="config:set tax/calculation/price_includes_tax 0" stepKey="unsetCatalogPrice"/> + <magentoCLI command="config:set tax/calculation/shipping_includes_tax 0" stepKey="unsetSippingPrice"/> + <magentoCLI command="config:set tax/calculation/cross_border_trade_enabled 1" stepKey="unsetCrossBorderTrade"/> + <magentoCLI command="config:set tax/calculation/discount_tax 0" stepKey="unsetDiscount"/> + <magentoCLI command="config:set tax/cart_display/price 1" stepKey="unsetPrice"/> + <magentoCLI command="config:set tax/cart_display/subtotal 1" stepKey="unsetSubtotal"/> + <magentoCLI command="config:set carriers/freeshipping/active 0" stepKey="unsetFreeShipping"/> + <!-- Log out --> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Add testing products to the cart --> + <amOnPage url="{{StorefrontProductPage.url($$createSimpleProductFirst.custom_attributes[url_key]$$)}}" stepKey="goToProductPage"/> + <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="2" stepKey="setQuantity"/> + <actionGroup ref="StorefrontAddToCartCustomOptionsProductPageActionGroup" stepKey="addProductToCard"> + <argument name="productName" value="$$createSimpleProductFirst.name$$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForPageLoad"/> + <amOnPage url="{{StorefrontProductPage.url($$createSimpleProductSecond.custom_attributes[url_key]$$)}}" stepKey="goToSecondProductPage"/> + <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="2" stepKey="setQuantityForTheSecondProduct"/> + <actionGroup ref="StorefrontAddToCartCustomOptionsProductPageActionGroup" stepKey="addSecondProductToCard"> + <argument name="productName" value="$$createSimpleProductSecond.name$$"/> + </actionGroup> + <amOnPage url="{{StorefrontProductPage.url($$createSimpleProductThird.custom_attributes[url_key]$$)}}" stepKey="goToThirdProductPage"/> + <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="2" stepKey="setQuantityForTheThirdProduct"/> + <actionGroup ref="StorefrontAddToCartCustomOptionsProductPageActionGroup" stepKey="addThirdProductToCard"> + <argument name="productName" value="$$createSimpleProductThird.name$$"/> + </actionGroup> + <see selector="{{StorefrontMinicartSection.quantity}}" userInput="6" stepKey="seeCartQuantity"/> + <!-- Go to the shopping cart page --> + <amOnPage url="{{CheckoutCartPage.url}}" stepKey="amOnPageShoppingCart"/> + <waitForPageLoad stepKey="waitForCheckoutPageLoad"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.orderTotal}}" stepKey="waitForOrderTotalVisible"/> + <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectCountry"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForOrderTotalUpdate"/> + <see selector="{{CheckoutCartSummarySection.discountAmount}}" userInput="-$29.00" stepKey="seeDiscountAmount"/> + <see selector="{{CheckoutCartSummarySection.subTotal}}" userInput="$29.00" stepKey="seeSubTotal"/> + <see selector="{{CheckoutCartSummarySection.orderTotal}}" userInput="0.00" stepKey="seeOrderTotal"/> + </test> +</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml index d8c5b42dbaaaf..dbeb4ba94061b 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 @@ </actionGroup> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!-- Assign config1 and the associated child products to CAT1 --> - <actionGroup ref="AdminAssignProductToCategory" stepKey="assignConfigurableProduct1ToCategory"> + <actionGroup ref="AdminAssignProductToCategoryActionGroup" stepKey="assignConfigurableProduct1ToCategory"> <argument name="productId" value="$$createConfigProductCreateConfigurableProduct1.id$$"/> <argument name="categoryName" value="$$createCategory.name$$"/> </actionGroup> - <actionGroup ref="AdminAssignProductToCategory" stepKey="assignConfig1ChildProduct1ToCategory"> + <actionGroup ref="AdminAssignProductToCategoryActionGroup" stepKey="assignConfig1ChildProduct1ToCategory"> <argument name="productId" value="$$createConfigChildProduct1CreateConfigurableProduct1.id$$"/> <argument name="categoryName" value="$$createCategory.name$$"/> </actionGroup> - <actionGroup ref="AdminAssignProductToCategory" stepKey="assignConfig1ChildProduct2ToCategory"> + <actionGroup ref="AdminAssignProductToCategoryActionGroup" stepKey="assignConfig1ChildProduct2ToCategory"> <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="assignConfigurableProduct2ToCategory2"> + <actionGroup ref="AdminAssignProductToCategoryActionGroup" stepKey="assignConfigurableProduct2ToCategory2"> <argument name="productId" value="$$createConfigProductCreateConfigurableProduct2.id$$"/> <argument name="categoryName" value="$$createCategory2.name$$"/> </actionGroup> - <actionGroup ref="AdminAssignProductToCategory" stepKey="assignConfig2ChildProduct1ToCategory2"> + <actionGroup ref="AdminAssignProductToCategoryActionGroup" stepKey="assignConfig2ChildProduct1ToCategory2"> <argument name="productId" value="$$createConfigChildProduct1CreateConfigurableProduct2.id$$"/> <argument name="categoryName" value="$$createCategory2.name$$"/> </actionGroup> - <actionGroup ref="AdminAssignProductToCategory" stepKey="assignConfig2ChildProduct2ToCategory2"> + <actionGroup ref="AdminAssignProductToCategoryActionGroup" stepKey="assignConfig2ChildProduct2ToCategory2"> <argument name="productId" value="$$createConfigChildProduct2CreateConfigurableProduct2.id$$"/> <argument name="categoryName" value="$$createCategory2.name$$"/> </actionGroup> @@ -94,7 +94,7 @@ <argument name="product" value="$$createConfigProductCreateConfigurableProduct1$$"/> <argument name="productCount" value="2"/> </actionGroup> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToCart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" 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 --> @@ -110,7 +110,7 @@ <argument name="productCount" value="4"/> </actionGroup> <!-- Discount amount is applied --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToCart2"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToCart2"/> <see selector="{{CheckoutCartSummarySection.discountTotal}}" userInput="-$100.00" stepKey="discountIsApply"/> </test> </tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToGroupedProductWithInvisibleIndividualProductTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToGroupedProductWithInvisibleIndividualProductTest.xml new file mode 100644 index 0000000000000..13b2661dcb9d0 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToGroupedProductWithInvisibleIndividualProductTest.xml @@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCategoryRulesShouldApplyToGroupedProductWithInvisibleIndividualProductTest"> + <annotations> + <features value="SalesRule"/> + <stories value="Create cart price rule"/> + <title value="Category rules should apply to grouped product with invisible individual products"/> + <description value="Category rules should apply to grouped product with invisible individual products"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-13608"/> + <group value="SalesRule"/> + </annotations> + <before> + <createData entity="ApiCategory" stepKey="createCategoryOne"/> + <createData entity="ApiSimpleProduct" stepKey="createFirstSimpleProduct"> + <field key ="price">100</field> + <field key="visibility">1</field> + <requiredEntity createDataKey="createCategoryOne"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createSecondSimpleProduct"> + <field key ="price">200</field> + <field key="visibility">1</field> + <requiredEntity createDataKey="createCategoryOne"/> + </createData> + <createData entity="ApiCategory" stepKey="createCategoryTwo"/> + <createData entity="ApiSimpleProduct" stepKey="createThirdSimpleProduct"> + <field key ="price">300</field> + <field key="visibility">1</field> + <requiredEntity createDataKey="createCategoryTwo"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createFourthSimpleProduct"> + <field key ="price">400</field> + <field key="visibility">1</field> + <requiredEntity createDataKey="createCategoryTwo"/> + </createData> + <createData entity="ApiGroupedProduct2" stepKey="createGroupedProduct"> + <requiredEntity createDataKey="createCategoryOne"/> + </createData> + <createData entity="OneSimpleProductLink" stepKey="addFirstProduct"> + <requiredEntity createDataKey="createGroupedProduct"/> + <requiredEntity createDataKey="createFirstSimpleProduct"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addFirstProduct" stepKey="addSecondProduct"> + <requiredEntity createDataKey="createGroupedProduct"/> + <requiredEntity createDataKey="createSecondSimpleProduct"/> + </updateData> + <createData entity="ApiGroupedProduct2" stepKey="createSecondGroupedProduct"> + <requiredEntity createDataKey="createCategoryTwo"/> + </createData> + <createData entity="OneSimpleProductLink" stepKey="addThirdProduct"> + <requiredEntity createDataKey="createSecondGroupedProduct"/> + <requiredEntity createDataKey="createThirdSimpleProduct"/> + </createData> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addThirdProduct" stepKey="addFourthProduct"> + <requiredEntity createDataKey="createSecondGroupedProduct"/> + <requiredEntity createDataKey="createFourthSimpleProduct"/> + </updateData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="createFirstSimpleProduct" stepKey="deleteFirstSimpleProduct"/> + <deleteData createDataKey="createSecondSimpleProduct" stepKey="deleteSecondSimpleProduct"/> + <deleteData createDataKey="createThirdSimpleProduct" stepKey="deleteThirdSimpleProduct"/> + <deleteData createDataKey="createFourthSimpleProduct" stepKey="deleteFourthSimpleProduct"/> + <deleteData createDataKey="createGroupedProduct" stepKey="deleteGroupedProduct"/> + <deleteData createDataKey="createSecondGroupedProduct" stepKey="deleteSecondGroupedProduct"/> + <deleteData createDataKey="createCategoryOne" stepKey="deleteCategoryOne"/> + <deleteData createDataKey="createCategoryTwo" stepKey="deleteCategoryTwo"/> + <actionGroup ref="AdminDeleteCartPriceRuleActionGroup" stepKey="deleteCartPriceRule"> + <argument name="ruleName" value="TestSalesRule"/> + </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Start to create new cart price rule via Category conditions --> + <actionGroup ref="AdminCreateCartPriceRuleWithConditionIsCategoryActionGroup" stepKey="createCartPriceRuleWithCondition"> + <argument name="ruleName" value="TestSalesRule"/> + <argument name="actionValue" value="$createCategoryTwo.id$"/> + </actionGroup> + <!-- Add SecondGroupedProduct to the cart --> + <actionGroup ref="StorefrontAddGroupedProductWithTwoLinksToCartActionGroup" stepKey="addSecondGroupedProductToCart"> + <argument name="product" value="$createSecondGroupedProduct$"/> + <argument name="linkedProduct1Name" value="$createThirdSimpleProduct.name$"/> + <argument name="linkedProduct2Name" value="$createFourthSimpleProduct.name$"/> + </actionGroup> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="openTheCartWithSecondGroupedProduct"/> + <!-- Discount amount is not applied --> + <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="checkDiscountIsNotApplied"> + <argument name="subtotal" value="$700.00"/> + <argument name="shipping" value="$10.00"/> + <argument name="total" value="$710.00"/> + </actionGroup> + <!-- Discount is absent in cart subtotal --> + <dontSeeElement selector="{{CheckoutCartSummarySection.discountLabel}}" stepKey="discountIsNotApplied"/> + <!-- Add FirstGroupedProduct to the cart --> + <actionGroup ref="StorefrontAddGroupedProductWithTwoLinksToCartActionGroup" stepKey="addFirsGroupedProductToCart"> + <argument name="product" value="$createGroupedProduct$"/> + <argument name="linkedProduct1Name" value="$createFirstSimpleProduct.name$"/> + <argument name="linkedProduct2Name" value="$createSecondSimpleProduct.name$"/> + </actionGroup> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="openTheCartWithFirstAndSecondGroupedProducts"/> + <!-- Discount amount is applied for product from first category only --> + <actionGroup ref="StorefrontCheckCartTotalWithDiscountCategoryActionGroup" stepKey="checkDiscountIsApplied"> + <argument name="subtotal" value="$1,000.00"/> + <argument name="shipping" value="$20.00"/> + <argument name="discount" value="150.00"/> + <argument name="total" value="$870.00"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/SalesRule/Test/Unit/Controller/Adminhtml/Promo/Quote/ExportCouponsCsvTest.php b/app/code/Magento/SalesRule/Test/Unit/Controller/Adminhtml/Promo/Quote/ExportCouponsCsvTest.php new file mode 100644 index 0000000000000..f3658ab52f8bb --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Unit/Controller/Adminhtml/Promo/Quote/ExportCouponsCsvTest.php @@ -0,0 +1,87 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\SalesRule\Test\Unit\Controller\Adminhtml\Promo\Quote; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCouponsCsv; +use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\Response\Http\FileFactory; +use Magento\Framework\View\Result\Layout; +use Magento\Framework\View\LayoutInterface; +use Magento\Framework\View\Element\AbstractBlock; +use Magento\SalesRule\Block\Adminhtml\Promo\Quote\Edit\Tab\Coupons\Grid; +use PHPUnit\Framework\TestCase; + +class ExportCouponsCsvTest extends TestCase +{ + /** + * @var ExportCouponsCsv + */ + private $controller; + + /** + * @var FileFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $fileFactoryMock; + + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var ResultFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $resultFactoryMock; + + /** + * Setup environment + */ + protected function setUp() + { + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->fileFactoryMock = $this->createMock(FileFactory::class); + $this->resultFactoryMock = $this->createMock(ResultFactory::class); + + $this->controller = $this->objectManagerHelper->getObject( + ExportCouponsCsv::class, + [ + 'fileFactory' => $this->fileFactoryMock, + 'resultFactory' => $this->resultFactoryMock + ] + ); + } + + /** + * Test execute function + */ + public function testExecute() + { + $fileName = 'coupon_codes.csv'; + + $resultLayoutMock = $this->createMock(Layout::class); + $layoutMock = $this->createMock(LayoutInterface::class); + $contentMock = $this->createPartialMock(AbstractBlock::class, ['getCsvFile']); + $this->resultFactoryMock + ->expects($this->once()) + ->method('create') + ->with(ResultFactory::TYPE_LAYOUT)->willReturn($resultLayoutMock); + $resultLayoutMock->expects($this->once())->method('getLayout')->willReturn($layoutMock); + $layoutMock->expects($this->once())->method('createBlock')->with(Grid::class) + ->willReturn($contentMock); + $contentMock->expects($this->once())->method('getCsvFile')->willReturn('csvFile'); + $this->fileFactoryMock + ->expects($this->once()) + ->method('create') + ->with($fileName, 'csvFile', DirectoryList::VAR_DIR); + + $this->controller->execute(); + } +} diff --git a/app/code/Magento/SalesRule/Test/Unit/Controller/Adminhtml/Promo/Quote/ExportCouponsXmlTest.php b/app/code/Magento/SalesRule/Test/Unit/Controller/Adminhtml/Promo/Quote/ExportCouponsXmlTest.php new file mode 100644 index 0000000000000..83ad95171033f --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Unit/Controller/Adminhtml/Promo/Quote/ExportCouponsXmlTest.php @@ -0,0 +1,89 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\SalesRule\Test\Unit\Controller\Adminhtml\Promo\Quote; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCouponsXml; +use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\Response\Http\FileFactory; +use Magento\Framework\View\Result\Layout; +use Magento\Framework\View\LayoutInterface; +use Magento\Framework\View\Element\AbstractBlock; +use Magento\SalesRule\Block\Adminhtml\Promo\Quote\Edit\Tab\Coupons\Grid; +use PHPUnit\Framework\TestCase; + +class ExportCouponsXmlTest extends TestCase +{ + /** + * @var ExportCouponsXml + */ + private $controller; + + /** + * @var FileFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $fileFactoryMock; + + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var ResultFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $resultFactoryMock; + + /** + * Setup environment + */ + protected function setUp() + { + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->fileFactoryMock = $this->createMock(FileFactory::class); + $this->resultFactoryMock = $this->createMock(ResultFactory::class); + + $this->controller = $this->objectManagerHelper->getObject( + ExportCouponsXml::class, + [ + 'fileFactory' => $this->fileFactoryMock, + 'resultFactory' => $this->resultFactoryMock + ] + ); + } + + /** + * Test execute function + */ + public function testExecute() + { + $fileName = 'coupon_codes.xml'; + + $resultLayoutMock = $this->createMock(Layout::class); + $layoutMock = $this->createMock(LayoutInterface::class); + $contentMock = $this->createPartialMock(AbstractBlock::class, ['getExcelFile']); + $this->resultFactoryMock + ->expects($this->once()) + ->method('create') + ->with(ResultFactory::TYPE_LAYOUT)->willReturn($resultLayoutMock); + $resultLayoutMock->expects($this->once())->method('getLayout')->willReturn($layoutMock); + $layoutMock->expects($this->once())->method('createBlock')->with(Grid::class) + ->willReturn($contentMock); + $contentMock->expects($this->once())->method('getExcelFile') + ->with($fileName) + ->willReturn('xmlFile'); + $this->fileFactoryMock + ->expects($this->once()) + ->method('create') + ->with($fileName, 'xmlFile', DirectoryList::VAR_DIR); + + $this->controller->execute(); + } +} diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Quote/Address/Total/ShippingDiscountTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Quote/Address/Total/ShippingDiscountTest.php new file mode 100644 index 0000000000000..b5b6d047c3af2 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Unit/Model/Quote/Address/Total/ShippingDiscountTest.php @@ -0,0 +1,228 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SalesRule\Test\Unit\Model\Quote\Address\Total; + +use Magento\SalesRule\Model\Quote\Address\Total\ShippingDiscount; +use Magento\SalesRule\Model\Validator; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Address; +use Magento\Quote\Api\Data\ShippingAssignmentInterface; +use Magento\Quote\Api\Data\ShippingInterface; +use Magento\Quote\Model\Quote\Address\Total; + +/** + * Class \Magento\SalesRule\Test\Unit\Model\Quote\Address\Total\ShippingDiscountTest + */ +class ShippingDiscountTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject | Validator + */ + protected $validatorMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject | Quote + */ + private $quoteMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject | Total + */ + private $totalMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject | Address + */ + private $addressMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject | ShippingAssignmentInterface + */ + private $shippingAssignmentMock; + + /** + * @var ShippingDiscount + */ + private $discount; + + protected function setUp() + { + $this->validatorMock = $this->getMockBuilder(\Magento\SalesRule\Model\Validator::class) + ->disableOriginalConstructor() + ->setMethods( + [ + 'reset', + 'processShippingAmount', + '__wakeup', + ] + ) + ->getMock(); + $this->quoteMock = $this->createMock(\Magento\Quote\Model\Quote::class); + $this->totalMock = $this->createPartialMock( + \Magento\Quote\Model\Quote\Address\Total::class, + [ + 'getDiscountAmount', + 'getDiscountDescription', + 'addTotalAmount', + 'addBaseTotalAmount', + 'setShippingDiscountAmount', + 'setBaseShippingDiscountAmount', + 'getSubtotal', + 'setSubtotalWithDiscount', + 'setBaseSubtotalWithDiscount', + 'getBaseSubtotal', + 'getBaseDiscountAmount', + 'setDiscountDescription' + ] + ); + + $this->addressMock = $this->createPartialMock( + Address::class, + [ + 'getQuote', + 'getShippingAmount', + 'getShippingDiscountAmount', + 'getBaseShippingDiscountAmount', + 'setShippingDiscountAmount', + 'setBaseShippingDiscountAmount', + 'getDiscountDescription', + 'setDiscountAmount', + 'setBaseDiscountAmount', + '__wakeup' + ] + ); + + $shipping = $this->createMock(ShippingInterface::class); + $shipping->expects($this->any())->method('getAddress')->willReturn($this->addressMock); + $this->shippingAssignmentMock = $this->createMock(ShippingAssignmentInterface::class); + $this->shippingAssignmentMock->expects($this->any())->method('getShipping')->willReturn($shipping); + + $this->discount = new ShippingDiscount( + $this->validatorMock + ); + } + + /** + * Test collect with the quote has no shipping amount discount + */ + public function testCollectNoShippingAmount() + { + $itemNoDiscount = $this->createMock(\Magento\Quote\Model\Quote\Item::class); + + $this->addressMock->expects($this->any())->method('getQuote')->willReturn($this->quoteMock); + + $this->addressMock->expects($this->any())->method('getShippingAmount')->willReturn(0); + + $this->shippingAssignmentMock->expects($this->any())->method('getItems') + ->willReturn([$itemNoDiscount]); + + $this->addressMock->expects($this->once())->method('setShippingDiscountAmount') + ->with(0) + ->willReturnSelf(); + $this->addressMock->expects($this->once())->method('setBaseShippingDiscountAmount') + ->with(0) + ->willReturnSelf(); + + /* Assert Collect function */ + $this->assertInstanceOf( + ShippingDiscount::class, + $this->discount->collect($this->quoteMock, $this->shippingAssignmentMock, $this->totalMock) + ); + } + + /** + * Test collect with the quote has shipping amount discount + */ + public function testCollectWithShippingAmountDiscount() + { + $shippingAmount = 100; + $shippingDiscountAmount = 50; + $baseShippingDiscountAmount = 50; + $discountDescription = 'Discount $50'; + $subTotal = 200; + $discountAmount = -100; + $baseSubTotal = 200; + $baseDiscountAmount = -100; + + $itemNoDiscount = $this->createMock(\Magento\Quote\Model\Quote\Item::class); + + $this->addressMock->expects($this->any())->method('getQuote')->willReturn($this->quoteMock); + + $this->addressMock->expects($this->any())->method('getShippingAmount')->willReturn($shippingAmount); + + $this->addressMock->expects($this->any())->method('getShippingDiscountAmount') + ->willReturn($shippingDiscountAmount); + $this->addressMock->expects($this->any())->method('getBaseShippingDiscountAmount') + ->willReturn($baseShippingDiscountAmount); + + $this->addressMock->expects($this->any())->method('getDiscountDescription') + ->willReturn($discountDescription); + + $this->shippingAssignmentMock->expects($this->any())->method('getItems') + ->willReturn([$itemNoDiscount]); + + $this->totalMock->expects($this->once())->method('addTotalAmount') + ->with('discount', -$shippingDiscountAmount)->willReturnSelf(); + $this->totalMock->expects($this->once())->method('addBaseTotalAmount') + ->with('discount', -$baseShippingDiscountAmount)->willReturnSelf(); + + $this->totalMock->expects($this->once())->method('setShippingDiscountAmount') + ->with($shippingDiscountAmount)->willReturnSelf(); + $this->totalMock->expects($this->once())->method('setBaseShippingDiscountAmount') + ->with($baseShippingDiscountAmount)->willReturnSelf(); + + $this->totalMock->expects($this->any())->method('getSubtotal') + ->willReturn($subTotal); + $this->totalMock->expects($this->any())->method('getDiscountAmount') + ->willReturn($discountAmount); + + $this->totalMock->expects($this->any())->method('getBaseSubtotal') + ->willReturn($baseSubTotal); + $this->totalMock->expects($this->any())->method('getBaseDiscountAmount') + ->willReturn($baseDiscountAmount); + + $this->totalMock->expects($this->once())->method('setDiscountDescription') + ->with($discountDescription)->willReturnSelf(); + + $this->totalMock->expects($this->once())->method('setSubtotalWithDiscount') + ->with(100)->willReturnSelf(); + $this->totalMock->expects($this->once())->method('setBaseSubtotalWithDiscount') + ->with(100)->willReturnSelf(); + + $this->addressMock->expects($this->once())->method('setDiscountAmount') + ->with($discountAmount)->willReturnSelf(); + + $this->addressMock->expects($this->once())->method('setBaseDiscountAmount') + ->with($baseDiscountAmount)->willReturnSelf(); + + /* Assert Collect function */ + $this->assertInstanceOf( + ShippingDiscount::class, + $this->discount->collect($this->quoteMock, $this->shippingAssignmentMock, $this->totalMock) + ); + } + + /** + * Test fetch function with discount = 100 + */ + public function testFetch() + { + $discountAmount = 100; + $discountDescription = 100; + $expectedResult = [ + 'code' => 'discount', + 'value' => 100, + 'title' => __('Discount (%1)', $discountDescription) + ]; + $this->totalMock->expects($this->once())->method('getDiscountAmount') + ->willReturn($discountAmount); + $this->totalMock->expects($this->once())->method('getDiscountDescription') + ->willReturn($discountDescription); + $this->assertEquals($expectedResult, $this->discount->fetch($this->quoteMock, $this->totalMock)); + } +} diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Quote/DiscountTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Quote/DiscountTest.php index 090dbd7fe5d6d..72355625318c5 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Model/Quote/DiscountTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Model/Quote/DiscountTest.php @@ -47,6 +47,11 @@ class DiscountTest extends \PHPUnit\Framework\TestCase */ protected $addressMock; + /** + * @var \Magento\SalesRule\Model\Rule\Action\Discount\DataFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $discountFactory; + protected function setUp() { $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -72,16 +77,35 @@ protected function setUp() $priceCurrencyMock = $this->createMock(\Magento\Framework\Pricing\PriceCurrencyInterface::class); $priceCurrencyMock->expects($this->any()) ->method('round') - ->will($this->returnCallback( - function ($argument) { - return round($argument, 2); - } - )); + ->will( + $this->returnCallback( + function ($argument) { + return round($argument, 2); + } + ) + ); $this->addressMock = $this->createPartialMock( \Magento\Quote\Model\Quote\Address::class, - ['getQuote', 'getAllItems', 'getShippingAmount', '__wakeup', 'getCustomAttributesCodes'] + [ + 'getQuote', + 'getAllItems', + 'getShippingAmount', + '__wakeup', + 'getCustomAttributesCodes', + 'getExtensionAttributes' + ] ); + $addressExtension = $this->getMockBuilder( + \Magento\Framework\Api\ExtensionAttributesInterface::class + )->setMethods(['setDiscounts', 'getDiscounts'])->getMock(); + $addressExtension->method('getDiscounts')->willReturn([]); + $addressExtension->expects($this->any()) + ->method('setDiscounts') + ->willReturn([]); + $this->addressMock->expects( + $this->any() + )->method('getExtensionAttributes')->will($this->returnValue($addressExtension)); $this->addressMock->expects($this->any()) ->method('getCustomAttributesCodes') ->willReturn([]); @@ -90,6 +114,10 @@ function ($argument) { $shipping->expects($this->any())->method('getAddress')->willReturn($this->addressMock); $this->shippingAssignmentMock = $this->createMock(\Magento\Quote\Api\Data\ShippingAssignmentInterface::class); $this->shippingAssignmentMock->expects($this->any())->method('getShipping')->willReturn($shipping); + $this->discountFactory = $this->createPartialMock( + \Magento\SalesRule\Model\Rule\Action\Discount\DataFactory::class, + ['create'] + ); /** @var \Magento\SalesRule\Model\Quote\Discount $discount */ $this->discount = $this->objectManager->getObject( @@ -101,14 +129,38 @@ function ($argument) { 'priceCurrency' => $priceCurrencyMock, ] ); + $discountData = $this->getMockBuilder(\Magento\SalesRule\Model\Rule\Action\Discount\Data::class) + ->setConstructorArgs( + [ + 'amount' => 0, + 'baseAmount' => 0, + 'originalAmount' => 0, + 'baseOriginalAmount' => 0 + ] + ) + ->getMock(); + $this->discountFactory->expects($this->any()) + ->method('create') + ->with($this->anything()) + ->will($this->returnValue($discountData)); } public function testCollectItemNoDiscount() { $itemNoDiscount = $this->createPartialMock( \Magento\Quote\Model\Quote\Item::class, - ['getNoDiscount', '__wakeup'] + ['getNoDiscount', '__wakeup', 'getExtensionAttributes'] ); + $itemExtension = $this->getMockBuilder( + \Magento\Framework\Api\ExtensionAttributesInterface::class + )->setMethods(['setDiscounts', 'getDiscounts'])->getMock(); + $itemExtension->method('getDiscounts')->willReturn([]); + $itemExtension->expects($this->any()) + ->method('setDiscounts') + ->willReturn([]); + $itemNoDiscount->expects( + $this->any() + )->method('getExtensionAttributes')->will($this->returnValue($itemExtension)); $itemNoDiscount->expects($this->once())->method('getNoDiscount')->willReturn(true); $this->validatorMock->expects($this->once())->method('sortItemsByPriority') ->with([$itemNoDiscount], $this->addressMock) @@ -178,10 +230,21 @@ public function testCollectItemHasChildren($childItemData, $parentData, $expecte 'getHasChildren', 'isChildrenCalculated', 'getChildren', + 'getExtensionAttributes', '__wakeup', ] ) ->getMock(); + $itemExtension = $this->getMockBuilder( + \Magento\Framework\Api\ExtensionAttributesInterface::class + )->setMethods(['setDiscounts', 'getDiscounts'])->getMock(); + $itemExtension->method('getDiscounts')->willReturn([]); + $itemExtension->expects($this->any()) + ->method('setDiscounts') + ->willReturn([]); + $itemWithChildren->expects( + $this->any() + )->method('getExtensionAttributes')->will($this->returnValue($itemExtension)); $itemWithChildren->expects($this->once())->method('getNoDiscount')->willReturn(false); $itemWithChildren->expects($this->once())->method('getParentItem')->willReturn(false); $itemWithChildren->expects($this->once())->method('getHasChildren')->willReturn(true); @@ -310,10 +373,21 @@ public function testCollectItemHasNoChildren() 'getHasChildren', 'isChildrenCalculated', 'getChildren', + 'getExtensionAttributes', '__wakeup', ] ) ->getMock(); + $itemExtension = $this->getMockBuilder( + \Magento\Framework\Api\ExtensionAttributesInterface::class + )->setMethods(['setDiscounts', 'getDiscounts'])->getMock(); + $itemExtension->method('getDiscounts')->willReturn([]); + $itemExtension->expects($this->any()) + ->method('setDiscounts') + ->willReturn([]); + $itemWithChildren->expects( + $this->any() + )->method('getExtensionAttributes')->will($this->returnValue($itemExtension)); $itemWithChildren->expects($this->once())->method('getNoDiscount')->willReturn(false); $itemWithChildren->expects($this->once())->method('getParentItem')->willReturn(false); $itemWithChildren->expects($this->once())->method('getHasChildren')->willReturn(false); diff --git a/app/code/Magento/SalesRule/etc/di.xml b/app/code/Magento/SalesRule/etc/di.xml index f44b172d6b479..4ba67e2fa5871 100644 --- a/app/code/Magento/SalesRule/etc/di.xml +++ b/app/code/Magento/SalesRule/etc/di.xml @@ -30,6 +30,10 @@ type="Magento\SalesRule\Model\Data\CouponMassDeleteResult" /> <preference for="Magento\SalesRule\Api\CouponManagementInterface" type="Magento\SalesRule\Model\Service\CouponManagementService" /> + <preference for="Magento\SalesRule\Api\Data\RuleDiscountInterface" + type="Magento\SalesRule\Model\Data\RuleDiscount" /> + <preference for="Magento\SalesRule\Api\Data\DiscountDataInterface" + type="Magento\SalesRule\Model\Data\DiscountData" /> <type name="Magento\SalesRule\Helper\Coupon"> <arguments> <argument name="couponParameters" xsi:type="array"> @@ -178,13 +182,12 @@ </argument> </arguments> </type> - <type name="Magento\Quote\Model\Cart\CartTotalRepository"> <plugin name="coupon_label_plugin" type="Magento\SalesRule\Plugin\CartTotalRepository" /> </type> - <type name="Magento\Sales\Model\Order"> - <plugin name="coupon_uses_increment_plugin" type="Magento\SalesRule\Plugin\CouponUsagesIncrement" sortOrder="20"/> + <type name="Magento\Sales\Model\Service\OrderService"> <plugin name="coupon_uses_decrement_plugin" type="Magento\SalesRule\Plugin\CouponUsagesDecrement" /> + <plugin name="coupon_uses_increment_plugin" type="Magento\SalesRule\Plugin\CouponUsagesIncrement" sortOrder="20"/> </type> <preference for="Magento\SalesRule\Model\Spi\CodeLimitManagerInterface" diff --git a/app/code/Magento/SalesRule/etc/extension_attributes.xml b/app/code/Magento/SalesRule/etc/extension_attributes.xml index 202ced4204f73..c69c309d8741b 100644 --- a/app/code/Magento/SalesRule/etc/extension_attributes.xml +++ b/app/code/Magento/SalesRule/etc/extension_attributes.xml @@ -7,6 +7,9 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd"> <extension_attributes for="Magento\Quote\Api\Data\CartItemInterface"> - <attribute code="discounts" type="string" /> + <attribute code="discounts" type="Magento\SalesRule\Api\Data\RuleDiscountInterface[]" /> + </extension_attributes> + <extension_attributes for="Magento\Quote\Api\Data\AddressInterface"> + <attribute code="discounts" type="Magento\SalesRule\Api\Data\RuleDiscountInterface[]" /> </extension_attributes> </config> \ No newline at end of file diff --git a/app/code/Magento/SalesRule/i18n/en_US.csv b/app/code/Magento/SalesRule/i18n/en_US.csv index 7511d147ae224..83a5aa76ba0c8 100644 --- a/app/code/Magento/SalesRule/i18n/en_US.csv +++ b/app/code/Magento/SalesRule/i18n/en_US.csv @@ -22,6 +22,7 @@ Conditions,Conditions Generate,Generate "Coupon Code","Coupon Code" Created,Created +Used,Used Uses,Uses No,No Yes,Yes diff --git a/app/code/Magento/SalesRule/registration.php b/app/code/Magento/SalesRule/registration.php index b3e6cf5bc1b14..53305ca8287f7 100644 --- a/app/code/Magento/SalesRule/registration.php +++ b/app/code/Magento/SalesRule/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_SalesRule', __DIR__); diff --git a/app/code/Magento/SalesRule/view/adminhtml/ui_component/sales_rule_form.xml b/app/code/Magento/SalesRule/view/adminhtml/ui_component/sales_rule_form.xml index 639e12006232b..e1c12f45012ee 100644 --- a/app/code/Magento/SalesRule/view/adminhtml/ui_component/sales_rule_form.xml +++ b/app/code/Magento/SalesRule/view/adminhtml/ui_component/sales_rule_form.xml @@ -297,6 +297,9 @@ </item> </argument> <settings> + <validation> + <rule name="validate-digits" xsi:type="boolean">true</rule> + </validation> <dataType>text</dataType> <label translate="true">Uses per Coupon</label> <dataScope>uses_per_coupon</dataScope> @@ -309,6 +312,9 @@ </item> </argument> <settings> + <validation> + <rule name="validate-digits" xsi:type="boolean">true</rule> + </validation> <notice translate="true"> Usage limit enforced for logged in customers only. </notice> @@ -356,6 +362,9 @@ </item> </argument> <settings> + <validation> + <rule name="validate-digits" xsi:type="boolean">true</rule> + </validation> <dataType>text</dataType> <label translate="true">Priority</label> <dataScope>sort_order</dataScope> @@ -422,6 +431,8 @@ <settings> <validation> <rule name="required-entry" xsi:type="boolean">true</rule> + <rule name="validate-number" xsi:type="boolean">true</rule> + <rule name="validate-zero-or-greater" xsi:type="boolean">true</rule> </validation> <dataType>text</dataType> <label translate="true">Discount Amount</label> @@ -435,6 +446,10 @@ </item> </argument> <settings> + <validation> + <rule name="validate-number" xsi:type="boolean">true</rule> + <rule name="validate-zero-or-greater" xsi:type="boolean">true</rule> + </validation> <dataType>text</dataType> <label translate="true">Maximum Qty Discount is Applied To</label> <dataScope>discount_qty</dataScope> @@ -447,6 +462,10 @@ </item> </argument> <settings> + <validation> + <rule name="validate-number" xsi:type="boolean">true</rule> + <rule name="validate-zero-or-greater" xsi:type="boolean">true</rule> + </validation> <dataType>text</dataType> <label translate="true">Discount Qty Step (Buy X)</label> <dataScope>discount_step</dataScope> diff --git a/app/code/Magento/SalesRule/view/frontend/web/js/action/cancel-coupon.js b/app/code/Magento/SalesRule/view/frontend/web/js/action/cancel-coupon.js index 72eec1be0766c..35395434cef1e 100644 --- a/app/code/Magento/SalesRule/view/frontend/web/js/action/cancel-coupon.js +++ b/app/code/Magento/SalesRule/view/frontend/web/js/action/cancel-coupon.js @@ -16,9 +16,10 @@ define([ 'Magento_Checkout/js/action/get-payment-information', 'Magento_Checkout/js/model/totals', 'mage/translate', - 'Magento_Checkout/js/model/full-screen-loader' + 'Magento_Checkout/js/model/full-screen-loader', + 'Magento_Checkout/js/action/recollect-shipping-rates' ], function ($, quote, urlManager, errorProcessor, messageContainer, storage, getPaymentInformationAction, totals, $t, - fullScreenLoader + fullScreenLoader, recollectShippingRates ) { 'use strict'; @@ -56,6 +57,7 @@ define([ var deferred = $.Deferred(); totals.isLoading(true); + recollectShippingRates(); getPaymentInformationAction(deferred); $.when(deferred).done(function () { isApplied(false); diff --git a/app/code/Magento/SalesRule/view/frontend/web/js/action/set-coupon-code.js b/app/code/Magento/SalesRule/view/frontend/web/js/action/set-coupon-code.js index 994ccf2b395d2..4dbc5820feae9 100644 --- a/app/code/Magento/SalesRule/view/frontend/web/js/action/set-coupon-code.js +++ b/app/code/Magento/SalesRule/view/frontend/web/js/action/set-coupon-code.js @@ -17,9 +17,10 @@ define([ 'mage/translate', 'Magento_Checkout/js/action/get-payment-information', 'Magento_Checkout/js/model/totals', - 'Magento_Checkout/js/model/full-screen-loader' + 'Magento_Checkout/js/model/full-screen-loader', + 'Magento_Checkout/js/action/recollect-shipping-rates' ], function (ko, $, quote, urlManager, errorProcessor, messageContainer, storage, $t, getPaymentInformationAction, - totals, fullScreenLoader + totals, fullScreenLoader, recollectShippingRates ) { 'use strict'; @@ -62,6 +63,7 @@ define([ isApplied(true); totals.isLoading(true); + recollectShippingRates(); getPaymentInformationAction(deferred); $.when(deferred).done(function () { fullScreenLoader.stopLoader(); diff --git a/app/code/Magento/SalesSequence/registration.php b/app/code/Magento/SalesSequence/registration.php index 57b9c79bc1055..576b3dc5b9efb 100644 --- a/app/code/Magento/SalesSequence/registration.php +++ b/app/code/Magento/SalesSequence/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_SalesSequence', __DIR__); diff --git a/app/code/Magento/SampleData/registration.php b/app/code/Magento/SampleData/registration.php index 05c37ae1aec60..2e86fc7e1cc48 100644 --- a/app/code/Magento/SampleData/registration.php +++ b/app/code/Magento/SampleData/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_SampleData', __DIR__); diff --git a/app/code/Magento/Search/Model/ResourceModel/Query/Collection.php b/app/code/Magento/Search/Model/ResourceModel/Query/Collection.php index e34c841f32bd1..a1bc1df3f9bdb 100644 --- a/app/code/Magento/Search/Model/ResourceModel/Query/Collection.php +++ b/app/code/Magento/Search/Model/ResourceModel/Query/Collection.php @@ -130,7 +130,6 @@ public function setQueryFilter($query) */ public function setPopularQueryFilter($storeIds = null) { - $this->getSelect()->reset( \Magento\Framework\DB\Select::FROM )->reset( @@ -140,13 +139,10 @@ public function setPopularQueryFilter($storeIds = null) )->from( ['main_table' => $this->getTable('search_query')] ); - if ($storeIds) { - $this->addStoreFilter($storeIds); - $this->getSelect()->where('num_results > 0'); - } elseif (null === $storeIds) { - $this->addStoreFilter($this->_storeManager->getStore()->getId()); - $this->getSelect()->where('num_results > 0'); - } + + $storeIds = $storeIds ?: $this->_storeManager->getStore()->getId(); + $this->addStoreFilter($storeIds); + $this->getSelect()->where('num_results > 0'); $this->getSelect()->order(['popularity desc']); @@ -172,10 +168,9 @@ public function setRecentQueryFilter() */ public function addStoreFilter($storeIds) { - if (!is_array($storeIds)) { - $storeIds = [$storeIds]; - } - $this->getSelect()->where('main_table.store_id IN (?)', $storeIds); + $condition = is_array($storeIds) ? 'main_table.store_id IN (?)' : 'main_table.store_id = ?'; + $this->getSelect()->where($condition, $storeIds); + return $this; } } diff --git a/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminDeleteAllSearchTermsActionGroup.xml b/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminDeleteAllSearchTermsActionGroup.xml new file mode 100644 index 0000000000000..be01df1bcf5a7 --- /dev/null +++ b/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminDeleteAllSearchTermsActionGroup.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="AdminDeleteAllSearchTermsActionGroup"> + <selectOption userInput="selectAll" selector="{{AdminCatalogSearchTermIndexSection.selectMassActionCheckbox}}" stepKey="checkAllSearchTerms"/> + <selectOption selector="{{AdminCatalogSearchTermIndexSection.massActions}}" userInput="delete" stepKey="selectDeleteOption"/> + <click selector="{{AdminCatalogSearchTermIndexSection.submit}}" stepKey="clickSubmitButton"/> + <click selector="{{AdminCatalogSearchTermIndexSection.okButton}}" stepKey="clickOkButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminDeleteSearchTermActionGroup.xml b/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminDeleteSearchTermActionGroup.xml new file mode 100644 index 0000000000000..2013dfcf0e3b2 --- /dev/null +++ b/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminDeleteSearchTermActionGroup.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="AdminDeleteSearchTermActionGroup"> + <annotations> + <description>Deletes the Search Terms in the Admin Search Term grid.</description> + </annotations> + + <selectOption selector="{{AdminCatalogSearchTermIndexSection.massActions}}" userInput="delete" stepKey="selectDeleteOption"/> + <click selector="{{AdminCatalogSearchTermIndexSection.submit}}" stepKey="clickSubmitButton"/> + <click selector="{{AdminCatalogSearchTermIndexSection.okButton}}" stepKey="clickOkButton"/> + <waitForElementVisible selector="{{AdminCatalogSearchTermMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminSearchTermActionGroup.xml b/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminSearchTermActionGroup.xml deleted file mode 100644 index aeadf69050912..0000000000000 --- a/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminSearchTermActionGroup.xml +++ /dev/null @@ -1,47 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!-- Filter by search query and select --> - <actionGroup name="searchTermFilterBySearchQuery"> - <annotations> - <description>Fills in the provided Search Query on the Admin Search Term grid page.</description> - </annotations> - <arguments> - <argument name="searchQuery" type="string"/> - </arguments> - - <click selector="{{AdminCatalogSearchTermIndexSection.resetFilterButton}}" stepKey="clickOnResetButton"/> - <waitForPageLoad stepKey="waitForResetFilter"/> - <fillField selector="{{AdminCatalogSearchTermIndexSection.searchQuery}}" userInput="{{searchQuery}}" stepKey="fillSearchQuery"/> - <click selector="{{AdminCatalogSearchTermIndexSection.searchButton}}" stepKey="clickSearchButton"/> - <waitForPageLoad stepKey="waitForSearchResultLoad"/> - <checkOption selector="{{AdminCatalogSearchTermIndexSection.searchTermRowCheckboxBySearchQuery(searchQuery)}}" stepKey="checkCheckBox"/> - </actionGroup> - - <!-- Delete search term --> - <actionGroup name="deleteSearchTerm"> - <annotations> - <description>Deletes the Search Terms in the Admin Search Term grid.</description> - </annotations> - - <selectOption selector="{{AdminCatalogSearchTermIndexSection.massActions}}" userInput="delete" stepKey="selectDeleteOption"/> - <click selector="{{AdminCatalogSearchTermIndexSection.submit}}" stepKey="clickSubmitButton"/> - <click selector="{{AdminCatalogSearchTermIndexSection.okButton}}" stepKey="clickOkButton"/> - <waitForElementVisible selector="{{AdminCatalogSearchTermMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> - </actionGroup> - - <!-- Delete all existing search terms --> - <actionGroup name="DeleteAllSearchTerms"> - <selectOption userInput="selectAll" selector="{{AdminCatalogSearchTermIndexSection.selectMassActionCheckbox}}" stepKey="checkAllSearchTerms"/> - <selectOption selector="{{AdminCatalogSearchTermIndexSection.massActions}}" userInput="delete" stepKey="selectDeleteOption"/> - <click selector="{{AdminCatalogSearchTermIndexSection.submit}}" stepKey="clickSubmitButton"/> - <click selector="{{AdminCatalogSearchTermIndexSection.okButton}}" stepKey="clickOkButton"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminSearchTermFilterBySearchQueryActionGroup.xml b/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminSearchTermFilterBySearchQueryActionGroup.xml new file mode 100644 index 0000000000000..4536b0126b872 --- /dev/null +++ b/app/code/Magento/Search/Test/Mftf/ActionGroup/AdminSearchTermFilterBySearchQueryActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- Filter by search query and select --> + <actionGroup name="AdminSearchTermFilterBySearchQueryActionGroup"> + <annotations> + <description>Fills in the provided Search Query on the Admin Search Term grid page.</description> + </annotations> + <arguments> + <argument name="searchQuery" type="string"/> + </arguments> + + <click selector="{{AdminCatalogSearchTermIndexSection.resetFilterButton}}" stepKey="clickOnResetButton"/> + <waitForPageLoad stepKey="waitForResetFilter"/> + <fillField selector="{{AdminCatalogSearchTermIndexSection.searchQuery}}" userInput="{{searchQuery}}" stepKey="fillSearchQuery"/> + <click selector="{{AdminCatalogSearchTermIndexSection.searchButton}}" stepKey="clickSearchButton"/> + <waitForPageLoad stepKey="waitForSearchResultLoad"/> + <checkOption selector="{{AdminCatalogSearchTermIndexSection.searchTermRowCheckboxBySearchQuery(searchQuery)}}" stepKey="checkCheckBox"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Search/Test/Mftf/Data/SearchEngineConfigData.xml b/app/code/Magento/Search/Test/Mftf/Data/SearchEngineConfigData.xml new file mode 100644 index 0000000000000..7a6eb86a5cf52 --- /dev/null +++ b/app/code/Magento/Search/Test/Mftf/Data/SearchEngineConfigData.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="SetDefaultSearchEngineConfig"> + <data key="path">catalog/search/engine</data> + <data key="value">mysql</data> + </entity> + <entity name="SetMinQueryLength3Config"> + <data key="path">catalog/search/min_query_length</data> + <data key="value">3</data> + </entity> + <entity name="SetMinQueryLength2Config"> + <data key="path">catalog/search/min_query_length</data> + <data key="value">2</data> + </entity> +</entities> \ No newline at end of file diff --git a/app/code/Magento/Search/Test/Mftf/Test/AdminGlobalSearchOnProductPageTest.xml b/app/code/Magento/Search/Test/Mftf/Test/AdminGlobalSearchOnProductPageTest.xml index 45a5f53c3e448..725f45c0bc6e3 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/AdminGlobalSearchOnProductPageTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/AdminGlobalSearchOnProductPageTest.xml @@ -24,12 +24,12 @@ </before> <after> <!-- Delete product --> - <actionGroup ref="deleteProductBySku" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> <argument name="sku" value="{{SimpleProduct.sku}}"/> </actionGroup> <!-- Delete category --> - <actionGroup ref="DeleteCategory" stepKey="deleteCreatedNewRootCategory"> + <actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCreatedNewRootCategory"> <argument name="categoryEntity" value="_defaultCategory"/> </actionGroup> @@ -40,20 +40,20 @@ <!-- Create Simple Product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="adminProductIndexPageAdd"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProductPage"> <argument name="product" value="SimpleProduct"/> </actionGroup> - <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="SimpleProduct"/> </actionGroup> <!-- Create new category for product --> - <actionGroup ref="FillNewProductCategory" stepKey="FillNewProductCategory"> + <actionGroup ref="FillNewProductCategoryActionGroup" stepKey="FillNewProductCategory"> <argument name="categoryName" value="{{_defaultCategory.name}}"/> </actionGroup> <!-- Save product form --> - <actionGroup ref="saveProductForm" stepKey="saveProductForm"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> <!-- Click on the magnifying glass to start searching --> <click selector="{{AdminGlobalSearchSection.globalSearch}}" stepKey="clickSearchBtn"/> diff --git a/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml b/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml index 67ccb51bf401e..01c361d336541 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml @@ -38,27 +38,27 @@ <waitForPageLoad stepKey="waitForAdminCatalogSearchTermIndexPageLoad"/> <!-- Select all created below search terms --> - <actionGroup ref="searchTermFilterBySearchQuery" stepKey="filterByFirstSearchQuery"> + <actionGroup ref="AdminSearchTermFilterBySearchQueryActionGroup" stepKey="filterByFirstSearchQuery"> <argument name="searchQuery" value="$$createFirstSearchTerm.query_text$$"/> </actionGroup> - <actionGroup ref="searchTermFilterBySearchQuery" stepKey="filterBySecondSearchQuery"> + <actionGroup ref="AdminSearchTermFilterBySearchQueryActionGroup" stepKey="filterBySecondSearchQuery"> <argument name="searchQuery" value="$$createSecondSearchTerm.query_text$$"/> </actionGroup> - <actionGroup ref="searchTermFilterBySearchQuery" stepKey="filterByThirdSearchQuery"> + <actionGroup ref="AdminSearchTermFilterBySearchQueryActionGroup" stepKey="filterByThirdSearchQuery"> <argument name="searchQuery" value="$$createThirdSearchTerm.query_text$$"/> </actionGroup> <!-- Delete created below search terms --> - <actionGroup ref="deleteSearchTerm" stepKey="deleteSearchTerms"/> + <actionGroup ref="AdminDeleteSearchTermActionGroup" stepKey="deleteSearchTerms"/> <!-- Assert search terms are absent on the search term page --> - <actionGroup ref="AssertSearchTermNotInGrid" stepKey="assertFirstSearchTermNotInGrid"> + <actionGroup ref="AssertSearchTermNotInGridActionGroup" stepKey="assertFirstSearchTermNotInGrid"> <argument name="searchQuery" value="$$createFirstSearchTerm.query_text$$"/> </actionGroup> - <actionGroup ref="AssertSearchTermNotInGrid" stepKey="assertSecondSearchTermNotInGrid"> + <actionGroup ref="AssertSearchTermNotInGridActionGroup" stepKey="assertSecondSearchTermNotInGrid"> <argument name="searchQuery" value="$$createSecondSearchTerm.query_text$$"/> </actionGroup> - <actionGroup ref="AssertSearchTermNotInGrid" stepKey="assertThirdSearchTermNotInGrid"> + <actionGroup ref="AssertSearchTermNotInGridActionGroup" stepKey="assertThirdSearchTermNotInGrid"> <argument name="searchQuery" value="$$createThirdSearchTerm.query_text$$"/> </actionGroup> @@ -70,14 +70,14 @@ <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="quickSearchForFirstSearchTerm"> <argument name="phrase" value="$$createFirstSearchTerm.query_text$$"/> </actionGroup> - <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmptyForFirstSearchTerm"/> + <actionGroup ref="StorefrontCheckSearchIsEmptyActionGroup" stepKey="checkEmptyForFirstSearchTerm"/> <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="quickSearchForSecondSearchTerm"> <argument name="phrase" value="$$createSecondSearchTerm.query_text$$"/> </actionGroup> - <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmptyForSecondSearchTerm"/> + <actionGroup ref="StorefrontCheckSearchIsEmptyActionGroup" stepKey="checkEmptyForSecondSearchTerm"/> <actionGroup ref="StorefrontCheckQuickSearchActionGroup" stepKey="quickSearchForThirdSearchTerm"> <argument name="phrase" value="$$createThirdSearchTerm.query_text$$"/> </actionGroup> - <actionGroup ref="StorefrontCheckSearchIsEmpty" stepKey="checkEmptyForThirdSearchTerm"/> + <actionGroup ref="StorefrontCheckSearchIsEmptyActionGroup" stepKey="checkEmptyForThirdSearchTerm"/> </test> </tests> diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductDescriptionTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductDescriptionTest.xml index c5124ac9c74a1..126d4612d7488 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductDescriptionTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductDescriptionTest.xml @@ -27,8 +27,8 @@ <waitForPageLoad stepKey="waitForAdminCatalogSearchTermIndexPageLoad"/> <!-- Delete all search terms --> <comment userInput="Delete all search terms" stepKey="deleteAllSearchTermsComment"/> - <actionGroup ref="DeleteAllSearchTerms" stepKey="deleteAllSearchTerms"/> - <actionGroup ref="deleteAllProductsUsingProductGrid" stepKey="deleteAllProducts"/> + <actionGroup ref="AdminDeleteAllSearchTermsActionGroup" stepKey="deleteAllSearchTerms"/> + <actionGroup ref="DeleteAllProductsUsingProductGridActionGroup" stepKey="deleteAllProducts"/> <!-- Create product with description --> <comment userInput="Create product with description" stepKey="createProductWithDescriptionComment"/> <createData entity="SimpleProductWithDescription" stepKey="simpleProduct"/> @@ -47,12 +47,12 @@ <waitForPageLoad stepKey="waitForAdminCatalogSearchTermIndexPageLoad"/> <!-- Filter the search term --> <comment userInput="Filter search term" stepKey="filterSearchTermComment"/> - <actionGroup ref="searchTermFilterBySearchQuery" stepKey="filterByThirdSearchQuery"> + <actionGroup ref="AdminSearchTermFilterBySearchQueryActionGroup" stepKey="filterByThirdSearchQuery"> <argument name="searchQuery" value="{{ApiProductDescription.value}}"/> </actionGroup> <!-- Delete created below search terms --> <comment userInput="Delete created below search terms" stepKey="deleteCreatedBelowSearchTermsComment"/> - <actionGroup ref="deleteSearchTerm" stepKey="deleteSearchTerms"/> + <actionGroup ref="AdminDeleteSearchTermActionGroup" stepKey="deleteSearchTerms"/> <actionGroup ref="logout" stepKey="logout"/> </after> <!-- Go to storefront home page --> @@ -79,7 +79,7 @@ <waitForPageLoad stepKey="waitForAdminCatalogSearchTermIndexPageLoad"/> <!-- Filter the search term --> <comment userInput="Filter search term" stepKey="filterSearchTermComment2"/> - <actionGroup ref="searchTermFilterBySearchQuery" stepKey="filterByThirdSearchQuery"> + <actionGroup ref="AdminSearchTermFilterBySearchQueryActionGroup" stepKey="filterByThirdSearchQuery"> <argument name="searchQuery" value="{{ApiProductDescription.value}}"/> </actionGroup> <!-- Assert Search Term in grid --> diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductNameTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductNameTest.xml index e49db08954e14..fc933c90341f9 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductNameTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductNameTest.xml @@ -38,11 +38,11 @@ <waitForPageLoad stepKey="waitForAdminCatalogSearchTermIndexPageLoad"/> <!--Filter the search term --> - <actionGroup ref="searchTermFilterBySearchQuery" stepKey="filterByThirdSearchQuery"> + <actionGroup ref="AdminSearchTermFilterBySearchQueryActionGroup" stepKey="filterByThirdSearchQuery"> <argument name="searchQuery" value="$$simpleProduct.name$$"/> </actionGroup> <!-- Delete created below search terms --> - <actionGroup ref="deleteSearchTerm" stepKey="deleteSearchTerms"/> + <actionGroup ref="AdminDeleteSearchTermActionGroup" stepKey="deleteSearchTerms"/> <actionGroup ref="logout" stepKey="logout"/> </after> diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductShortDescriptionTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductShortDescriptionTest.xml index a1aa8be999aea..65472b9e10282 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductShortDescriptionTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductShortDescriptionTest.xml @@ -39,12 +39,12 @@ <waitForPageLoad stepKey="waitForAdminCatalogSearchTermIndexPageLoad"/> <!--Filter the search term --> - <actionGroup ref="searchTermFilterBySearchQuery" stepKey="filterByThirdSearchQuery"> + <actionGroup ref="AdminSearchTermFilterBySearchQueryActionGroup" stepKey="filterByThirdSearchQuery"> <argument name="searchQuery" value="{{ApiProductShortDescription.value}}"/> </actionGroup> <!-- Delete created below search terms --> - <actionGroup ref="deleteSearchTerm" stepKey="deleteSearchTerms"/> + <actionGroup ref="AdminDeleteSearchTermActionGroup" stepKey="deleteSearchTerms"/> <actionGroup ref="logout" stepKey="logout"/> </after> diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductSkuTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductSkuTest.xml index 3a8443706c9c7..6e80823b78e0f 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductSkuTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductSkuTest.xml @@ -39,12 +39,12 @@ <waitForPageLoad stepKey="waitForAdminCatalogSearchTermIndexPageLoad"/> <!--Filter the search term --> - <actionGroup ref="searchTermFilterBySearchQuery" stepKey="filterByThirdSearchQuery"> + <actionGroup ref="AdminSearchTermFilterBySearchQueryActionGroup" stepKey="filterByThirdSearchQuery"> <argument name="searchQuery" value="$$simpleProduct.sku$$"/> </actionGroup> <!-- Delete created below search terms --> - <actionGroup ref="deleteSearchTerm" stepKey="deleteSearchTerms"/> + <actionGroup ref="AdminDeleteSearchTermActionGroup" stepKey="deleteSearchTerms"/> <actionGroup ref="logout" stepKey="logout"/> </after> diff --git a/app/code/Magento/Search/Test/Unit/Ui/Component/Listing/Column/SynonymActionsTest.php b/app/code/Magento/Search/Test/Unit/Ui/Component/Listing/Column/SynonymActionsTest.php new file mode 100644 index 0000000000000..d5563ec1cb289 --- /dev/null +++ b/app/code/Magento/Search/Test/Unit/Ui/Component/Listing/Column/SynonymActionsTest.php @@ -0,0 +1,149 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Search\Test\Unit\Ui\Component\Listing\Column; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\UrlInterface; +use Magento\Search\Ui\Component\Listing\Column\SynonymActions; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class SynonymActionsTest extends TestCase +{ + /** + * Stub synonym group id + */ + private const STUB_SYNONYM_GROUP_ID = 1; + + /** + * Synonym group delete url + */ + private const SYNONYM_GROUP_DELETE_URL = 'http://localhost/magento2/admin/search/synonyms/delete/group_id/%d'; + + /** + * Synonym group edit url + */ + private const SYNONYM_GROUP_EDIT_URL = 'http://localhost/magento2/admin/search/synonyms/edit/group_id/%d'; + + /** + * @var SynonymActions + */ + private $synonymActions; + + /** + * @var UrlInterface|MockObject + */ + private $urlBuilderMock; + + /** + * Setup environment to test + */ + protected function setup() + { + $this->urlBuilderMock = $this->createMock(UrlInterface::class); + + $objectManager = new ObjectManager($this); + + $this->synonymActions = $objectManager->getObject( + SynonymActions::class, + [ + 'urlBuilder' => $this->urlBuilderMock, + 'data' => [ + 'name' => 'actions' + ] + ] + ); + } + + /** + * Test prepareDataSource() with data source has no item + */ + public function testPrepareDataSourceWithNoItem() + { + $dataSource = [ + 'data' => [] + ]; + $expected = [ + 'data' => [] + ]; + /** + * Assert Result + */ + $this->assertEquals($expected, $this->synonymActions->prepareDataSource($dataSource)); + } + + /** + * Test prepareDataSource() with data source has items + */ + public function testPrepareDataSourceWithItems() + { + $dataSource = [ + 'data' => [ + 'items' => [ + [ + 'group_id' => self::STUB_SYNONYM_GROUP_ID + ] + ] + ] + ]; + + $expected = [ + 'data' => [ + 'items' => [ + [ + 'group_id' => self::STUB_SYNONYM_GROUP_ID, + 'actions' => [ + 'delete' => [ + 'href' => sprintf( + self::SYNONYM_GROUP_DELETE_URL, + self::STUB_SYNONYM_GROUP_ID + ), + 'label' => (string)__('Delete'), + 'confirm' => [ + 'title' => (string)__('Delete'), + 'message' => (string)__( + 'Are you sure you want to delete synonym group with id: %1?', + self::STUB_SYNONYM_GROUP_ID + ) + ], + '__disableTmpl' => true + ], + 'edit' => [ + 'href' => sprintf( + self::SYNONYM_GROUP_EDIT_URL, + self::STUB_SYNONYM_GROUP_ID + ), + 'label' => (string)__('View/Edit'), + '__disableTmpl' => true + ] + ] + ] + ] + ] + ]; + + $this->urlBuilderMock->method('getUrl')->will( + $this->returnValueMap([ + [ + SynonymActions::SYNONYM_URL_PATH_DELETE, ['group_id' => self::STUB_SYNONYM_GROUP_ID], + sprintf(self::SYNONYM_GROUP_DELETE_URL, self::STUB_SYNONYM_GROUP_ID) + ], + [ + SynonymActions::SYNONYM_URL_PATH_EDIT, ['group_id' => self::STUB_SYNONYM_GROUP_ID], + sprintf(self::SYNONYM_GROUP_EDIT_URL, self::STUB_SYNONYM_GROUP_ID) + ] + ]) + ); + + /** + * Assert Result + */ + $this->assertEquals($expected, $this->synonymActions->prepareDataSource($dataSource)); + } +} diff --git a/app/code/Magento/Search/etc/db_schema.xml b/app/code/Magento/Search/etc/db_schema.xml index ab4b54298c2a3..1a01ffa42401c 100644 --- a/app/code/Magento/Search/etc/db_schema.xml +++ b/app/code/Magento/Search/etc/db_schema.xml @@ -46,6 +46,10 @@ <index referenceId="SEARCH_QUERY_IS_PROCESSED" indexType="btree"> <column name="is_processed"/> </index> + <index referenceId="SEARCH_QUERY_STORE_ID_POPULARITY" indexType="btree"> + <column name="store_id"/> + <column name="popularity"/> + </index> </table> <table name="search_synonyms" resource="default" engine="innodb" comment="table storing various synonyms groups"> <column xsi:type="bigint" name="group_id" padding="20" unsigned="true" nullable="false" identity="true" diff --git a/app/code/Magento/Search/etc/db_schema_whitelist.json b/app/code/Magento/Search/etc/db_schema_whitelist.json index 71adbc68887d0..16bbd0ce9fa3c 100644 --- a/app/code/Magento/Search/etc/db_schema_whitelist.json +++ b/app/code/Magento/Search/etc/db_schema_whitelist.json @@ -17,7 +17,8 @@ "SEARCH_QUERY_QUERY_TEXT_STORE_ID_POPULARITY": true, "SEARCH_QUERY_STORE_ID": true, "SEARCH_QUERY_IS_PROCESSED": true, - "SEARCH_QUERY_SYNONYM_FOR": true + "SEARCH_QUERY_SYNONYM_FOR": true, + "SEARCH_QUERY_STORE_ID_POPULARITY": true }, "constraint": { "PRIMARY": true, @@ -43,4 +44,4 @@ "SEARCH_SYNONYMS_WEBSITE_ID_STORE_WEBSITE_WEBSITE_ID": true } } -} \ No newline at end of file +} diff --git a/app/code/Magento/Search/registration.php b/app/code/Magento/Search/registration.php index 177f95971c8af..48210f971c4c0 100644 --- a/app/code/Magento/Search/registration.php +++ b/app/code/Magento/Search/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Search', __DIR__); diff --git a/app/code/Magento/Search/view/frontend/templates/form.mini.phtml b/app/code/Magento/Search/view/frontend/templates/form.mini.phtml index 766dd4d992bd4..0dd9c819c855a 100644 --- a/app/code/Magento/Search/view/frontend/templates/form.mini.phtml +++ b/app/code/Magento/Search/view/frontend/templates/form.mini.phtml @@ -24,7 +24,8 @@ $helper = $this->helper(\Magento\Search\Helper\Data::class); data-mage-init='{"quickSearch":{ "formSelector":"#search_mini_form", "url":"<?= $block->escapeUrl($helper->getSuggestUrl())?>", - "destinationSelector":"#search_autocomplete"} + "destinationSelector":"#search_autocomplete", + "minSearchLength":"<?= $block->escapeHtml($helper->getMinQueryLength()) ?>"} }' type="text" name="<?= $block->escapeHtmlAttr($helper->getQueryParamName()) ?>" diff --git a/app/code/Magento/Search/view/frontend/web/js/form-mini.js b/app/code/Magento/Search/view/frontend/web/js/form-mini.js index 64e6eceb1eba6..b4493c5f38089 100644 --- a/app/code/Magento/Search/view/frontend/web/js/form-mini.js +++ b/app/code/Magento/Search/view/frontend/web/js/form-mini.js @@ -30,7 +30,7 @@ define([ $.widget('mage.quickSearch', { options: { autocomplete: 'off', - minSearchLength: 2, + minSearchLength: 3, responseFieldElements: 'ul li', selectClass: 'selected', template: diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenEditingUserTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenEditingUserTest.xml index 2ce54d6c0fda5..9f421668bdc4f 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenEditingUserTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminUserLockWhenEditingUserTest.xml @@ -20,14 +20,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <!-- - @TODO: Remove "executeJS" in scope of MQE-1561 - Hack to be able to pass current admin user password without hardcoding it. - --> - <executeJS function="return '{{DefaultAdminUser.password}}'" stepKey="adminPassword" /> - <createData entity="NewAdminUser" stepKey="user"> - <field key="current_password">{$adminPassword}</field> - </createData> + <createData entity="NewAdminUser" stepKey="user" /> <!-- Log in to Admin Panel --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> diff --git a/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml b/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml new file mode 100644 index 0000000000000..74a9c68cb2f79 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordComplexityTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="NewCustomerPasswordComplexityTest"> + <annotations> + <features value="Security"/> + <stories value="Checking customer's password complexity"/> + <title value="Notify the customer if password complexity does not match the requirements"/> + <description value="Notify the customer if password complexity does not match the requirements"/> + <testCaseId value="MC-14368"/> + <group value="security"/> + <group value="mtf_migrated"/> + </annotations> + + <!-- Go to storefront home page --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> + + <!-- See the Registration Link --> + <actionGroup ref="StorefrontSeeHeaderLinksActionGroup" stepKey="seeTheLink"/> + + <!-- Click the Registration Link --> + <actionGroup ref="StorefrontClickHeaderLinkActionGroup" stepKey="clickTheLink"> + <argument name="linkName" value="Create an Account"/> + </actionGroup> + + <!-- Fill Registration Form with not secure enough password --> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillRegistrationFormPasswordNotSecure"> + <argument name="customer" value="Simple_Customer_With_Not_Secure_Password"/> + </actionGroup> + + <!-- See the Error --> + <actionGroup ref="AssertMessageCustomerCreateAccountPasswordComplexityActionGroup" stepKey="seeTheErrorPasswordSecure"> + <argument name="message" value="Minimum of different classes of characters in password is 3. Classes of characters: Lower Case, Upper Case, Digits, Special Characters."/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordLengthTest.xml b/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordLengthTest.xml new file mode 100644 index 0000000000000..a10059d0603c5 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/NewCustomerPasswordLengthTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="NewCustomerPasswordLengthTest"> + <annotations> + <features value="Security"/> + <stories value="Checking customer's password length"/> + <title value="Notify the customer if password length does not match the requirements"/> + <description value="Notify the customer if password length does not match the requirements"/> + <testCaseId value="MC-14367"/> + <group value="security"/> + <group value="mtf_migrated"/> + </annotations> + + <!-- Go to storefront home page --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStoreFrontHomePage"/> + + <!-- See the Registration Link --> + <actionGroup ref="StorefrontSeeHeaderLinksActionGroup" stepKey="seeTheLink"/> + + <!-- Click the Registration Link --> + <actionGroup ref="StorefrontClickHeaderLinkActionGroup" stepKey="clickTheLink"> + <argument name="linkName" value="Create an Account"/> + </actionGroup> + + <!-- Fill Registration Form with Password length is bellow 8 Characters --> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillRegistrationFormPasswordLengthBellowEightCharacters"> + <argument name="customer" value="Simple_Customer_With_Password_Length_Is_Below_Eight_Characters"/> + </actionGroup> + + <!-- See the Error --> + <actionGroup ref="AssertMessageCustomerCreateAccountPasswordComplexityActionGroup" stepKey="seeTheErrorPasswordLength"> + <argument name="message" value="Minimum length of this field must be equal or greater than 8 symbols. Leading and trailing spaces will be ignored."/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Security/registration.php b/app/code/Magento/Security/registration.php index b5bc1db3aec6e..80cc28f8d2103 100644 --- a/app/code/Magento/Security/registration.php +++ b/app/code/Magento/Security/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Security', __DIR__); diff --git a/app/code/Magento/SendFriend/registration.php b/app/code/Magento/SendFriend/registration.php index f8edd64000b50..37453eca71e3a 100644 --- a/app/code/Magento/SendFriend/registration.php +++ b/app/code/Magento/SendFriend/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_SendFriend', __DIR__); diff --git a/app/code/Magento/SendFriend/view/frontend/email/product_share.html b/app/code/Magento/SendFriend/view/frontend/email/product_share.html index d2ed441494221..00a4d7b4d5ce5 100644 --- a/app/code/Magento/SendFriend/view/frontend/email/product_share.html +++ b/app/code/Magento/SendFriend/view/frontend/email/product_share.html @@ -10,10 +10,11 @@ "var email":"Recipient Email address", "var name":"Recipient name", "var message|raw":"Sender custom message", -"var sender_email":"Sender email", -"var sender_name":"Sender name", +"var sender_email":"Sender Email", +"var sender_name":"Sender Name", "var product_url":"URL for Product", -"var product_image":"URL for product small image (75 px)" +"var product_image":"URL for product small image (75 px)", +"var message":"Message" } @--> {{template config_path="design/email/header_template"}} diff --git a/app/code/Magento/Shipping/Model/Config/Source/Allmethods.php b/app/code/Magento/Shipping/Model/Config/Source/Allmethods.php index e310df8ed11cb..f64c24856eba5 100644 --- a/app/code/Magento/Shipping/Model/Config/Source/Allmethods.php +++ b/app/code/Magento/Shipping/Model/Config/Source/Allmethods.php @@ -5,6 +5,9 @@ */ namespace Magento\Shipping\Model\Config\Source; +/** + * @inheritdoc + */ class Allmethods implements \Magento\Framework\Option\ArrayInterface { /** @@ -33,6 +36,7 @@ public function __construct( /** * Return array of carriers. + * * If $isActiveOnlyFlag is set to true, will return only active carriers * * @param bool $isActiveOnlyFlag @@ -56,6 +60,11 @@ public function toOptionArray($isActiveOnlyFlag = false) ); $methods[$carrierCode] = ['label' => $carrierTitle, 'value' => []]; foreach ($carrierMethods as $methodCode => $methodTitle) { + + /** Check it $carrierMethods array was well formed */ + if (!$methodCode) { + continue; + } $methods[$carrierCode]['value'][] = [ 'value' => $carrierCode . '_' . $methodCode, 'label' => '[' . $carrierCode . '] ' . $methodTitle, diff --git a/app/code/Magento/Shipping/Model/Tracking/Result/Status.php b/app/code/Magento/Shipping/Model/Tracking/Result/Status.php index cb24bd8ebb0e8..784b806fa3f86 100644 --- a/app/code/Magento/Shipping/Model/Tracking/Result/Status.php +++ b/app/code/Magento/Shipping/Model/Tracking/Result/Status.php @@ -1,21 +1,29 @@ <?php +declare(strict_types=1); + /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Shipping\Model\Tracking\Result; /** - * Fields: - * - carrier: carrier code - * - carrierTitle: carrier title + * Tracking Status DataObject + * + * @method string|null getCarrier() + * @method Status setCarrier(string $carrierCode) + * @method string|null getCarrierTitle() + * @method Status setCarrierTitle(string $carrierTitle) */ -class Status extends \Magento\Shipping\Model\Tracking\Result\AbstractResult +class Status extends AbstractResult { /** + * Returns all Status data + * * @return array */ - public function getAllData() + public function getAllData(): array { return $this->_data; } diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertCreatedShipmentInShipmentsTabActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertCreatedShipmentInShipmentsTabActionGroup.xml new file mode 100644 index 0000000000000..1a7d3355e4ee4 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertCreatedShipmentInShipmentsTabActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + + <actionGroup name="AdminAssertCreatedShipmentsInShipmentsTabActionGroup"> + <click stepKey="navigateToShipmentsTab" selector="{{AdminOrderDetailsOrderViewSection.shipments}}"/> + <waitForPageLoad stepKey="waitForTabLoad"/> + <grabTextFrom selector="{{AdminShipmentsGridSection.shipmentId}}" stepKey="grabShipmentId"/> + <assertNotEmpty actual="$grabShipmentId" stepKey="assertShipmentIdIsNotEmpty" after="grabShipmentId"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertShipmentInShipmentsGridActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertShipmentInShipmentsGridActionGroup.xml new file mode 100644 index 0000000000000..de293c24a9c5d --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertShipmentInShipmentsGridActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertShipmentInShipmentsGrid"> + <arguments> + <argument name="shipmentId" type="string"/> + </arguments> + <!--Assert Shipment in Shipments Grid--> + <amOnPage url="{{AdminShipmentsGridPage.url}}" stepKey="onShipmentsGridPage"/> + <waitForPageLoad stepKey="waitForLoadingPage"/> + <conditionalClick selector="{{AdminShipmentsGridSection.clearFilters}}" dependentSelector="{{AdminShipmentsGridSection.clearFilters}}" visible="true" stepKey="clearFilter"/> + <waitForLoadingMaskToDisappear stepKey="waitForFilterLoad"/> + <click selector="{{AdminShipmentsGridSection.buttonFilters}}" stepKey="openFilterSearch"/> + <waitForLoadingMaskToDisappear stepKey="waitForFilterFields"/> + <fillField userInput="{{shipmentId}}" selector="{{AdminShipmentsGridSection.fieldShipment}}" stepKey="fillSearchByShipmentId"/> + <click selector="{{AdminShipmentsGridSection.applyFilter}}" stepKey="clickSearchButton"/> + <waitForLoadingMaskToDisappear stepKey="waitForSearchResult"/> + <see userInput="{{shipmentId}}" selector="{{AdminShipmentsGridSection.rowShipments}}" stepKey="seeShipmentId"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertShipmentItemsActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertShipmentItemsActionGroup.xml new file mode 100644 index 0000000000000..c4a0b4536fe20 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminAssertShipmentItemsActionGroup.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="AdminAssertShipmentItemsActionGroup"> + <arguments> + <argument name="product" defaultValue="" type="string"/> + <argument name="qty" defaultValue="" type="string"/> + </arguments> + <click selector="{{AdminShipmentsGridSection.shipmentId}}" stepKey="clickView"/> + <scrollTo selector="{{AdminShipmentItemsSection.itemName('1')}}" stepKey="scrollToShippedItems"/> + <see userInput="{{product}}" selector="{{AdminShipmentItemsSection.itemName('1')}}" stepKey="seeProductName"/> + <see userInput="{{qty}}" selector="{{AdminShipmentItemsSection.productQty}}" stepKey="seeQty"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminChangeTableRatesShippingMethodStatusActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminChangeTableRatesShippingMethodStatusActionGroup.xml index e0fec2a6dc4d2..e506ca3a7662f 100644 --- a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminChangeTableRatesShippingMethodStatusActionGroup.xml +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminChangeTableRatesShippingMethodStatusActionGroup.xml @@ -17,14 +17,4 @@ <uncheckOption selector="{{AdminShippingMethodTableRatesSection.enabledUseSystemValue}}" stepKey="uncheckUseSystemValue"/> <selectOption selector="{{AdminShippingMethodTableRatesSection.carriersTableRateActive}}" userInput="{{status}}" stepKey="changeTableRatesMethodStatus"/> </actionGroup> - <actionGroup name="AdminImportFileTableRatesShippingMethodActionGroup"> - <annotations> - <description>Import a file in Table Rates tab in Shipping Method config page.</description> - </annotations> - <arguments> - <argument name="file" type="string" defaultValue="test_tablerates.csv"/> - </arguments> - <conditionalClick selector="{{AdminShippingMethodTableRatesSection.carriersTableRateTab}}" dependentSelector="{{AdminShippingMethodTableRatesSection.carriersTableRateActive}}" visible="false" stepKey="expandTab"/> - <attachFile selector="{{AdminShippingMethodTableRatesSection.importFile}}" userInput="{{file}}" stepKey="attachFileForImport"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminCreateShipmentFromOrderPageActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminCreateShipmentFromOrderPageActionGroup.xml new file mode 100644 index 0000000000000..0e1358651c58a --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminCreateShipmentFromOrderPageActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!--Create Shipment With Tracking Number--> + <actionGroup name="AdminCreateShipmentFromOrderPage"> + <arguments> + <argument name="Title" defaultValue="" type="string"/> + <argument name="Number" defaultValue="" type="string"/> + <argument name="Comment" defaultValue="" type="string"/> + <argument name="Qty" defaultValue="" type="string"/> + </arguments> + + <click stepKey="clickShipButton" selector="{{AdminOrderDetailsMainActionsSection.ship}}"/> + <click stepKey="clickAddTrackingNumber" selector="{{AdminShipmentPaymentShippingSection.AddTrackingNumber}}"/> + <fillField stepKey="fillTitle" userInput="{{Title}}" selector="{{AdminShipmentPaymentShippingSection.Title('1')}}"/> + <fillField stepKey="fillNumber" userInput="{{Number}}" selector="{{AdminShipmentPaymentShippingSection.Number('1')}}"/> + <fillField stepKey="fillQty" userInput="{{Qty}}" selector="{{AdminShipmentItemsSection.itemQtyToShip('1')}}"/> + <fillField stepKey="fillComment" userInput="{{Comment}}" selector="{{AdminShipmentTotalSection.CommentText}}"/> + <click stepKey="clickSubmitButton" selector="{{AdminShipmentMainActionsSection.submitShipment}}"/> + <see userInput="The shipment has been created." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminImportFileTableRatesShippingMethodActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminImportFileTableRatesShippingMethodActionGroup.xml new file mode 100644 index 0000000000000..bfae3d8b76f19 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AdminImportFileTableRatesShippingMethodActionGroup.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="AdminImportFileTableRatesShippingMethodActionGroup"> + <annotations> + <description>Import a file in Table Rates tab in Shipping Method config page.</description> + </annotations> + <arguments> + <argument name="file" type="string" defaultValue="test_tablerates.csv"/> + </arguments> + <conditionalClick selector="{{AdminShippingMethodTableRatesSection.carriersTableRateTab}}" dependentSelector="{{AdminShippingMethodTableRatesSection.carriersTableRateActive}}" visible="false" stepKey="expandTab"/> + <attachFile selector="{{AdminShippingMethodTableRatesSection.importFile}}" userInput="{{file}}" stepKey="attachFileForImport"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AssertThereIsNoShipButtonActionGroup.xml b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AssertThereIsNoShipButtonActionGroup.xml new file mode 100644 index 0000000000000..10521769c5070 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/ActionGroup/AssertThereIsNoShipButtonActionGroup.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"> + <!--Create Shipment With Tracking Number--> + <actionGroup name="AssertThereIsNoShipButtonActionGroup"> + <dontSee stepKey="dontSeeShipButton" selector="{{AdminOrderDetailsMainActionsSection.ship}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Shipping/Test/Mftf/Data/AdminShippingSettingsConfigData.xml b/app/code/Magento/Shipping/Test/Mftf/Data/AdminShippingSettingsConfigData.xml index ad366fd7294e5..342472aab6f42 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Data/AdminShippingSettingsConfigData.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Data/AdminShippingSettingsConfigData.xml @@ -23,4 +23,14 @@ <entity name="AdminShippingSettingsOriginStreetAddress2ConfigData"> <data key="path">shipping/origin/street_line2</data> </entity> + <entity name="AdminFreeshippingActiveConfigData"> + <data key="path">carriers/freeshipping/active</data> + <data key="enabled">1</data> + <data key="disabled">0</data> + </entity> + <entity name="AdminFreeshippingMinimumOrderAmountConfigData"> + <data key="path">carriers/freeshipping/free_shipping_subtotal</data> + <data key="hundred">100</data> + <data key="default">0</data> + </entity> </entities> diff --git a/app/code/Magento/Shipping/Test/Mftf/Data/FreeShippingMethodData.xml b/app/code/Magento/Shipping/Test/Mftf/Data/FreeShippingMethodData.xml index d700aa622c177..3d3667e59903f 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Data/FreeShippingMethodData.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Data/FreeShippingMethodData.xml @@ -26,6 +26,7 @@ <requiredEntity type="title">freeTitleDefault</requiredEntity> <requiredEntity type="name">freeNameDefault</requiredEntity> <requiredEntity type="free_shipping_subtotal">freeShippingSubtotalDefault</requiredEntity> + <requiredEntity type="tax_including">TaxIncludingDefault</requiredEntity> <requiredEntity type="specificerrmsg">freeSpecificerrmsgDefault</requiredEntity> <requiredEntity type="sallowspecific">freeSallowspecificDefault</requiredEntity> <requiredEntity type="specificcountry">freeSpecificcountryDefault</requiredEntity> @@ -44,6 +45,9 @@ <entity name="freeShippingSubtotalDefault" type="free_shipping_subtotal"> <data key="value" /> </entity> + <entity name="TaxIncludingDefault" type="tax_including"> + <data key="value">0</data> + </entity> <entity name="freeSpecificerrmsgDefault" type="specificerrmsg"> <data key="value">This shipping method is not available. To use this shipping method, please contact us.</data> </entity> @@ -66,6 +70,17 @@ <entity name="freeShippingSubtotal" type="free_shipping_subtotal"> <data key="value">101</data> </entity> + <!--Set Free Shipping "Include Tax to Amount" to "Yes"--> + <entity name="SetTaxIncluding" type="free_shipping_method"> + <requiredEntity type="tax_including">TaxIncluding</requiredEntity> + </entity> + <entity name="TaxIncluding" type="tax_including"> + <data key="value">1</data> + </entity> + <!--Set to default Free Shipping "Include Tax to Amount"--> + <entity name="SetTaxIncludingToDefault" type="free_shipping_method"> + <requiredEntity type="tax_including">TaxIncludingDefault</requiredEntity> + </entity> <!--Set to default Free Shipping Subtotal--> <entity name="setFreeShippingSubtotalToDefault" type="free_shipping_method"> <requiredEntity type="free_shipping_subtotal">freeShippingSubtotalDefault</requiredEntity> diff --git a/app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_methods-meta.xml b/app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_methods-meta.xml index 5781b886386f6..14c0d6d5af725 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_methods-meta.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Metadata/shipping_methods-meta.xml @@ -66,6 +66,9 @@ <object key="free_shipping_subtotal" dataType="free_shipping_subtotal"> <field key="value">string</field> </object> + <object key="tax_including" dataType="tax_including"> + <field key="value">boolean</field> + </object> <object key="specificerrmsg" dataType="specificerrmsg"> <field key="value">string</field> </object> diff --git a/app/code/Magento/Shipping/Test/Mftf/Page/AdminShipmentsGridPage.xml b/app/code/Magento/Shipping/Test/Mftf/Page/AdminShipmentsGridPage.xml new file mode 100644 index 0000000000000..61aad55401248 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/Page/AdminShipmentsGridPage.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="AdminShipmentsGridPage" url="sales/shipment/" area="admin" module="Magento_Sales"> + <section name="AdminShipmentsGridSection"/> + </page> +</pages> diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentItemsSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentItemsSection.xml index 0345c3f2949f4..3630de8978924 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentItemsSection.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentItemsSection.xml @@ -16,5 +16,6 @@ <element name="nameColumn" type="text" selector=".order-shipment-table .col-product .product-title"/> <element name="skuColumn" type="text" selector=".order-shipment-table .col-product .product-sku-block"/> <element name="itemQtyInvoiced" type="text" selector="(//*[@class='col-ordered-qty']//th[contains(text(), 'Invoiced')]/following-sibling::td)[{{var}}]" parameterized="true"/> + <element name="productQty" type="text" selector="td.col-qty"/> </section> </sections> diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentPaymentShippingSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentPaymentShippingSection.xml index 48c7106c2d65e..eb94014d7a50c 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentPaymentShippingSection.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentPaymentShippingSection.xml @@ -17,7 +17,7 @@ <element name="AddTrackingNumber" type="button" selector="#tracking_numbers_table tfoot [data-ui-id='shipment-tracking-add-button']"/> <element name="Carrier" type="select" selector="#tracking_numbers_table tr:nth-of-type({{row}}) .col-carrier select" parameterized="true"/> <element name="Title" type="input" selector="#tracking_numbers_table tr:nth-of-type({{row}}) .col-title input" parameterized="true"/> - <element name="Number" type="input" selector="#tracking_numbers_table tr:nth-of-type({{row}} .col-number input)" parameterized="true"/> + <element name="Number" type="input" selector="#tracking_numbers_table tr:nth-of-type({{row}}) .col-number input" parameterized="true"/> <element name="Delete" type="button" selector="#tracking_numbers_table tr:nth-of-type({{row}} .col-delete button.action-delete)" parameterized="true"/> </section> </sections> \ No newline at end of file diff --git a/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentsGridSection.xml b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentsGridSection.xml new file mode 100644 index 0000000000000..84aed3052c736 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/Section/AdminShipmentsGridSection.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="AdminShipmentsGridSection"> + <element name="shipmentId" type="text" selector="//*[@id='sales_order_view_tabs_order_shipments_content']//tbody/tr/td[2]/div"/> + <element name="clearFilters" type="button" selector="button.action-tertiary.action-clear"/> + <element name="buttonFilters" type="button" selector=".data-grid-filters-action-wrap > button"/> + <element name="fieldShipment" type="input" selector="input[name='increment_id']"/> + <element name="applyFilter" type="button" selector="button[data-action='grid-filter-apply']"/> + <element name="rowShipments" type="text" selector="div.data-grid-cell-content"/> + </section> +</sections> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml index b2e3e2516a5c3..7784fdd31e58e 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateOrderCustomStoreShippingMethodTableRatesTest.xml @@ -78,22 +78,22 @@ <!--Assign product to custom website--> <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="goToProductEditPage"/> <waitForPageLoad stepKey="waitForProductPageLoad"/> - <actionGroup ref="unassignWebsiteFromProductActionGroup" stepKey="unassignWebsiteInProduct"> + <actionGroup ref="UnassignWebsiteFromProductActionGroup" stepKey="unassignWebsiteInProduct"> <argument name="website" value="{{_defaultWebsite.name}}"/> </actionGroup> <actionGroup ref="SelectProductInWebsitesActionGroup" stepKey="selectWebsiteInProduct"> <argument name="website" value="{{customWebsite.name}}"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!--Create order--> - <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> <argument name="customer" value="$$createCustomer$$"/> <argument name="storeView" value="customStore"/> </actionGroup> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToTheOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToTheOrder"> <argument name="product" value="$$createProduct$$"/> </actionGroup> - <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerInfo"> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerInfo"> <argument name="customer" value="$$createCustomer$$"/> <argument name="address" value="US_Address_TX"/> </actionGroup> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml new file mode 100644 index 0000000000000..0e4fd3ce9a86c --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.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="AdminCreatePartialShipmentEntityTest"> + <annotations> + <stories value="Create Partial Shipment Entity"/> + <title value="Create Partial Shipment for Offline Payment Methods"/> + <description value="Admin Should be Able to Create Partial Shipments"/> + <severity value="MAJOR"/> + <testCaseId value="MC-13331"/> + <group value="sales"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="SimpleProduct2" stepKey="createSimpleProduct"> + </createData> + <!-- Enable payment method one of "Check/Money Order" and shipping method one of "Free Shipping" --> + <magentoCLI command="config:set {{enabledCheckMoneyOrder.label}} {{enabledCheckMoneyOrder.value}}" stepKey="enableCheckMoneyOrder"/> + <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShipping"/> + <magentoCLI command="cache:clean config" stepKey="flushCache"/> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShippingMethod"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- TEST BODY --> + <!-- Create Order --> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="goToCreateOrderPage"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addProductToOrder"> + <argument name="product" value="$$createSimpleProduct$$"/> + <argument name="productQty" value="2"/> + </actionGroup> + <!-- Select Free shipping --> + <actionGroup ref="OrderSelectFreeShippingActionGroup" stepKey="selectFreeShippingOption"/> + <!--Click *Submit Order* button--> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="clickSubmitOrder"/> + <!-- Create Partial Shipment --> + <actionGroup ref="AdminCreateShipmentFromOrderPage" stepKey="createNewShipment"> + <argument name="Qty" value="1"/> + <argument name="Title" value="Title"/> + <argument name="Number" value="199"/> + <argument name="Comment" value="comments for shipment"/> + </actionGroup> + <!-- Assert There is no "Ship Button" in Order Information --> + <actionGroup ref="AssertThereIsNoShipButtonActionGroup" stepKey="dontSeeShipButton"/> + <!-- Assert Created Shipment in Shipments Tab--> + <actionGroup ref="AdminAssertCreatedShipmentsInShipmentsTabActionGroup" stepKey="assertCreatedShipment"/> + <grabTextFrom selector="{{AdminShipmentsGridSection.shipmentId}}" stepKey="grabShipmentId"/> + <!-- Assert Shipment items --> + <actionGroup ref="AdminAssertShipmentItemsActionGroup" stepKey="assertShipmentItems"> + <argument name="product" value="$$createSimpleProduct.name$$"/> + <argument name="qty" value="1"/> + </actionGroup> + <!-- Assert Created Shipment in Shipments Grid--> + <actionGroup ref="AdminAssertShipmentInShipmentsGrid" stepKey="assertShipmentInGrid"> + <argument name="shipmentId" value="{$grabShipmentId}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml new file mode 100644 index 0000000000000..f4087932a0710 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.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="AdminCreateShipmentEntityWithTrackingNumberTest"> + <annotations> + <stories value="Shipment Entity With Tracking Number"/> + <title value="Create Shipment for Offline Payment Methods"/> + <description value="Admin Should be Able to Create Shipments"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14330"/> + <group value="sales"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="SimpleProduct2" stepKey="createSimpleProduct"> + </createData> + <!-- Enable payment method one of "Check/Money Order" and shipping method one of "Free Shipping" --> + <magentoCLI command="config:set {{enabledCheckMoneyOrder.label}} {{enabledCheckMoneyOrder.value}}" stepKey="enableCheckMoneyOrder"/> + <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShipping"/> + <magentoCLI command="cache:clean config" stepKey="flushCache"/> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShippingMethod"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- TEST BODY --> + + <!-- Create Order --> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="goToCreateOrderPage"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addProductToOrder"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <!-- Select Free shipping --> + <actionGroup ref="OrderSelectFreeShippingActionGroup" stepKey="selectFreeShippingOption"/> + <!--Click *Submit Order* button--> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="clickSubmitOrder"/> + <!-- Create Shipment --> + <actionGroup ref="AdminCreateShipmentFromOrderPage" stepKey="createNewShipment"> + <argument name="Title" value="Title"/> + <argument name="Number" value="199"/> + <argument name="Qty" value="1"/> + <argument name="Comment" value="comments for shipment"/> + </actionGroup> + <!-- Assert There is no "Ship Button" in Order Information --> + <actionGroup ref="AssertThereIsNoShipButtonActionGroup" stepKey="dontSeeShipButton"/> + <!-- Assert Created Shipment in Shipments Tab--> + <actionGroup ref="AdminAssertCreatedShipmentsInShipmentsTabActionGroup" stepKey="assertCreatedShipment"/> + <grabTextFrom selector="{{AdminShipmentsGridSection.shipmentId}}" stepKey="grabShipmentId"/> + <!-- Assert Shipment items --> + <actionGroup ref="AdminAssertShipmentItemsActionGroup" stepKey="assertShipmentItems"> + <argument name="product" value="$$createSimpleProduct.name$$"/> + <argument name="qty" value="1"/> + </actionGroup> + <!-- Assert Created Shipment in Shipments Grid--> + <actionGroup ref="AdminAssertShipmentInShipmentsGrid" stepKey="assertShipmentInGrid"> + <argument name="shipmentId" value="{$grabShipmentId}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml index bb29a4a28bcf6..81fe492ffc005 100644 --- a/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml +++ b/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml @@ -54,7 +54,7 @@ <argument name="Customer" value="$$createCustomer$$"/> </actionGroup> <!--Add the created product to the shopping cart--> - <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart"> <argument name="product" value="$$createProduct$$"/> </actionGroup> <!--Proceed to Checkout from the mini cart--> diff --git a/app/code/Magento/Shipping/Test/Unit/Helper/DataTest.php b/app/code/Magento/Shipping/Test/Unit/Helper/DataTest.php new file mode 100644 index 0000000000000..b82e3537d26e2 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Unit/Helper/DataTest.php @@ -0,0 +1,102 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Shipping\Test\Unit\Helper; + +use PHPUnit\Framework\TestCase; +use Magento\Shipping\Helper\Data as HelperData; +use Magento\Framework\Url\DecoderInterface; +use Magento\Framework\App\Helper\Context; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * Data helper test + * + * Class \Magento\Shipping\Test\Unit\Helper\DataTest + */ +class DataTest extends TestCase +{ + /** + * @var HelperData + */ + private $helper; + + /** + * @var DecoderInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $urlDecoderMock; + + /** + * @var Context|\PHPUnit_Framework_MockObject_MockObject + */ + private $contextMock; + + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * Setup environment to test + */ + protected function setUp() + { + $this->contextMock = $this->createMock(Context::class); + $this->urlDecoderMock = $this->createMock(DecoderInterface::class); + $this->contextMock->expects($this->any())->method('getUrlDecoder') + ->willReturn($this->urlDecoderMock); + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->helper = $this->objectManagerHelper->getObject( + HelperData::class, + [ + 'context' => $this->contextMock + ] + ); + } + + /** + * test decodeTrackingHash() with data provider below + * + * @param string $hash + * @param string $urlDecodeResult + * @param array $expected + * @dataProvider decodeTrackingHashDataProvider + */ + public function testDecodeTrackingHash($hash, $urlDecodeResult, $expected) + { + $this->urlDecoderMock->expects($this->any())->method('decode') + ->with($hash) + ->willReturn($urlDecodeResult); + $this->assertEquals($expected, $this->helper->decodeTrackingHash($hash)); + } + + /** + * Dataset to test getData() + * + * @return array + */ + public function decodeTrackingHashDataProvider() + { + return [ + 'Test with hash key is allowed' => [ + strtr(base64_encode('order_id:1:protected_code'), '+/=', '-_,'), + 'order_id:1:protected_code', + [ + 'key' => 'order_id', + 'id' => 1, + 'hash' => 'protected_code' + ] + ], + 'Test with hash key is not allowed' => [ + strtr(base64_encode('invoice_id:1:protected_code'), '+/=', '-_,'), + 'invoice_id:1:protected_code', + [] + ] + ]; + } +} diff --git a/app/code/Magento/Shipping/Test/Unit/Model/Config/Source/AllmethodsTest.php b/app/code/Magento/Shipping/Test/Unit/Model/Config/Source/AllmethodsTest.php new file mode 100644 index 0000000000000..985cc0e53bad5 --- /dev/null +++ b/app/code/Magento/Shipping/Test/Unit/Model/Config/Source/AllmethodsTest.php @@ -0,0 +1,106 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Shipping\Test\Unit\Model\Config\Source; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Shipping\Model\Carrier\AbstractCarrierInterface; +use Magento\Shipping\Model\Config; +use Magento\Shipping\Model\Config\Source\Allmethods; +use PHPUnit\Framework\MockObject\MockObject; + +/** + * Tests for Allmethods Class + */ +class AllmethodsTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ScopeConfigInterface|MockObject $scopeConfig + */ + private $scopeConfig; + + /** + * @var Config|MockObject $shippingConfig + */ + private $shippingConfig; + + /** + * @var Allmethods $allmethods + */ + private $allmethods; + + /** + * @var MockObject + */ + private $carriersMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->scopeConfig = $this->createMock(ScopeConfigInterface::class); + $this->shippingConfig = $this->createMock(Config::class); + $this->carriersMock = $this->getMockBuilder(AbstractCarrierInterface::class) + ->setMethods( + [ + 'isActive', + 'getAllowedMethods' + ] + )->getMockForAbstractClass(); + + $this->allmethods = new Allmethods( + $this->scopeConfig, + $this->shippingConfig + ); + } + + /** + * Ensure that options converted correctly + * + * @dataProvider getCarriersMethodsProvider + * @param array $expectedArray + * @return void + */ + public function testToOptionArray(array $expectedArray): void + { + $expectedArray['getAllCarriers'] = [$this->carriersMock]; + + $this->shippingConfig->expects($this->once()) + ->method('getAllCarriers') + ->willReturn($expectedArray['getAllCarriers']); + $this->carriersMock->expects($this->once()) + ->method('isActive') + ->willReturn(true); + $this->carriersMock->expects($this->once()) + ->method('getAllowedMethods') + ->willReturn($expectedArray['allowedMethods']); + $this->assertEquals([$expectedArray['expected_result']], $this->allmethods->toOptionArray()); + } + + /** + * Returns providers data for test + * + * @return array + */ + public function getCarriersMethodsProvider(): array + { + return [ + [ + [ + 'allowedMethods' => [null => 'method_title'], + 'expected_result' => [ 'value' => [], 'label' => null], + 'getAllCarriers' => [] + ], + [ + 'allowedMethods' => ['method_code' => 'method_title'], + 'expected_result' => [ 'value' => [], 'label' => 'method_code'], + 'getAllCarriers' => [] + ] + + ] + ]; + } +} diff --git a/app/code/Magento/Shipping/registration.php b/app/code/Magento/Shipping/registration.php index 10a1bc529a534..9652f5a61a3bc 100644 --- a/app/code/Magento/Shipping/registration.php +++ b/app/code/Magento/Shipping/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Shipping', __DIR__); diff --git a/app/code/Magento/Signifyd/Test/Mftf/Page/AdminFraudProtectionPage.xml b/app/code/Magento/Signifyd/Test/Mftf/Page/AdminFraudProtectionPage.xml new file mode 100644 index 0000000000000..07b58b8594843 --- /dev/null +++ b/app/code/Magento/Signifyd/Test/Mftf/Page/AdminFraudProtectionPage.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="AdminFraudProtectionPage" url="admin/system_config/edit/section/fraud_protection/" area="admin" module="Magento_Signifyd"> + <section name="AdminSignifydConfigurationSection"/> + </page> +</pages> diff --git a/app/code/Magento/Signifyd/Test/Mftf/Section/AdminSignifydConfigurationSection.xml b/app/code/Magento/Signifyd/Test/Mftf/Section/AdminSignifydConfigurationSection.xml new file mode 100644 index 0000000000000..618e9d520dd87 --- /dev/null +++ b/app/code/Magento/Signifyd/Test/Mftf/Section/AdminSignifydConfigurationSection.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="AdminSignifydConfigurationSection"> + <element name="head" type="button" selector="#fraud_protection_signifyd_config-head"/> + <element name="enabled" type="input" selector="#fraud_protection_signifyd_config_active"/> + <element name="url" type="text" selector="#fraud_protection_signifyd_config_api_url"/> + </section> +</sections> diff --git a/app/code/Magento/Signifyd/Test/Mftf/Test/AdminSignifydConfigDependentOnActiveFieldTest.xml b/app/code/Magento/Signifyd/Test/Mftf/Test/AdminSignifydConfigDependentOnActiveFieldTest.xml new file mode 100644 index 0000000000000..dcae0c4091ba6 --- /dev/null +++ b/app/code/Magento/Signifyd/Test/Mftf/Test/AdminSignifydConfigDependentOnActiveFieldTest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminSignifydConfigDependentOnActiveFieldTest"> + <annotations> + <features value="Signifyd"/> + <title value="Signifyd config dependent on active field" /> + <description value="Signifyd system configs dependent by Enable this Solution field."/> + <severity value="MINOR"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <magentoCLI command="config:set fraud_protection/signifyd/active 1" stepKey="enableSignifyd"/> + </before> + + <after> + <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI command="config:set fraud_protection/signifyd/active 0" stepKey="disableSignifyd"/> + </after> + + <amOnPage url="{{AdminFraudProtectionPage.url}}" stepKey="openFraudProtectionPagePage" /> + <conditionalClick dependentSelector="{{AdminSignifydConfigurationSection.enabled}}" visible="false" selector="{{AdminSignifydConfigurationSection.head}}" stepKey="openCollapsibleBlock"/> + <seeInField selector="{{AdminSignifydConfigurationSection.url}}" userInput="https://api.signifyd.com/v2/" stepKey="seeApiUrlField"/> + <selectOption selector="{{AdminSignifydConfigurationSection.enabled}}" userInput="0" stepKey="disableSignifydOption"/> + <dontSeeElement selector="{{AdminSignifydConfigurationSection.url}}" stepKey="dontSeeApiUrlField"/> + </test> +</tests> diff --git a/app/code/Magento/Signifyd/etc/adminhtml/system.xml b/app/code/Magento/Signifyd/etc/adminhtml/system.xml index 2dd75d2d91e5b..272ce78aec2e5 100644 --- a/app/code/Magento/Signifyd/etc/adminhtml/system.xml +++ b/app/code/Magento/Signifyd/etc/adminhtml/system.xml @@ -38,25 +38,37 @@ </field> <field id="api_key" translate="label" type="obscure" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0"> <label>API Key</label> - <comment><![CDATA[Your API key can be found on the <a href="http://signifyd.com/settings" target="_blank">settings page</a> in the Signifyd console]]></comment> + <comment><![CDATA[Your API key can be found on the <a href="http://signifyd.com/settings" target="_blank">settings page</a> in the Signifyd console.]]></comment> <config_path>fraud_protection/signifyd/api_key</config_path> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> + <depends> + <field id="active">1</field> + </depends> </field> <field id="api_url" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> <label>API URL</label> <config_path>fraud_protection/signifyd/api_url</config_path> <comment>Don’t change unless asked to do so.</comment> + <depends> + <field id="active">1</field> + </depends> </field> <field id="debug" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Debug</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <config_path>fraud_protection/signifyd/debug</config_path> + <depends> + <field id="active">1</field> + </depends> </field> <field id="webhook_url" translate="label comment" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Webhook URL</label> <comment><![CDATA[Your webhook URL will be used to <a href="https://app.signifyd.com/settings/notifications" target="_blank">configure</a> a guarantee completed webhook in Signifyd. Webhooks are used to sync Signifyd`s guarantee decisions back to Magento.]]></comment> <attribute type="handler_url">signifyd/webhooks/handler</attribute> <frontend_model>Magento\Signifyd\Block\Adminhtml\System\Config\Field\WebhookUrl</frontend_model> + <depends> + <field id="active">1</field> + </depends> </field> </group> </group> diff --git a/app/code/Magento/Signifyd/registration.php b/app/code/Magento/Signifyd/registration.php index 72b11f7eac214..e7fa9cfd2f9b3 100644 --- a/app/code/Magento/Signifyd/registration.php +++ b/app/code/Magento/Signifyd/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Signifyd', __DIR__); diff --git a/app/code/Magento/Sitemap/Model/Sitemap.php b/app/code/Magento/Sitemap/Model/Sitemap.php index 0d69634ccfa5e..ea5659cf909ff 100644 --- a/app/code/Magento/Sitemap/Model/Sitemap.php +++ b/app/code/Magento/Sitemap/Model/Sitemap.php @@ -10,6 +10,7 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\DataObject; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Filesystem; use Magento\Framework\UrlInterface; use Magento\Robots\Model\Config\Value; use Magento\Sitemap\Model\ItemProvider\ItemProviderInterface; @@ -191,6 +192,16 @@ class Sitemap extends \Magento\Framework\Model\AbstractModel implements \Magento */ private $lastModMinTsVal; + /** + * @var Filesystem + */ + private $filesystem; + + /** + * @var DocumentRoot + */ + private $documentRoot; + /** * Initialize dependencies. * @@ -238,8 +249,9 @@ public function __construct( ) { $this->_escaper = $escaper; $this->_sitemapData = $sitemapData; - $documentRoot = $documentRoot ?: ObjectManager::getInstance()->get(DocumentRoot::class); - $this->_directory = $filesystem->getDirectoryWrite($documentRoot->getPath()); + $this->documentRoot = $documentRoot ?: ObjectManager::getInstance()->get(DocumentRoot::class); + $this->filesystem = $filesystem; + $this->_directory = $filesystem->getDirectoryWrite($this->documentRoot->getPath()); $this->_categoryFactory = $categoryFactory; $this->_productFactory = $productFactory; $this->_cmsFactory = $cmsFactory; @@ -727,6 +739,9 @@ protected function _getFormattedLastmodDate($date) */ protected function _getDocumentRoot() { + if (PHP_SAPI === 'cli') { + return $this->getDocumentRootFromBaseDir() ?? ''; + } // phpcs:ignore Magento2.Functions.DiscouragedFunction return realpath($this->_request->getServer('DOCUMENT_ROOT')); } @@ -742,10 +757,14 @@ protected function _getStoreBaseDomain() $storeParsedUrl = parse_url($this->_getStoreBaseUrl()); $url = $storeParsedUrl['scheme'] . '://' . $storeParsedUrl['host']; - $documentRoot = trim(str_replace('\\', '/', $this->_getDocumentRoot()), '/'); - $baseDir = trim(str_replace('\\', '/', $this->_getBaseDir()), '/'); + // Set document root to false if we were unable to get it + $documentRoot = $this->_getDocumentRoot() ?: false; + if ($documentRoot) { + $documentRoot = trim(str_replace(DIRECTORY_SEPARATOR, '/', $documentRoot), '/'); + } + $baseDir = trim(str_replace(DIRECTORY_SEPARATOR, '/', $this->_getBaseDir()), '/'); - if (strpos($baseDir, (string) $documentRoot) === 0) { + if ($documentRoot !== false && strpos($baseDir, (string) $documentRoot) === 0) { //case when basedir is in document root $installationFolder = trim(str_replace($documentRoot, '', $baseDir), '/'); $storeDomain = rtrim($url . '/' . $installationFolder, '/'); @@ -866,4 +885,30 @@ public function getIdentities() Value::CACHE_TAG . '_' . $this->getStoreId(), ]; } + + /** + * Get document root using base directory (root directory) and base path (base url path) + * + * Document root is determined using formula: BaseDir = DocumentRoot + BasePath. + * Returns <b>NULL</b> if BaseDir does not end with BasePath (e.g document root contains a symlink to BaseDir). + * + * @return string|null + */ + private function getDocumentRootFromBaseDir(): ?string + { + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $basePath = rtrim(parse_url($this->_getStoreBaseUrl(UrlInterface::URL_TYPE_WEB), PHP_URL_PATH) ?: '', '/'); + $basePath = str_replace('/', DIRECTORY_SEPARATOR, $basePath); + $basePath = rtrim($basePath, DIRECTORY_SEPARATOR); + $baseDir = rtrim($this->_getBaseDir(), DIRECTORY_SEPARATOR); + $length = strlen($basePath); + if (!$length) { + $documentRoot = $baseDir; + } elseif (substr($baseDir, -$length) === $basePath) { + $documentRoot = rtrim(substr($baseDir, 0, strlen($baseDir) - $length), DIRECTORY_SEPARATOR); + } else { + $documentRoot = null; + } + return $documentRoot; + } } diff --git a/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingSiteDeleteByNameActionGroup.xml b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingSiteDeleteByNameActionGroup.xml new file mode 100644 index 0000000000000..16bf43da2e690 --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingSiteDeleteByNameActionGroup.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="AdminMarketingSiteDeleteByNameActionGroup"> + <annotations> + <description>Go to the Site map page. Delete a site map based on the provided Name.</description> + </annotations> + <arguments> + <argument name="filename" type="string"/> + </arguments> + + <amOnPage url="{{AdminMarketingSiteMapGridPage.url}}" stepKey="amOnSiteMapGridPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <click selector="{{AdminMarketingSiteMapGridSection.resetButton}}" stepKey="resetSearchFilter"/> + <fillField selector="{{AdminMarketingSiteMapGridSection.fileNameTextField}}" userInput="{{filename}}" stepKey="fillFileNameField"/> + <click selector="{{AdminMarketingSiteMapGridSection.searchButton}}" stepKey="clickSearchButton"/> + <see userInput="{{filename}}" selector="{{AdminMarketingSiteMapGridSection.firstSearchResult}}" stepKey="verifyThatCorrectStoreGroupFound"/> + <click selector="{{AdminMarketingSiteMapGridSection.firstSearchResult}}" stepKey="clickEditExistingRow"/> + <waitForPageLoad stepKey="waitForSiteMapToLoad"/> + <click selector="{{AdminMarketingSiteMapEditActionSection.delete}}" stepKey="deleteSiteMap"/> + <waitForAjaxLoad stepKey="waitForAjaxLoad"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> + <waitForPageLoad stepKey="waitForDeleteLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingSiteMapFillFormActionGroup.xml b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingSiteMapFillFormActionGroup.xml new file mode 100644 index 0000000000000..06e992736bf06 --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingSiteMapFillFormActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminMarketingSiteMapFillFormActionGroup"> + <annotations> + <description>Fill data to Site map form</description> + </annotations> + <arguments> + <argument name="sitemap" type="entity" defaultValue="DefaultSiteMap"/> + </arguments> + <fillField selector="{{AdminMarketingSiteMapEditActionSection.filename}}" userInput="{{sitemap.filename}}" stepKey="fillFilename"/> + <fillField selector="{{AdminMarketingSiteMapEditActionSection.path}}" userInput="{{sitemap.path}}" stepKey="fillPath"/> + <click selector="{{AdminMarketingSiteMapEditActionSection.save}}" stepKey="saveSiteMap"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingSiteMapNavigateNewActionGroup.xml b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingSiteMapNavigateNewActionGroup.xml new file mode 100644 index 0000000000000..78cfeab66f1c4 --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AdminMarketingSiteMapNavigateNewActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminMarketingSiteMapNavigateNewActionGroup"> + <annotations> + <description>Navigate to New Site Map</description> + </annotations> + <amOnPage url="{{AdminMarketingSiteMapNewPage.url}}" stepKey="openNewSiteMapPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertSiteMapCreateSuccessActionGroup.xml b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertSiteMapCreateSuccessActionGroup.xml new file mode 100644 index 0000000000000..77f26063e8034 --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertSiteMapCreateSuccessActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertSiteMapCreateSuccessActionGroup"> + <annotations> + <description>Validate the success message after creating site map.</description> + </annotations> + + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the sitemap." stepKey="seeSuccess"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertSiteMapDeleteSuccessActionGroup.xml b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertSiteMapDeleteSuccessActionGroup.xml new file mode 100644 index 0000000000000..15df8aa2f25f1 --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/ActionGroup/AssertSiteMapDeleteSuccessActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertSiteMapDeleteSuccessActionGroup"> + <annotations> + <description>Validate the success message after delete site map.</description> + </annotations> + + <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the sitemap." stepKey="seeSuccess"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Data/SitemapData.xml b/app/code/Magento/Sitemap/Test/Mftf/Data/SitemapData.xml new file mode 100644 index 0000000000000..0b5d5d3dcdefe --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/Data/SitemapData.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="DefaultSiteMap"> + <data key="filename">sitemap.xml</data> + <data key="path">/</data> + </entity> +</entities> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Page/AdminMarketingSiteMapGridPage.xml b/app/code/Magento/Sitemap/Test/Mftf/Page/AdminMarketingSiteMapGridPage.xml new file mode 100644 index 0000000000000..b15a16bf134ad --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/Page/AdminMarketingSiteMapGridPage.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="AdminMarketingSiteMapGridPage" url="admin/sitemap/" area="admin" module="Sitemap"> + <section name="AdminMarketingSiteMapGridSection"/> + </page> +</pages> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Page/AdminMarketingSiteMapNewPage.xml b/app/code/Magento/Sitemap/Test/Mftf/Page/AdminMarketingSiteMapNewPage.xml new file mode 100644 index 0000000000000..5450ece5bb3c2 --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/Page/AdminMarketingSiteMapNewPage.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="AdminMarketingSiteMapNewPage" url="admin/sitemap/new/" area="admin" module="Sitemap"> + <section name="AdminMarketingSiteMapEditActionSection"/> + </page> +</pages> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Section/AdminMarketingSiteMapEditActionSection.xml b/app/code/Magento/Sitemap/Test/Mftf/Section/AdminMarketingSiteMapEditActionSection.xml new file mode 100644 index 0000000000000..841071350526a --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/Section/AdminMarketingSiteMapEditActionSection.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminMarketingSiteMapEditActionSection"> + <element name="save" type="button" selector="#save" timeout="10"/> + <element name="delete" type="button" selector="#delete" timeout="10"/> + <element name="saveAndGenerate" type="button" selector="#generate" timeout="10"/> + <element name="reset" type="button" selector="#reset"/> + <element name="back" type="button" selector="#back"/> + <element name="filename" type="input" selector="input[name='sitemap_filename']"/> + <element name="path" type="input" selector="input[name='sitemap_path']"/> + </section> +</sections> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Section/AdminMarketingSiteMapGridSection.xml b/app/code/Magento/Sitemap/Test/Mftf/Section/AdminMarketingSiteMapGridSection.xml new file mode 100644 index 0000000000000..50c96ae6748ce --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/Section/AdminMarketingSiteMapGridSection.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="AdminMarketingSiteMapGridSection"> + <element name="resetButton" type="button" selector="button[title='Reset Filter']"/> + <element name="searchButton" type="button" selector=".admin__filter-actions [title='Search']"/> + <element name="firstSearchResult" type="text" selector="#sitemapGrid_table>tbody>tr:nth-child(1)"/> + <element name="fileNameTextField" type="input" selector="#sitemapGrid_filter_sitemap_filename" timeout="90"/> + </section> +</sections> diff --git a/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingSiteMapCreateNewTest.xml b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingSiteMapCreateNewTest.xml new file mode 100644 index 0000000000000..57d8f8c75d23d --- /dev/null +++ b/app/code/Magento/Sitemap/Test/Mftf/Test/AdminMarketingSiteMapCreateNewTest.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminMarketingSiteMapCreateNewTest"> + <annotations> + <features value="Sitemap"/> + <stories value="Create Site Map"/> + <title value="Create New Site Map with valid data"/> + <description value="Create New Site Map with valid data"/> + <severity value="CRITICAL"/> + <group value="sitemap"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminMarketingSiteDeleteByNameActionGroup" stepKey="deleteSiteMap"> + <argument name="filename" value="{{DefaultSiteMap.filename}}" /> + </actionGroup> + <actionGroup ref="AssertSiteMapDeleteSuccessActionGroup" stepKey="assertDeleteSuccessMessage"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <actionGroup ref="AdminMarketingSiteMapNavigateNewActionGroup" stepKey="navigateNewSiteMap"/> + <actionGroup ref="AdminMarketingSiteMapFillFormActionGroup" stepKey="fillSiteMapForm"> + <argument name="sitemap" value="DefaultSiteMap" /> + </actionGroup> + <actionGroup ref="AssertSiteMapCreateSuccessActionGroup" stepKey="seeSuccessMessage"/> + </test> +</tests> diff --git a/app/code/Magento/Sitemap/Test/Unit/Model/SitemapTest.php b/app/code/Magento/Sitemap/Test/Unit/Model/SitemapTest.php index a5e40bf3fcff7..16d506c1cdfa3 100644 --- a/app/code/Magento/Sitemap/Test/Unit/Model/SitemapTest.php +++ b/app/code/Magento/Sitemap/Test/Unit/Model/SitemapTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Sitemap\Test\Unit\Model; +use Magento\Framework\App\Request\Http; use Magento\Framework\DataObject; use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\Write as DirectoryWrite; @@ -86,6 +87,15 @@ class SitemapTest extends \PHPUnit\Framework\TestCase */ private $configReaderMock; + /** + * @var Http|\PHPUnit_Framework_MockObject_MockObject + */ + private $request; + /** + * @var Store|\PHPUnit_Framework_MockObject_MockObject + */ + private $store; + /** * @inheritdoc */ @@ -143,6 +153,12 @@ protected function setUp() $this->configReaderMock = $this->getMockForAbstractClass(SitemapConfigReaderInterface::class); $this->itemProviderMock = $this->getMockForAbstractClass(ItemProviderInterface::class); + $this->request = $this->createMock(Http::class); + $this->store = $this->createPartialMock(Store::class, ['isFrontUrlSecure', 'getBaseUrl']); + $this->storeManagerMock = $this->createMock(StoreManagerInterface::class); + $this->storeManagerMock->expects($this->any()) + ->method('getStore') + ->willReturn($this->store); } /** @@ -476,25 +492,15 @@ function ($from, $to) { $model = $this->getModelMock(true); - $storeMock = $this->getMockBuilder(Store::class) - ->setMethods(['isFrontUrlSecure', 'getBaseUrl']) - ->disableOriginalConstructor() - ->getMock(); - - $storeMock->expects($this->atLeastOnce()) + $this->store->expects($this->atLeastOnce()) ->method('isFrontUrlSecure') ->willReturn(false); - $storeMock->expects($this->atLeastOnce()) + $this->store->expects($this->atLeastOnce()) ->method('getBaseUrl') ->with($this->isType('string'), false) ->willReturn('http://store.com/'); - $this->storeManagerMock->expects($this->atLeastOnce()) - ->method('getStore') - ->with(1) - ->willReturn($storeMock); - return $model; } @@ -599,10 +605,6 @@ private function getModelConstructorArgs() ->disableOriginalConstructor() ->getMock(); - $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class) - ->setMethods(['getStore']) - ->getMockForAbstractClass(); - $objectManager = new ObjectManager($this); $escaper = $objectManager->getObject(\Magento\Framework\Escaper::class); @@ -617,7 +619,8 @@ private function getModelConstructorArgs() 'filesystem' => $this->filesystemMock, 'itemProvider' => $this->itemProviderMock, 'configReader' => $this->configReaderMock, - 'escaper' => $escaper + 'escaper' => $escaper, + 'request' => $this->request, ] ); $constructArguments['resource'] = null; @@ -732,4 +735,62 @@ public static function siteUrlDataProvider() ] ]; } + + /** + * Check site URL getter + * + * @param string $storeBaseUrl + * @param string $baseDir + * @param string $documentRoot + * @dataProvider getDocumentRootFromBaseDirUrlDataProvider + */ + public function testGetDocumentRootFromBaseDir( + string $storeBaseUrl, + string $baseDir, + ?string $documentRoot + ) { + $this->store->setCode('store'); + $this->store->method('getBaseUrl')->willReturn($storeBaseUrl); + $this->directoryMock->method('getAbsolutePath')->willReturn($baseDir); + /** @var $model Sitemap */ + $model = $this->getMockBuilder(Sitemap::class) + ->setMethods(['_construct']) + ->setConstructorArgs($this->getModelConstructorArgs()) + ->getMock(); + + $method = new \ReflectionMethod($model, 'getDocumentRootFromBaseDir'); + $method->setAccessible(true); + $this->assertSame($documentRoot, $method->invoke($model)); + } + + /** + * Provides test cases for document root testing + * + * @return array + */ + public function getDocumentRootFromBaseDirUrlDataProvider(): array + { + return [ + [ + 'http://magento.com/', + '/var/www', + '/var/www', + ], + [ + 'http://magento.com/usa', + '/var/www/usa', + '/var/www', + ], + [ + 'http://magento.com/usa/tx', + '/var/www/usa/tx', + '/var/www', + ], + 'symlink <document root>/usa/txt -> /var/www/html' => [ + 'http://magento.com/usa/tx', + '/var/www/html', + null, + ], + ]; + } } diff --git a/app/code/Magento/Sitemap/registration.php b/app/code/Magento/Sitemap/registration.php index 9fc965c854779..b713ddb98f381 100644 --- a/app/code/Magento/Sitemap/registration.php +++ b/app/code/Magento/Sitemap/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Sitemap', __DIR__); diff --git a/app/code/Magento/Store/Controller/Store/Redirect.php b/app/code/Magento/Store/Controller/Store/Redirect.php index 21692e9d6dd1e..5d61275e72a28 100644 --- a/app/code/Magento/Store/Controller/Store/Redirect.php +++ b/app/code/Magento/Store/Controller/Store/Redirect.php @@ -7,19 +7,25 @@ namespace Magento\Store\Controller\Store; +use Magento\Framework\App\Action\Action; use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\ResultInterface; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Session\Generic as Session; +use Magento\Framework\Session\SidResolverInterface; use Magento\Store\Api\StoreRepositoryInterface; use Magento\Store\Api\StoreResolverInterface; +use Magento\Store\Model\Store; use Magento\Store\Model\StoreResolver; -use Magento\Framework\Session\SidResolverInterface; -use Magento\Framework\Session\Generic as Session; +use Magento\Store\Model\StoreSwitcher\HashGenerator; /** * Builds correct url to target store and performs redirect. */ -class Redirect extends \Magento\Framework\App\Action\Action +class Redirect extends Action implements HttpGetActionInterface, HttpPostActionInterface { /** * @var StoreRepositoryInterface @@ -41,34 +47,44 @@ class Redirect extends \Magento\Framework\App\Action\Action */ private $session; + /** + * @var HashGenerator + */ + private $hashGenerator; + /** * @param Context $context * @param StoreRepositoryInterface $storeRepository * @param StoreResolverInterface $storeResolver * @param Session $session * @param SidResolverInterface $sidResolver + * @param HashGenerator $hashGenerator */ public function __construct( Context $context, StoreRepositoryInterface $storeRepository, StoreResolverInterface $storeResolver, Session $session, - SidResolverInterface $sidResolver + SidResolverInterface $sidResolver, + HashGenerator $hashGenerator ) { parent::__construct($context); $this->storeRepository = $storeRepository; $this->storeResolver = $storeResolver; $this->session = $session; $this->sidResolver = $sidResolver; + $this->hashGenerator = $hashGenerator; } /** - * @return ResponseInterface|\Magento\Framework\Controller\ResultInterface + * Performs store redirect + * + * @return ResponseInterface|ResultInterface * @throws NoSuchEntityException */ public function execute() { - /** @var \Magento\Store\Model\Store $currentStore */ + /** @var Store $currentStore */ $currentStore = $this->storeRepository->getById($this->storeResolver->getCurrentStoreId()); $targetStoreCode = $this->_request->getParam(StoreResolver::PARAM_NAME); $fromStoreCode = $this->_request->getParam('___from_store'); @@ -79,7 +95,7 @@ public function execute() } try { - /** @var \Magento\Store\Model\Store $targetStore */ + /** @var Store $fromStore */ $fromStore = $this->storeRepository->get($fromStoreCode); } catch (NoSuchEntityException $e) { $error = __('Requested store is not found'); @@ -103,11 +119,16 @@ public function execute() $query[$sidName] = $this->session->getSessionId(); } + $customerHash = $this->hashGenerator->generateHash($fromStore); + $query = array_merge($query, $customerHash); + $arguments = [ '_nosid' => true, '_query' => $query ]; $this->_redirect->redirect($this->_response, 'stores/store/switch', $arguments); } + + return null; } } diff --git a/app/code/Magento/Store/Controller/Store/SwitchAction.php b/app/code/Magento/Store/Controller/Store/SwitchAction.php index d8ac1b308d7ed..41acb1605ec7c 100644 --- a/app/code/Magento/Store/Controller/Store/SwitchAction.php +++ b/app/code/Magento/Store/Controller/Store/SwitchAction.php @@ -11,7 +11,6 @@ use Magento\Framework\App\Action\Action; use Magento\Framework\App\Action\Context as ActionContext; use Magento\Framework\App\Http\Context as HttpContext; -use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Store\Api\StoreCookieManagerInterface; use Magento\Store\Api\StoreRepositoryInterface; @@ -21,6 +20,7 @@ use Magento\Store\Model\StoreSwitcherInterface; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Store\Controller\Store\SwitchAction\CookieManager; /** * Handles store switching url and makes redirect. @@ -56,6 +56,11 @@ class SwitchAction extends Action implements HttpGetActionInterface, HttpPostAct */ private $storeSwitcher; + /** + * @var CookieManager + */ + private $cookieManager; + /** * Initialize dependencies. * @@ -65,6 +70,7 @@ class SwitchAction extends Action implements HttpGetActionInterface, HttpPostAct * @param StoreRepositoryInterface $storeRepository * @param StoreManagerInterface $storeManager * @param StoreSwitcherInterface $storeSwitcher + * @param CookieManager $cookieManager */ public function __construct( ActionContext $context, @@ -72,7 +78,8 @@ public function __construct( HttpContext $httpContext, StoreRepositoryInterface $storeRepository, StoreManagerInterface $storeManager, - StoreSwitcherInterface $storeSwitcher = null + StoreSwitcherInterface $storeSwitcher, + CookieManager $cookieManager ) { parent::__construct($context); $this->storeCookieManager = $storeCookieManager; @@ -80,7 +87,8 @@ public function __construct( $this->storeRepository = $storeRepository; $this->storeManager = $storeManager; $this->messageManager = $context->getMessageManager(); - $this->storeSwitcher = $storeSwitcher ?: ObjectManager::getInstance()->get(StoreSwitcherInterface::class); + $this->storeSwitcher = $storeSwitcher; + $this->cookieManager = $cookieManager; } /** @@ -88,12 +96,13 @@ public function __construct( * * @return void * @throws StoreSwitcher\CannotSwitchStoreException + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Stdlib\Cookie\CookieSizeLimitReachedException + * @throws \Magento\Framework\Stdlib\Cookie\FailureToSendException */ public function execute() { - $targetStoreCode = $this->_request->getParam( - \Magento\Store\Model\StoreManagerInterface::PARAM_NAME - ); + $targetStoreCode = $this->_request->getParam(StoreManagerInterface::PARAM_NAME); $fromStoreCode = $this->_request->getParam( '___from_store', $this->storeCookieManager->getStoreCodeFromCookie() @@ -115,6 +124,7 @@ public function execute() $this->messageManager->addErrorMessage($error); } else { $redirectUrl = $this->storeSwitcher->switch($fromStore, $targetStore, $requestedUrlToRedirect); + $this->cookieManager->setCookieForStore($targetStore); } $this->getResponse()->setRedirect($redirectUrl); diff --git a/app/code/Magento/Store/Controller/Store/SwitchAction/CookieManager.php b/app/code/Magento/Store/Controller/Store/SwitchAction/CookieManager.php new file mode 100644 index 0000000000000..182ae35b0ff61 --- /dev/null +++ b/app/code/Magento/Store/Controller/Store/SwitchAction/CookieManager.php @@ -0,0 +1,65 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Store\Controller\Store\SwitchAction; + +use Magento\Framework\Stdlib\Cookie\CookieMetadataFactory; +use Magento\Framework\Stdlib\CookieManagerInterface; +use Magento\Store\Api\Data\StoreInterface; + +/** + * Handles store switching cookie for the frontend storage clean + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) + */ +class CookieManager +{ + /** + * @var string + */ + const COOKIE_NAME = 'section_data_clean'; + + /** + * @var CookieMetadataFactory + */ + private $cookieMetadataFactory; + + /** + * @var CookieManagerInterface + */ + private $cookieManager; + + /** + * @param CookieMetadataFactory $cookieMetadataFactory + * @param CookieManagerInterface $cookieManager + */ + public function __construct( + CookieMetadataFactory $cookieMetadataFactory, + CookieManagerInterface $cookieManager + ) { + $this->cookieMetadataFactory = $cookieMetadataFactory; + $this->cookieManager = $cookieManager; + } + + /** + * Set cookie for store + * + * @param StoreInterface $targetStore + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Stdlib\Cookie\CookieSizeLimitReachedException + * @throws \Magento\Framework\Stdlib\Cookie\FailureToSendException + */ + public function setCookieForStore(StoreInterface $targetStore) + { + $cookieMetadata = $this->cookieMetadataFactory->createPublicCookieMetadata() + ->setHttpOnly(false) + ->setDuration(15) + ->setPath($targetStore->getStorePath()); + $this->cookieManager->setPublicCookie(self::COOKIE_NAME, $targetStore->getCode(), $cookieMetadata); + } +} diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php index faa26b24a5505..5eda6f4a9b57d 100644 --- a/app/code/Magento/Store/Model/Store.php +++ b/app/code/Magento/Store/Model/Store.php @@ -423,9 +423,6 @@ public function __construct( /** * @inheritdoc - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __sleep() { @@ -438,9 +435,6 @@ public function __sleep() * Init not serializable fields * * @return void - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __wakeup() { diff --git a/app/code/Magento/Store/Model/StoreCookieManager.php b/app/code/Magento/Store/Model/StoreCookieManager.php index d5194379c69d8..d94357caf785c 100644 --- a/app/code/Magento/Store/Model/StoreCookieManager.php +++ b/app/code/Magento/Store/Model/StoreCookieManager.php @@ -1,6 +1,5 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -11,10 +10,13 @@ use Magento\Store\Api\Data\StoreInterface; use Magento\Store\Api\StoreCookieManagerInterface; +/** + * DTO class to work with cookies. + */ class StoreCookieManager implements StoreCookieManagerInterface { /** - * Cookie name + * @var string */ const COOKIE_NAME = 'store'; @@ -41,7 +43,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function getStoreCodeFromCookie() { @@ -49,12 +51,12 @@ public function getStoreCodeFromCookie() } /** - * {@inheritdoc} + * @inheritdoc */ public function setStoreCookie(StoreInterface $store) { $cookieMetadata = $this->cookieMetadataFactory->createPublicCookieMetadata() - ->setHttpOnly(true) + ->setHttpOnly(false) ->setDurationOneYear() ->setPath($store->getStorePath()); @@ -62,7 +64,7 @@ public function setStoreCookie(StoreInterface $store) } /** - * {@inheritdoc} + * @inheritdoc */ public function deleteStoreCookie(StoreInterface $store) { diff --git a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php index 456941bd41c25..d1858939434b7 100644 --- a/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php +++ b/app/code/Magento/Store/Model/StoreSwitcher/HashGenerator.php @@ -7,19 +7,18 @@ namespace Magento\Store\Model\StoreSwitcher; +use Magento\Authorization\Model\UserContextInterface; +use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\DeploymentConfig as DeploymentConfig; +use Magento\Framework\Config\ConfigOptionsListConstants; +use Magento\Framework\Url\Helper\Data as UrlHelper; use Magento\Store\Api\Data\StoreInterface; use Magento\Store\Model\StoreSwitcher\HashGenerator\HashData; -use Magento\Store\Model\StoreSwitcherInterface; -use \Magento\Framework\App\DeploymentConfig as DeploymentConfig; -use Magento\Framework\Url\Helper\Data as UrlHelper; -use Magento\Framework\Config\ConfigOptionsListConstants; -use Magento\Authorization\Model\UserContextInterface; -use \Magento\Framework\App\ActionInterface; /** * Generate one time token and build redirect url */ -class HashGenerator implements StoreSwitcherInterface +class HashGenerator { /** * @var \Magento\Framework\App\DeploymentConfig @@ -52,48 +51,40 @@ public function __construct( } /** - * Builds redirect url with token + * Generate hash data for customer * - * @param StoreInterface $fromStore store where we came from - * @param StoreInterface $targetStore store where to go to - * @param string $redirectUrl original url requested for redirect after switching - * @return string redirect url - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @param StoreInterface $fromStore + * @return array */ - public function switch(StoreInterface $fromStore, StoreInterface $targetStore, string $redirectUrl): string + public function generateHash(StoreInterface $fromStore): array { - $targetUrl = $redirectUrl; + $key = (string)$this->deploymentConfig->get(ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY); + $timeStamp = time(); + $customerId = null; - $encodedUrl = $this->urlHelper->getEncodedUrl($redirectUrl); + $result = []; if ($this->currentUser->getUserType() == UserContextInterface::USER_TYPE_CUSTOMER) { $customerId = $this->currentUser->getUserId(); - } - if ($customerId) { - // phpcs:ignore - $urlParts = parse_url($targetUrl); - $host = $urlParts['host']; - $scheme = $urlParts['scheme']; - $key = (string)$this->deploymentConfig->get(ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY); - $timeStamp = time(); - $fromStoreCode = $fromStore->getCode(); - $data = implode(',', [$customerId, $timeStamp, $fromStoreCode]); - $signature = hash_hmac('sha256', $data, $key); - $targetUrl = $scheme . "://" . $host . '/stores/store/switchrequest'; - $targetUrl = $this->urlHelper->addRequestParam( - $targetUrl, - ['customer_id' => $customerId] - ); - $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['time_stamp' => $timeStamp]); - $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['signature' => $signature]); - $targetUrl = $this->urlHelper->addRequestParam($targetUrl, ['___from_store' => $fromStoreCode]); - $targetUrl = $this->urlHelper->addRequestParam( - $targetUrl, - [ActionInterface::PARAM_NAME_URL_ENCODED => $encodedUrl] + $data = implode( + ',', + [ + $customerId, + $timeStamp, + $fromStore->getCode() + ] ); + $signature = hash_hmac('sha256', $data, $key); + + $result = [ + 'customer_id' => $customerId, + 'time_stamp' => $timeStamp, + 'signature' => $signature + ]; } - return $targetUrl; + + return $result; } /** diff --git a/app/code/Magento/Store/Model/StoreSwitcher/HashProcessor.php b/app/code/Magento/Store/Model/StoreSwitcher/HashProcessor.php new file mode 100644 index 0000000000000..909fe9f6683f8 --- /dev/null +++ b/app/code/Magento/Store/Model/StoreSwitcher/HashProcessor.php @@ -0,0 +1,127 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Store\Model\StoreSwitcher; + +use Magento\Authorization\Model\UserContextInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Model\ResourceModel\CustomerRepository; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Framework\App\DeploymentConfig as DeploymentConfig; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Message\ManagerInterface; +use Magento\Framework\Url\Helper\Data as UrlHelper; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreSwitcher\HashGenerator\HashData; +use Magento\Store\Model\StoreSwitcherInterface; + +/** + * Process one time token and build redirect url + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) + */ +class HashProcessor implements StoreSwitcherInterface +{ + /** + * @var HashGenerator + */ + private $hashGenerator; + + /** + * @var RequestInterface + */ + private $request; + + /** + * @var ManagerInterface + */ + private $messageManager; + + /** + * @var customerSession + */ + private $customerSession; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @param HashGenerator $hashGenerator + * @param RequestInterface $request + * @param ManagerInterface $messageManager + * @param CustomerRepository $customerRepository + * @param CustomerSession $customerSession + */ + public function __construct( + HashGenerator $hashGenerator, + RequestInterface $request, + ManagerInterface $messageManager, + CustomerRepository $customerRepository, + CustomerSession $customerSession + ) { + $this->hashGenerator = $hashGenerator; + $this->request = $request; + $this->messageManager = $messageManager; + $this->customerSession = $customerSession; + $this->customerRepository = $customerRepository; + } + + /** + * Builds redirect url with token + * + * @param StoreInterface $fromStore store where we came from + * @param StoreInterface $targetStore store where to go to + * @param string $redirectUrl original url requested for redirect after switching + * @return string redirect url + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function switch(StoreInterface $fromStore, StoreInterface $targetStore, string $redirectUrl): string + { + $customerId = $this->request->getParam('customer_id'); + + if ($customerId) { + $fromStoreCode = (string)$this->request->getParam('___from_store'); + $timeStamp = (string)$this->request->getParam('time_stamp'); + $signature = (string)$this->request->getParam('signature'); + + $error = null; + + $data = new HashData( + [ + "customer_id" => $customerId, + "time_stamp" => $timeStamp, + "___from_store" => $fromStoreCode + ] + ); + + if ($redirectUrl && $this->hashGenerator->validateHash($signature, $data)) { + try { + $customer = $this->customerRepository->getById($customerId); + if (!$this->customerSession->isLoggedIn()) { + $this->customerSession->setCustomerDataAsLoggedIn($customer); + } + } catch (NoSuchEntityException $e) { + $error = __('The requested customer does not exist.'); + } catch (LocalizedException $e) { + $error = __('There was an error retrieving the customer record.'); + } + } else { + $error = __('The requested store cannot be found. Please check the request and try again.'); + } + + if ($error !== null) { + $this->messageManager->addErrorMessage($error); + } + } + + return $redirectUrl; + } +} diff --git a/app/code/Magento/Store/Model/System/Store.php b/app/code/Magento/Store/Model/System/Store.php index 744019b107247..d13781b8c146b 100644 --- a/app/code/Magento/Store/Model/System/Store.php +++ b/app/code/Magento/Store/Model/System/Store.php @@ -52,6 +52,11 @@ class Store extends \Magento\Framework\DataObject implements OptionSourceInterfa */ protected $_storeManager; + /** + * @var string + */ + private $nonEscapableNbspChar; + /** * Init model * Load Website, Group and Store collections @@ -61,6 +66,9 @@ class Store extends \Magento\Framework\DataObject implements OptionSourceInterfa public function __construct(\Magento\Store\Model\StoreManagerInterface $storeManager) { $this->_storeManager = $storeManager; + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $this->nonEscapableNbspChar = html_entity_decode(' ', ENT_NOQUOTES, 'UTF-8'); + return $this->reload(); } @@ -121,9 +129,6 @@ public function getStoreValuesForForm($empty = false, $all = false) $options[] = ['label' => __('All Store Views'), 'value' => 0]; } - // phpcs:ignore Magento2.Functions.DiscouragedFunction - $nonEscapableNbspChar = html_entity_decode(' ', ENT_NOQUOTES, 'UTF-8'); - foreach ($this->_websiteCollection as $website) { $websiteShow = false; foreach ($this->_groupCollection as $group) { @@ -140,13 +145,13 @@ public function getStoreValuesForForm($empty = false, $all = false) $websiteShow = true; } $values[] = [ - 'label' => str_repeat($nonEscapableNbspChar, 4) . $store->getName(), + 'label' => str_repeat($this->nonEscapableNbspChar, 4) . $store->getName(), 'value' => $store->getId(), ]; } if (!empty($values)) { $options[] = [ - 'label' => str_repeat($nonEscapableNbspChar, 4) . $group->getName(), + 'label' => str_repeat($this->nonEscapableNbspChar, 4) . $group->getName(), 'value' => $values, ]; } @@ -216,6 +221,22 @@ public function getStoresStructure($isAll = false, $storeIds = [], $groupIds = [ return $out; } + /** + * Get store options in tree view + * + * @param bool $isAll + * @param array $storeIds + * @param array $groupIds + * @param array $websiteIds + * @return array Format: array(array('value' => '<value>', 'label' => '<label>'), ...) + */ + public function getStoreOptionsTree($isAll = false, $storeIds = [], $groupIds = [], $websiteIds = []): array + { + $storeStructure = $this->getStoresStructure($isAll, $storeIds, $groupIds, $websiteIds); + + return $this->retrieveOptionValues($storeStructure); + } + /** * Website label/value array getter, compatible with form dropdown options * @@ -480,4 +501,35 @@ public function toOptionArray() { return $this->getStoreValuesForForm(); } + + /** + * Retrieve option values + * + * Return array of options as value-label pairs in tree view + * + * @param array $structure + * @param bool $needSpacePrefix + * @return array + */ + private function retrieveOptionValues(array $structure, bool $needSpacePrefix = false): array + { + $prefix = ''; + if ($needSpacePrefix) { + $prefix = str_repeat($this->nonEscapableNbspChar, 4); + } + + $values = []; + foreach ($structure as $item) { + $value = !empty($item['children']) + ? $this->retrieveOptionValues($item['children'], true) + : $item['value']; + + $values[] = [ + 'label' => $prefix . $item['label'], + 'value' => $value, + ]; + } + + return $values; + } } diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminAddCustomWebSiteToStoreGroupActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminAddCustomWebSiteToStoreGroupActionGroup.xml new file mode 100644 index 0000000000000..cee37d22e7970 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminAddCustomWebSiteToStoreGroupActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAddCustomWebSiteToStoreGroupActionGroup"> + <annotations> + <description>Goes to the Admin Stores grid page. Searches the grid for the provided Store Group. Edits the Store. Adds the provided Website to the Store. Clicks on Save. Validates that the Success Message is present and correct.</description> + </annotations> + <arguments> + <argument name="storeGroup" defaultValue="customStoreGroup"/> + <argument name="website" defaultValue="customWebsite"/> + </arguments> + + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> + <fillField userInput="{{storeGroup.name}}" selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" stepKey="fillSearchStoreGroupField"/> + <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> + <see userInput="{{storeGroup.name}}" selector="{{AdminStoresGridSection.storeGrpNameInFirstRow}}" stepKey="verifyThatCorrectStoreGroupFound"/> + <click selector="{{AdminStoresGridSection.storeGrpNameInFirstRow}}" stepKey="clickEditExistingStoreRow"/> + <waitForPageLoad stepKey="waitForStoreGroupPageLoad"/> + <selectOption selector="{{AdminNewStoreGroupSection.storeGrpWebsiteDropdown}}" userInput="{{website.name}}" stepKey="selectWebsite"/> + <selectOption selector="{{AdminNewStoreGroupSection.storeRootCategoryDropdown}}" userInput="Default Category" stepKey="chooseRootCategory"/> + <click selector="{{AdminNewStoreGroupActionsSection.saveButton}}" stepKey="clickSaveStoreGroup"/> + <conditionalClick selector="{{AdminNewStoreGroupSection.acceptNewStoreGroupCreation}}" dependentSelector="{{AdminNewStoreGroupSection.acceptNewStoreGroupCreation}}" visible="true" stepKey="clickAcceptNewStoreGroupCreationButton"/> + <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" stepKey="waitForPageReload"/> + <see userInput="You saved the store." stepKey="seeSavedMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateNewStoreGroupActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateNewStoreGroupActionGroup.xml index 4c00071da6b61..f1071f2743683 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateNewStoreGroupActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateNewStoreGroupActionGroup.xml @@ -31,106 +31,4 @@ <waitForElementVisible selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" stepKey="waitForStoreGridReload"/> <see userInput="You saved the store." stepKey="seeSavedMessage"/> </actionGroup> - - <actionGroup name="CreateCustomStore"> - <annotations> - <description>Goes to the Admin Stores grid page. Clicks on 'Create Store'. Fills in the provided Details (Website, Store Group Name and Store Group Code). Clicks on Save. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <argument name="website" type="string"/> - <argument name="store" type="string"/> - <argument name="rootCategory" type="string"/> - </arguments> - - <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> - <waitForPageLoad stepKey="waitForSystemStorePage"/> - <click selector="{{AdminStoresMainActionsSection.createStoreButton}}" stepKey="selectCreateStore"/> - <selectOption userInput="{{website}}" selector="{{AdminNewStoreGroupSection.storeGrpWebsiteDropdown}}" stepKey="selectMainWebsite"/> - <fillField userInput="{{store}}" selector="{{AdminNewStoreGroupSection.storeGrpNameTextField}}" stepKey="fillStoreName"/> - <fillField userInput="{{store}}" selector="{{AdminNewStoreGroupSection.storeGrpCodeTextField}}" stepKey="fillStoreCode"/> - <selectOption userInput="{{rootCategory}}" selector="{{AdminNewStoreGroupSection.storeRootCategoryDropdown}}" stepKey="selectStoreStatus"/> - <click selector="{{AdminStoreGroupActionsSection.saveButton}}" stepKey="clickSaveStoreGroup"/> - <waitForElementVisible selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" stepKey="waitForStoreGridReload"/> - <see userInput="You saved the store." stepKey="seeSavedMessage"/> - </actionGroup> - - <actionGroup name="AssertStoreGroupInGrid"> - <annotations> - <description>Goes to the Admin Stores grid page. Searches for the provided Store Group Name. Validates that the provided Store Group Name is present in the grid.</description> - </annotations> - <arguments> - <argument name="storeGroupName" type="string"/> - </arguments> - - <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> - <waitForPageLoad stepKey="waitForAdminSystemStorePageLoad"/> - <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> - <fillField userInput="{{storeGroupName}}" selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" stepKey="fillSearchStoreGroupField"/> - <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> - <waitForPageLoad stepKey="waitForStoreToLoad"/> - <see selector="{{AdminStoresGridSection.nthRow('1')}}" userInput="{{storeGroupName}}" stepKey="seeAssertStoreGroupInGridMessage"/> - </actionGroup> - - <actionGroup name="EditStoreGroupActionGroup"> - <annotations> - <description>Edit store group.</description> - </annotations> - <arguments> - <argument name="storeGroupName" type="string"/> - </arguments> - <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> - <waitForPageLoad stepKey="waitForAdminSystemStorePageLoad"/> - <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> - <waitForPageLoad stepKey="waitForResetResult"/> - <fillField userInput="{{storeGroupName}}" selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" stepKey="fillStoreGroupFilter"/> - <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> - <waitForPageLoad stepKey="waitForSearchResult"/> - <click selector="{{AdminStoresGridSection.storeGrpNameInFirstRow}}" stepKey="clicksStoreGroupName"/> - <waitForPageLoad stepKey="waitForStorePageToLoad"/> - </actionGroup> - - <actionGroup name="ChangeDefaultStoreViewActionGroup" extends="EditStoreGroupActionGroup"> - <annotations> - <description>Change the default store view for provided store group.</description> - </annotations> - <arguments> - <argument name="storeViewName" type="string"/> - </arguments> - <selectOption selector="{{AdminEditStoreGroupSection.defaultStoreView}}" userInput="{{storeViewName}}" stepKey="changeDefaultStoreView" after="waitForStorePageToLoad"/> - <click selector="{{AdminStoreGroupActionsSection.saveButton}}" stepKey="clickSaveStoreGroup"/> - <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForModal"/> - <see selector="{{AdminConfirmationModalSection.title}}" userInput="Warning message" stepKey="seeWarningAboutTakingALongTimeToComplete"/> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmModal"/> - <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="seeForSuccessMessage"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You saved the store." stepKey="seeSuccessMessage"/> - </actionGroup> - - <actionGroup name="AssertDefaultStoreViewActionGroup" extends="EditStoreGroupActionGroup"> - <annotations> - <description>Asserts that the provided store view is default in provided store group.</description> - </annotations> - <arguments> - <argument name="storeViewName" type="string"/> - </arguments> - <seeOptionIsSelected selector="{{AdminEditStoreGroupSection.defaultStoreView}}" userInput="{{storeViewName}}" stepKey="assertDefaultStoreView" after="waitForStorePageToLoad"/> - </actionGroup> - - <actionGroup name="AssertStoreGroupForm"> - <annotations> - <description>Clicks on the 1st Store in the 'Stores' grid. Validates that the provided Details (Website, Store Group Name, Store Group Code and Root Category) are present and correct.</description> - </annotations> - <arguments> - <argument name="website" type="string"/> - <argument name="storeGroupName" type="string"/> - <argument name="storeGroupCode" type="string"/> - <argument name="rootCategory" type="string"/> - </arguments> - - <click selector="{{AdminStoresGridSection.storeGrpNameInFirstRow}}" stepKey="clickEditExistingStoreRow"/> - <waitForPageLoad stepKey="waitTillAdminSystemStoreGroupPage"/> - <seeInField selector="{{AdminNewStoreGroupSection.storeGrpWebsiteDropdown}}" userInput="{{website}}" stepKey="seeAssertWebsite"/> - <seeInField selector="{{AdminNewStoreGroupSection.storeGrpNameTextField}}" userInput="{{storeGroupName}}" stepKey="seeAssertStoreGroupName"/> - <seeInField selector="{{AdminNewStoreGroupSection.storeGrpCodeTextField}}" userInput="{{storeGroupCode}}" stepKey="seeAssertStoreGroupCode"/> - <seeInField selector="{{AdminNewStoreGroupSection.storeRootCategoryDropdown}}" userInput="{{rootCategory}}" stepKey="seeAssertRootCategory"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml index 0faf93d4bde14..108b8014bf127 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewActionGroup.xml @@ -30,65 +30,6 @@ <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForModal"/> <see selector="{{AdminConfirmationModalSection.title}}" userInput="Warning message" stepKey="seeWarningAboutTakingALongTimeToComplete"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmModal"/> - <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForPageReload"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You saved the store view." stepKey="seeSavedMessage"/> - </actionGroup> - - <actionGroup name="AdminCreateStoreViewWithoutCheckActionGroup" extends="AdminCreateStoreViewActionGroup"> - <annotations> - <description>EXTENDS: AdminCreateStoreViewActionGroup. Removes 'waitForPageReload' and 'seeSavedMessage'.</description> - </annotations> - - <remove keyForRemoval="waitForPageReload"/> - <remove keyForRemoval="seeSavedMessage"/> - </actionGroup> - - <!--Save the Store view--> - <actionGroup name="AdminCreateStoreViewActionSaveGroup"> - <annotations> - <description>Validates that the Success Message is present and correct.</description> - </annotations> - - <waitForLoadingMaskToDisappear stepKey="waitForGridLoad"/> - <waitForElementVisible selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="waitForStoreGridToReload2"/> - <see userInput="You saved the store view." stepKey="seeSavedMessage"/> - </actionGroup> - - <actionGroup name="navigateToAdminContentManagementPage"> - <annotations> - <description>Goes to the 'Configuration' page for 'Content Management'.</description> - </annotations> - - <amOnPage url="{{AdminContentManagementPage.url}}" stepKey="navigateToConfigurationPage"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - </actionGroup> - - <actionGroup name="saveStoreConfiguration"> - <annotations> - <description>Clicks on the Save button.</description> - </annotations> - - <comment userInput="saveStoreConfiguration" stepKey="comment"/> - <waitForElementVisible selector="{{StoreConfigSection.Save}}" stepKey="waitForSaveButton"/> - <click selector="{{StoreConfigSection.Save}}" stepKey="clickSaveButton"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - </actionGroup> - - <actionGroup name="saveStoreConfigurationAndValidateFieldError"> - <annotations> - <description>Clicks on Save. Validates that the fields are required.</description> - </annotations> - <arguments> - <argument name="inputFieldError" type="string"/> - <argument name="errorMessageSelector" type="string"/> - <argument name="errorMessage" type="string"/> - </arguments> - - <comment userInput="saveStoreConfigurationAndValidateFieldError" stepKey="comment"/> - <waitForElementVisible selector="{{StoreConfigSection.Save}}" stepKey="waitForSaveButton"/> - <click selector="{{StoreConfigSection.Save}}" stepKey="clickSaveButton"/> - <waitForElement selector="{{inputFieldError}}" stepKey="waitForErrorField"/> - <waitForElementVisible selector="{{errorMessageSelector}}" stepKey="waitForErrorMessage"/> - <see selector="{{errorMessageSelector}}" userInput="{{errorMessage}}" stepKey="seeErrorMessage"/> + <waitForText selector="{{AdminMessagesSection.success}}" userInput="You saved the store view." stepKey="seeSavedMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewSaveActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewSaveActionGroup.xml new file mode 100644 index 0000000000000..0d1a2bd5319df --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewSaveActionGroup.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="AdminCreateStoreViewSaveActionGroup"> + <annotations> + <description>Validates that the Success Message is present and correct.</description> + </annotations> + + <waitForLoadingMaskToDisappear stepKey="waitForGridLoad"/> + <waitForElementVisible selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="waitForStoreGridToReload2"/> + <see userInput="You saved the store view." stepKey="seeSavedMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewWithoutCheckActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewWithoutCheckActionGroup.xml new file mode 100644 index 0000000000000..c6837dc55b34e --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewWithoutCheckActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateStoreViewWithoutCheckActionGroup" extends="AdminCreateStoreViewActionGroup"> + <annotations> + <description>EXTENDS: AdminCreateStoreViewActionGroup. Removes 'waitForPageReload' and 'seeSavedMessage'.</description> + </annotations> + + <remove keyForRemoval="waitForPageReload"/> + <remove keyForRemoval="seeSavedMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml index 6a2f601754c41..19be38d130ace 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateWebsiteActionGroup.xml @@ -28,56 +28,4 @@ <waitForElementVisible selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="waitForStoreGridToReload"/> <see userInput="You saved the website." stepKey="seeSavedMessage"/> </actionGroup> - - <actionGroup name="AdminGetWebsiteIdActionGroup"> - <!--Get Website_id--> - <annotations> - <description>Goes to the Admin Stores grid page. Filters the grid for the provided Website. Grabs the Website ID from the URL.</description> - </annotations> - <arguments> - <argument name="website"/> - </arguments> - - <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnTheStorePage"/> - <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clickClearFilters"/> - <fillField selector="{{AdminStoresGridSection.websiteFilterTextField}}" userInput="{{website.name}}" stepKey="fillSearchWebsiteField"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFilters"/> - <see selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" userInput="{{website.name}}" stepKey="verifyThatCorrectWebsiteFound"/> - <click selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" stepKey="clickEditExistingWebsite"/> - <grabFromCurrentUrl regex="~(\d+)/~" stepKey="grabFromCurrentUrl"/> - </actionGroup> - - <actionGroup name="AssertWebsiteInGrid"> - <annotations> - <description>Goes to the Admin Stores grid page. Searches the grid for the provided Website Name. Validates that the Website appears in the grid.</description> - </annotations> - <arguments> - <argument name="websiteName" type="string"/> - </arguments> - - <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> - <waitForPageLoad stepKey="waitForAdminSystemStorePageLoad"/> - <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> - <fillField userInput="{{websiteName}}" selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="fillWebsiteField"/> - <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> - <waitForPageLoad stepKey="waitForStoreToLoad"/> - <seeElement selector="{{AdminStoresGridSection.websiteName(websiteName)}}" stepKey="seeAssertWebsiteInGrid"/> - </actionGroup> - - <actionGroup name="AssertWebsiteForm"> - <annotations> - <description>Clicks on the provided Website Name in the Admin Stores grid. Validates that the URL, Website Name/Code are present and correct.</description> - </annotations> - <arguments> - <argument name="websiteName" type="string"/> - <argument name="websiteCode" type="string"/> - </arguments> - - <click selector="{{AdminStoresGridSection.websiteName(websiteName)}}" stepKey="clickWebsiteFirstRowInGrid"/> - <waitForPageLoad stepKey="waitTillWebsiteFormPageIsOpened"/> - <grabFromCurrentUrl regex="~(\d+)/~" stepKey="grabWebsiteIdFromCurrentUrl"/> - <seeInCurrentUrl url="/system_store/editWebsite/website_id/{$grabWebsiteIdFromCurrentUrl}" stepKey="seeWebsiteId"/> - <seeInField selector="{{AdminNewWebsiteSection.name}}" userInput="{{websiteName}}" stepKey="seeAssertWebsiteName"/> - <seeInField selector="{{AdminNewWebsiteSection.code}}" userInput="{{websiteCode}}" stepKey="seeAssertWebsiteCode"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml index 63dc4b0ded4f9..520a80bcbc328 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewActionGroup.xml @@ -29,47 +29,7 @@ <waitForElementVisible selector="{{AdminConfirmationModalSection.title}}" stepKey="waitingForWarningModal"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmStoreDelete"/> <waitForPageLoad stepKey="waitForSuccessMessage"/> - <see userInput="You deleted the store view." stepKey="seeDeleteMessage"/> - </actionGroup> - - <actionGroup name="DeleteCustomStoreViewBackupEnabledYesActionGroup"> - <annotations> - <description>Goes to the Admin Stores grid page. Deleted the provided Store while creating a Backup. Validates that the Success Messages (Delete/Backup) are present and correct.</description> - </annotations> - <arguments> - <argument name="storeViewName" type="string"/> - </arguments> - - <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> - <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> - <fillField userInput="{{storeViewName}}" selector="{{AdminStoresGridSection.storeFilterTextField}}" stepKey="fillSearchStoreViewField"/> - <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> - <click selector="{{AdminStoresGridSection.storeNameInFirstRow}}" stepKey="clickEditExistingStoreViewRow"/> - <waitForPageLoad stepKey="waitForStoreToLoad"/> - <click selector="{{AdminNewStoreViewActionsSection.delete}}" stepKey="clickDeleteStoreViewButtonOnEditStorePage"/> - <selectOption userInput="Yes" selector="{{AdminStoreBackupOptionsSection.createBackupSelect}}" stepKey="setCreateDbBackupToYes"/> - <click selector="{{AdminNewStoreViewActionsSection.delete}}" stepKey="clickDeleteStoreViewButtonOnDeleteStorePage"/> - <waitForElementVisible selector="{{AdminConfirmationModalSection.title}}" stepKey="waitingForWarningModal"/> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmStoreViewDelete"/> - <waitForPageLoad stepKey="waitForSuccessMessage"/> - <see selector="{{AdminStoresGridSection.successMessage}}" userInput="The database was backed up." stepKey="seeAssertDatabaseBackedUpMessage"/> - <see selector="{{AdminStoresGridSection.successMessage}}" userInput="You deleted the store view." stepKey="seeAssertSuccessDeleteStoreViewMessage"/> - </actionGroup> - - <actionGroup name="AssertStoreViewNotInGrid"> - <annotations> - <description>Goes to the Admin Stores grid page. Searches the grid for the provided Store View name. Validates that it does NOT appear in the grid.</description> - </annotations> - <arguments> - <argument name="storeViewName" type="string"/> - </arguments> - - <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> - <waitForPageLoad stepKey="waitForAdminSystemStorePageLoad"/> - <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> - <fillField userInput="{{storeViewName}}" selector="{{AdminStoresGridSection.storeFilterTextField}}" stepKey="fillSearchStoreViewField"/> - <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> - <waitForPageLoad stepKey="waitForStoreToLoad"/> - <see selector="{{AdminStoresGridSection.emptyText}}" userInput="We couldn't find any records." stepKey="seeAssertStoreViewNotInGridMessage"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitSuccessMessageAppears"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the store view." stepKey="seeDeleteMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewIfExistsActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewIfExistsActionGroup.xml new file mode 100644 index 0000000000000..6ebf72a893c04 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteStoreViewIfExistsActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminDeleteStoreViewIfExistsActionGroup" extends="AdminSearchStoreViewByNameActionGroup"> + <annotations> + <description>EXTENDS: AdminSearchStoreViewByNameActionGroup. Goes to the Admin Stores grid page. Deletes the provided Store (if exists) without creating a Backup. Validates that the Success Message is present and correct.</description> + </annotations> + + <executeInSelenium function="function($webdriver) use ($I) { + $items = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::cssSelector('.col-store_title>a')); + if(!empty($items)) { + $I->click('.col-store_title>a'); + $I->waitForPageLoad(10); + $I->click('#delete'); + $I->waitForPageLoad(30); + $I->selectOption('select#store_create_backup', 'No'); + $I->click('#delete'); + $I->waitForPageLoad(30); + $I->waitForElementVisible('aside.confirm .modal-title', 10); + $I->click('aside.confirm .modal-footer button.action-accept'); + $I->waitForPageLoad(60); + $I->waitForElementVisible('#messages div.message-success', 10); + $I->see('You deleted the store view.', '#messages div.message-success'); + } + }" after="clickSearchButton" stepKey="deleteStoreViewIfExists"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminGetWebsiteIdActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminGetWebsiteIdActionGroup.xml new file mode 100644 index 0000000000000..9e58643981506 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminGetWebsiteIdActionGroup.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="AdminGetWebsiteIdActionGroup"> + <!--Get Website_id--> + <annotations> + <description>Goes to the Admin Stores grid page. Filters the grid for the provided Website. Grabs the Website ID from the URL.</description> + </annotations> + <arguments> + <argument name="website"/> + </arguments> + + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnTheStorePage"/> + <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clickClearFilters"/> + <fillField selector="{{AdminStoresGridSection.websiteFilterTextField}}" userInput="{{website.name}}" stepKey="fillSearchWebsiteField"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFilters"/> + <see selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" userInput="{{website.name}}" stepKey="verifyThatCorrectWebsiteFound"/> + <click selector="{{AdminStoresGridSection.websiteNameInFirstRow}}" stepKey="clickEditExistingWebsite"/> + <grabFromCurrentUrl regex="~(\d+)/~" stepKey="grabFromCurrentUrl"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSearchStoreViewByNameActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSearchStoreViewByNameActionGroup.xml new file mode 100644 index 0000000000000..cb8c798a7eaed --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSearchStoreViewByNameActionGroup.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="AdminSearchStoreViewByNameActionGroup"> + <annotations> + <description>Goes to the Admin Stores grid page. Clears filters and search by store view name.</description> + </annotations> + <arguments> + <argument name="storeViewName" type="string"/> + </arguments> + + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="resetSearchFilter"/> + <fillField selector="{{AdminStoresGridSection.storeFilterTextField}}" userInput="{{storeViewName}}" stepKey="fillSearchStoreViewField"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminStoreGroupCreateActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminStoreGroupCreateActionGroup.xml index 8afa8df4a5e3d..4da1208717f90 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminStoreGroupCreateActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminStoreGroupCreateActionGroup.xml @@ -28,28 +28,4 @@ <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" stepKey="waitForPageReload"/> <see userInput="You saved the store." stepKey="seeSavedMessage"/> </actionGroup> - - <actionGroup name="AdminAddCustomWebSiteToStoreGroup"> - <annotations> - <description>Goes to the Admin Stores grid page. Searches the grid for the provided Store Group. Edits the Store. Adds the provided Website to the Store. Clicks on Save. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <argument name="storeGroup" defaultValue="customStoreGroup"/> - <argument name="website" defaultValue="customWebsite"/> - </arguments> - - <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> - <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> - <fillField userInput="{{storeGroup.name}}" selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" stepKey="fillSearchStoreGroupField"/> - <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> - <see userInput="{{storeGroup.name}}" selector="{{AdminStoresGridSection.storeGrpNameInFirstRow}}" stepKey="verifyThatCorrectStoreGroupFound"/> - <click selector="{{AdminStoresGridSection.storeGrpNameInFirstRow}}" stepKey="clickEditExistingStoreRow"/> - <waitForPageLoad stepKey="waitForStoreGroupPageLoad"/> - <selectOption selector="{{AdminNewStoreGroupSection.storeGrpWebsiteDropdown}}" userInput="{{website.name}}" stepKey="selectWebsite"/> - <selectOption selector="{{AdminNewStoreGroupSection.storeRootCategoryDropdown}}" userInput="Default Category" stepKey="chooseRootCategory"/> - <click selector="{{AdminNewStoreGroupActionsSection.saveButton}}" stepKey="clickSaveStoreGroup"/> - <conditionalClick selector="{{AdminNewStoreGroupSection.acceptNewStoreGroupCreation}}" dependentSelector="{{AdminNewStoreGroupSection.acceptNewStoreGroupCreation}}" visible="true" stepKey="clickAcceptNewStoreGroupCreationButton"/> - <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" stepKey="waitForPageReload"/> - <see userInput="You saved the store." stepKey="seeSavedMessage"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml index 22b5d22c2fcc9..13e9beff9745b 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchStoreViewActionGroup.xml @@ -15,7 +15,7 @@ <arguments> <argument name="storeView" defaultValue="customStore.name"/> </arguments> - + <click selector="{{AdminMainActionsSection.storeViewDropdown}}" stepKey="clickStoreViewSwitchDropdown"/> <waitForElementVisible selector="{{AdminMainActionsSection.storeViewByName('Default Store View')}}" stepKey="waitForStoreViewsAreVisible"/> <click selector="{{AdminMainActionsSection.storeViewByName(storeView)}}" stepKey="clickStoreViewByName"/> @@ -25,13 +25,4 @@ <scrollToTopOfPage stepKey="scrollToStoreSwitcher"/> <see userInput="{{storeView}}" selector="{{AdminMainActionsSection.storeSwitcher}}" stepKey="seeNewStoreViewName"/> </actionGroup> - - <actionGroup name="AdminSwitchToAllStoreViewActionGroup" extends="AdminSwitchStoreViewActionGroup"> - <annotations> - <description>EXTENDS: AdminSwitchStoreViewActionGroup. Clicks on the 'All Store Views' drop down menu. Validates that the 'All Store Views' options is present and correct.</description> - </annotations> - - <click selector="{{AdminMainActionsSection.allStoreViews}}" stepKey="clickStoreViewByName" after="waitForStoreViewsAreVisible"/> - <see selector="{{AdminMainActionsSection.storeSwitcher}}" userInput="All Store Views" stepKey="seeNewStoreViewName" after="waitForStoreViewSwitched"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchToAllStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchToAllStoreViewActionGroup.xml new file mode 100644 index 0000000000000..fe56aa128dd7e --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminSwitchToAllStoreViewActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSwitchToAllStoreViewActionGroup" extends="AdminSwitchStoreViewActionGroup"> + <annotations> + <description>EXTENDS: AdminSwitchStoreViewActionGroup. Clicks on the 'All Store Views' drop down menu. Validates that the 'All Store Views' options is present and correct.</description> + </annotations> + + <click selector="{{AdminMainActionsSection.allStoreViews}}" stepKey="clickStoreViewByName" after="waitForStoreViewsAreVisible"/> + <see selector="{{AdminMainActionsSection.storeSwitcher}}" userInput="All Store Views" stepKey="seeNewStoreViewName" after="waitForStoreViewSwitched"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertDefaultStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertDefaultStoreViewActionGroup.xml new file mode 100644 index 0000000000000..e899241cfdd1f --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertDefaultStoreViewActionGroup.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="AssertDefaultStoreViewActionGroup" extends="EditStoreGroupActionGroup"> + <annotations> + <description>Asserts that the provided store view is default in provided store group.</description> + </annotations> + <arguments> + <argument name="storeViewName" type="string"/> + </arguments> + <seeOptionIsSelected selector="{{AdminEditStoreGroupSection.defaultStoreView}}" userInput="{{storeViewName}}" stepKey="assertDefaultStoreView" after="waitForStorePageToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStoreNotInGridActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStoreNotInGridActionGroup.xml new file mode 100644 index 0000000000000..3c9ebe0daa14e --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStoreNotInGridActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStoreNotInGridActionGroup"> + <annotations> + <description>Goes to the Admin Stores grid page. Validates that the provided Store Group Name is NOT present in the grid.</description> + </annotations> + <arguments> + <argument name="storeGroupName" type="string"/> + </arguments> + + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <waitForPageLoad stepKey="waitForAdminSystemStorePageLoad"/> + <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> + <fillField userInput="{{storeGroupName}}" selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" stepKey="fillSearchStoreGroupField"/> + <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> + <waitForPageLoad stepKey="waitForStoreToLoad"/> + <see selector="{{AdminStoresGridSection.emptyText}}" userInput="We couldn't find any records." stepKey="seeAssertStoreGroupNotInGridMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStoreViewNotInGridActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStoreViewNotInGridActionGroup.xml new file mode 100644 index 0000000000000..b258f2e826200 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertStoreViewNotInGridActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStoreViewNotInGridActionGroup"> + <annotations> + <description>Goes to the Admin Stores grid page. Searches the grid for the provided Store View name. Validates that it does NOT appear in the grid.</description> + </annotations> + <arguments> + <argument name="storeViewName" type="string"/> + </arguments> + + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <waitForPageLoad stepKey="waitForAdminSystemStorePageLoad"/> + <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> + <fillField userInput="{{storeViewName}}" selector="{{AdminStoresGridSection.storeFilterTextField}}" stepKey="fillSearchStoreViewField"/> + <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> + <waitForPageLoad stepKey="waitForStoreToLoad"/> + <see selector="{{AdminStoresGridSection.emptyText}}" userInput="We couldn't find any records." stepKey="seeAssertStoreViewNotInGridMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertWebsiteFormActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertWebsiteFormActionGroup.xml new file mode 100644 index 0000000000000..3738d975e5131 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertWebsiteFormActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertWebsiteFormActionGroup"> + <annotations> + <description>Clicks on the provided Website Name in the Admin Stores grid. Validates that the URL, Website Name/Code are present and correct.</description> + </annotations> + <arguments> + <argument name="websiteName" type="string"/> + <argument name="websiteCode" type="string"/> + </arguments> + + <click selector="{{AdminStoresGridSection.websiteName(websiteName)}}" stepKey="clickWebsiteFirstRowInGrid"/> + <waitForPageLoad stepKey="waitTillWebsiteFormPageIsOpened"/> + <grabFromCurrentUrl regex="~(\d+)/~" stepKey="grabWebsiteIdFromCurrentUrl"/> + <seeInCurrentUrl url="/system_store/editWebsite/website_id/{$grabWebsiteIdFromCurrentUrl}" stepKey="seeWebsiteId"/> + <seeInField selector="{{AdminNewWebsiteSection.name}}" userInput="{{websiteName}}" stepKey="seeAssertWebsiteName"/> + <seeInField selector="{{AdminNewWebsiteSection.code}}" userInput="{{websiteCode}}" stepKey="seeAssertWebsiteCode"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertWebsiteInGridActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertWebsiteInGridActionGroup.xml new file mode 100644 index 0000000000000..0835e3bc330b0 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AssertWebsiteInGridActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertWebsiteInGridActionGroup"> + <annotations> + <description>Goes to the Admin Stores grid page. Searches the grid for the provided Website Name. Validates that the Website appears in the grid.</description> + </annotations> + <arguments> + <argument name="websiteName" type="string"/> + </arguments> + + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <waitForPageLoad stepKey="waitForAdminSystemStorePageLoad"/> + <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> + <fillField userInput="{{websiteName}}" selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="fillWebsiteField"/> + <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> + <waitForPageLoad stepKey="waitForStoreToLoad"/> + <seeElement selector="{{AdminStoresGridSection.websiteName(websiteName)}}" stepKey="seeAssertWebsiteInGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/ChangeDefaultStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/ChangeDefaultStoreViewActionGroup.xml new file mode 100644 index 0000000000000..40ebb95e9cdb2 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/ChangeDefaultStoreViewActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ChangeDefaultStoreViewActionGroup" extends="EditStoreGroupActionGroup"> + <annotations> + <description>Change the default store view for provided store group.</description> + </annotations> + <arguments> + <argument name="storeViewName" type="string"/> + </arguments> + <selectOption selector="{{AdminEditStoreGroupSection.defaultStoreView}}" userInput="{{storeViewName}}" stepKey="changeDefaultStoreView" after="waitForStorePageToLoad"/> + <click selector="{{AdminStoreGroupActionsSection.saveButton}}" stepKey="clickSaveStoreGroup"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" stepKey="waitForModal"/> + <see selector="{{AdminConfirmationModalSection.title}}" userInput="Warning message" stepKey="seeWarningAboutTakingALongTimeToComplete"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmModal"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="seeForSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the store." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/CreateCustomStoreActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/CreateCustomStoreActionGroup.xml new file mode 100644 index 0000000000000..627b8f0b096a1 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/CreateCustomStoreActionGroup.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="CreateCustomStoreActionGroup"> + <annotations> + <description>Goes to the Admin Stores grid page. Clicks on 'Create Store'. Fills in the provided Details (Website, Store Group Name and Store Group Code). Clicks on Save. Validates that the Success Message is present and correct.</description> + </annotations> + <arguments> + <argument name="website" type="string"/> + <argument name="store" type="string"/> + <argument name="rootCategory" type="string"/> + </arguments> + + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <waitForPageLoad stepKey="waitForSystemStorePage"/> + <click selector="{{AdminStoresMainActionsSection.createStoreButton}}" stepKey="selectCreateStore"/> + <selectOption userInput="{{website}}" selector="{{AdminNewStoreGroupSection.storeGrpWebsiteDropdown}}" stepKey="selectMainWebsite"/> + <fillField userInput="{{store}}" selector="{{AdminNewStoreGroupSection.storeGrpNameTextField}}" stepKey="fillStoreName"/> + <fillField userInput="{{store}}" selector="{{AdminNewStoreGroupSection.storeGrpCodeTextField}}" stepKey="fillStoreCode"/> + <selectOption userInput="{{rootCategory}}" selector="{{AdminNewStoreGroupSection.storeRootCategoryDropdown}}" stepKey="selectStoreStatus"/> + <click selector="{{AdminStoreGroupActionsSection.saveButton}}" stepKey="clickSaveStoreGroup"/> + <waitForElementVisible selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" stepKey="waitForStoreGridReload"/> + <see userInput="You saved the store." stepKey="seeSavedMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/CreateCustomStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/CreateCustomStoreViewActionGroup.xml index 290d0ad623bad..cf57afc5f7439 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/CreateCustomStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/CreateCustomStoreViewActionGroup.xml @@ -26,26 +26,4 @@ <waitForPageLoad stepKey="waitForPageLoad2"/> <conditionalClick selector="{{AdminNewStoreSection.acceptNewStoreViewCreation}}" dependentSelector="{{AdminNewStoreSection.acceptNewStoreViewCreation}}" visible="true" stepKey="clickAcceptNewStoreViewCreationButton"/> </actionGroup> - - <actionGroup name="CreateStoreView"> - <annotations> - <description>Goes to the Admin Store Views creation page. Fills in the provided Store View, Store Group Name and Store View Status. Clicks on Save. Validates that the Success Message is present and correct.</description> - </annotations> - <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> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/CreateStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/CreateStoreViewActionGroup.xml new file mode 100644 index 0000000000000..916048e33ea44 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/CreateStoreViewActionGroup.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="CreateStoreViewActionGroup"> + <annotations> + <description>Goes to the Admin Store Views creation page. Fills in the provided Store View, Store Group Name and Store View Status. Clicks on Save. Validates that the Success Message is present and correct.</description> + </annotations> + <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> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomStoreActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomStoreActionGroup.xml index f93b0a22f7558..848fd5cf49d07 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomStoreActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomStoreActionGroup.xml @@ -26,46 +26,7 @@ <click selector="{{AdminStoresMainActionsSection.deleteButton}}" stepKey="clickDeleteStoreGroupButtonOnEditStorePage"/> <selectOption userInput="No" selector="{{AdminStoresDeleteStoreGroupSection.createDbBackup}}" stepKey="setCreateDbBackupToNo"/> <click selector="{{AdminStoresDeleteStoreGroupSection.deleteStoreGroupButton}}" stepKey="clickDeleteStoreGroupButtonOnDeleteStorePage"/> - <waitForElementVisible selector="{{AdminMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="You deleted the store." stepKey="seeSuccessMessage"/> - </actionGroup> - - <actionGroup name="DeleteCustomStoreBackupEnabledYesActionGroup"> - <annotations> - <description>Goes to the Admin Stores grid page. Deletes the provided Store Group Name while creating a DB backup. Validates that the Success Messages are present and correct.</description> - </annotations> - <arguments> - <argument name="storeGroupName" type="string"/> - </arguments> - - <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> - <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> - <fillField userInput="{{storeGroupName}}" selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" stepKey="fillSearchStoreGroupField"/> - <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> - <see userInput="{{storeGroupName}}" selector="{{AdminStoresGridSection.storeGrpNameInFirstRow}}" stepKey="verifyThatCorrectStoreGroupFound"/> - <click selector="{{AdminStoresGridSection.storeGrpNameInFirstRow}}" stepKey="clickEditExistingStoreRow"/> - <waitForPageLoad stepKey="waitForStoreToLoad"/> - <click selector="{{AdminStoresMainActionsSection.deleteButton}}" stepKey="clickDeleteStoreGroupButtonOnEditStorePage"/> - <selectOption userInput="Yes" selector="{{AdminStoresDeleteStoreGroupSection.createDbBackup}}" stepKey="setCreateDbBackupToNo"/> - <click selector="{{AdminStoresDeleteStoreGroupSection.deleteStoreGroupButton}}" stepKey="clickDeleteStoreGroupButtonOnDeleteStorePage"/> - <see selector="{{AdminStoresGridSection.successMessage}}" userInput="The database was backed up." stepKey="seeAssertDatabaseBackedUpMessage"/> - <see selector="{{AdminStoresGridSection.successMessage}}" userInput="You deleted the store." stepKey="seeAssertSuccessDeleteStoreGroupMessage"/> - </actionGroup> - - <actionGroup name="AssertStoreNotInGrid"> - <annotations> - <description>Goes to the Admin Stores grid page. Validates that the provided Store Group Name is NOT present in the grid.</description> - </annotations> - <arguments> - <argument name="storeGroupName" type="string"/> - </arguments> - - <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> - <waitForPageLoad stepKey="waitForAdminSystemStorePageLoad"/> - <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> - <fillField userInput="{{storeGroupName}}" selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" stepKey="fillSearchStoreGroupField"/> - <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> - <waitForPageLoad stepKey="waitForStoreToLoad"/> - <see selector="{{AdminStoresGridSection.emptyText}}" userInput="We couldn't find any records." stepKey="seeAssertStoreGroupNotInGridMessage"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the store." stepKey="seeSuccessMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomStoreBackupEnabledYesActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomStoreBackupEnabledYesActionGroup.xml new file mode 100644 index 0000000000000..97fb6a83ab06f --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomStoreBackupEnabledYesActionGroup.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="DeleteCustomStoreBackupEnabledYesActionGroup"> + <annotations> + <description>Goes to the Admin Stores grid page. Deletes the provided Store Group Name while creating a DB backup. Validates that the Success Messages are present and correct.</description> + </annotations> + <arguments> + <argument name="storeGroupName" type="string"/> + </arguments> + + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> + <fillField userInput="{{storeGroupName}}" selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" stepKey="fillSearchStoreGroupField"/> + <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> + <see userInput="{{storeGroupName}}" selector="{{AdminStoresGridSection.storeGrpNameInFirstRow}}" stepKey="verifyThatCorrectStoreGroupFound"/> + <click selector="{{AdminStoresGridSection.storeGrpNameInFirstRow}}" stepKey="clickEditExistingStoreRow"/> + <waitForPageLoad stepKey="waitForStoreToLoad"/> + <click selector="{{AdminStoresMainActionsSection.deleteButton}}" stepKey="clickDeleteStoreGroupButtonOnEditStorePage"/> + <selectOption userInput="Yes" selector="{{AdminStoresDeleteStoreGroupSection.createDbBackup}}" stepKey="setCreateDbBackupToNo"/> + <click selector="{{AdminStoresDeleteStoreGroupSection.deleteStoreGroupButton}}" stepKey="clickDeleteStoreGroupButtonOnDeleteStorePage"/> + <see selector="{{AdminStoresGridSection.successMessage}}" userInput="The database was backed up." stepKey="seeAssertDatabaseBackedUpMessage"/> + <see selector="{{AdminStoresGridSection.successMessage}}" userInput="You deleted the store." stepKey="seeAssertSuccessDeleteStoreGroupMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomStoreViewBackupEnabledYesActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomStoreViewBackupEnabledYesActionGroup.xml new file mode 100644 index 0000000000000..f670a4771ca5c --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomStoreViewBackupEnabledYesActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteCustomStoreViewBackupEnabledYesActionGroup"> + <annotations> + <description>Goes to the Admin Stores grid page. Deleted the provided Store while creating a Backup. Validates that the Success Messages (Delete/Backup) are present and correct.</description> + </annotations> + <arguments> + <argument name="storeViewName" type="string"/> + </arguments> + + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> + <fillField userInput="{{storeViewName}}" selector="{{AdminStoresGridSection.storeFilterTextField}}" stepKey="fillSearchStoreViewField"/> + <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> + <click selector="{{AdminStoresGridSection.storeNameInFirstRow}}" stepKey="clickEditExistingStoreViewRow"/> + <waitForPageLoad stepKey="waitForStoreToLoad"/> + <click selector="{{AdminNewStoreViewActionsSection.delete}}" stepKey="clickDeleteStoreViewButtonOnEditStorePage"/> + <selectOption userInput="Yes" selector="{{AdminStoreBackupOptionsSection.createBackupSelect}}" stepKey="setCreateDbBackupToYes"/> + <click selector="{{AdminNewStoreViewActionsSection.delete}}" stepKey="clickDeleteStoreViewButtonOnDeleteStorePage"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.title}}" stepKey="waitingForWarningModal"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmStoreViewDelete"/> + <waitForPageLoad stepKey="waitForSuccessMessage"/> + <see selector="{{AdminStoresGridSection.successMessage}}" userInput="The database was backed up." stepKey="seeAssertDatabaseBackedUpMessage"/> + <see selector="{{AdminStoresGridSection.successMessage}}" userInput="You deleted the store view." stepKey="seeAssertSuccessDeleteStoreViewMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml index 90dc74e3a3fee..77d148eedb99f 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/DeleteCustomWebsiteActionGroup.xml @@ -15,7 +15,7 @@ <arguments> <argument name="websiteName" defaultValue="customWebsite.name"/> </arguments> - + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnTheStorePage"/> <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="clickOnResetButton"/> <fillField userInput="{{websiteName}}" selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="fillSearchWebsiteField"/> @@ -26,6 +26,6 @@ <click selector="{{AdminStoresMainActionsSection.deleteButton}}" stepKey="clickDeleteWebsiteButtonOnEditStorePage"/> <selectOption userInput="No" selector="{{AdminStoresDeleteWebsiteSection.createDbBackup}}" stepKey="setCreateDbBackupToNo"/> <click selector="{{AdminStoresDeleteWebsiteSection.deleteButton}}" stepKey="clickDeleteButtonOnDeleteWebsitePage"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="You deleted the website." stepKey="checkSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the website." stepKey="checkSuccessMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/EditCustomStoreGroupAcceptWarningMessageActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/EditCustomStoreGroupAcceptWarningMessageActionGroup.xml index 5493ae7399ddd..1c2ae675573a8 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/EditCustomStoreGroupAcceptWarningMessageActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/EditCustomStoreGroupAcceptWarningMessageActionGroup.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="EditCustomStoreGroupAcceptWarningMessageActionGroup" extends="CreateCustomStore"> + <actionGroup name="EditCustomStoreGroupAcceptWarningMessageActionGroup" extends="CreateCustomStoreActionGroup"> <!-- Admin creates new Store group --> <annotations> <description>EXTENDS: CreateCustomStore. Removes 'selectCreateStore'. Clicks on the 1st row. Clicks on Ok.</description> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/EditStoreGroupActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/EditStoreGroupActionGroup.xml new file mode 100644 index 0000000000000..7e2a0ac443d51 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/EditStoreGroupActionGroup.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="EditStoreGroupActionGroup"> + <annotations> + <description>Edit store group.</description> + </annotations> + <arguments> + <argument name="storeGroupName" type="string"/> + </arguments> + <amOnPage url="{{AdminSystemStorePage.url}}" stepKey="amOnAdminSystemStorePage"/> + <waitForPageLoad stepKey="waitForAdminSystemStorePageLoad"/> + <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter"/> + <waitForPageLoad stepKey="waitForResetResult"/> + <fillField userInput="{{storeGroupName}}" selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" stepKey="fillStoreGroupFilter"/> + <click selector="{{AdminStoresGridSection.searchButton}}" stepKey="clickSearchButton"/> + <waitForPageLoad stepKey="waitForSearchResult"/> + <click selector="{{AdminStoresGridSection.storeGrpNameInFirstRow}}" stepKey="clicksStoreGroupName"/> + <waitForPageLoad stepKey="waitForStorePageToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/NavigateToAdminContentManagementPageActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/NavigateToAdminContentManagementPageActionGroup.xml new file mode 100644 index 0000000000000..bf3a3b2f8509e --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/NavigateToAdminContentManagementPageActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="NavigateToAdminContentManagementPageActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page for 'Content Management'.</description> + </annotations> + + <amOnPage url="{{AdminContentManagementPage.url}}" stepKey="navigateToConfigurationPage"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/SaveStoreConfigurationActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/SaveStoreConfigurationActionGroup.xml new file mode 100644 index 0000000000000..ce68b7a05b8d2 --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/SaveStoreConfigurationActionGroup.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="SaveStoreConfigurationActionGroup"> + <annotations> + <description>Clicks on the Save button.</description> + </annotations> + + <comment userInput="saveStoreConfiguration" stepKey="comment"/> + <waitForElementVisible selector="{{StoreConfigSection.Save}}" stepKey="waitForSaveButton"/> + <click selector="{{StoreConfigSection.Save}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/SaveStoreConfigurationAndValidateFieldErrorActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/SaveStoreConfigurationAndValidateFieldErrorActionGroup.xml new file mode 100644 index 0000000000000..82b591e8f92dc --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/SaveStoreConfigurationAndValidateFieldErrorActionGroup.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="SaveStoreConfigurationAndValidateFieldErrorActionGroup"> + <annotations> + <description>Clicks on Save. Validates that the fields are required.</description> + </annotations> + <arguments> + <argument name="inputFieldError" type="string"/> + <argument name="errorMessageSelector" type="string"/> + <argument name="errorMessage" type="string"/> + </arguments> + + <comment userInput="saveStoreConfigurationAndValidateFieldError" stepKey="comment"/> + <waitForElementVisible selector="{{StoreConfigSection.Save}}" stepKey="waitForSaveButton"/> + <click selector="{{StoreConfigSection.Save}}" stepKey="clickSaveButton"/> + <waitForElement selector="{{inputFieldError}}" stepKey="waitForErrorField"/> + <waitForElementVisible selector="{{errorMessageSelector}}" stepKey="waitForErrorMessage"/> + <see selector="{{errorMessageSelector}}" userInput="{{errorMessage}}" stepKey="seeErrorMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/StorefrontSwitchDefaultStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/StorefrontSwitchDefaultStoreViewActionGroup.xml new file mode 100644 index 0000000000000..e3b3cc7c1a9ff --- /dev/null +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/StorefrontSwitchDefaultStoreViewActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontSwitchDefaultStoreViewActionGroup" extends="StorefrontSwitchStoreViewActionGroup"> + <annotations> + <description>EXTENDS: StorefrontSwitchStoreViewActionGroup. Clicks on the Default Store View.</description> + </annotations> + + <remove keyForRemoval="clickSelectStoreView"/> + <click selector="{{StorefrontHeaderSection.storeViewOption('default')}}" stepKey="clickSelectDefaultStoreView" after="waitForStoreViewDropdown"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/StorefrontSwitchStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/StorefrontSwitchStoreViewActionGroup.xml index 62ebdb6e8e9eb..9d8fb267910e1 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/StorefrontSwitchStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/StorefrontSwitchStoreViewActionGroup.xml @@ -15,19 +15,10 @@ <arguments> <argument name="storeView" defaultValue="customStore"/> </arguments> - + <click selector="{{StorefrontHeaderSection.storeViewSwitcher}}" stepKey="clickStoreViewSwitcher"/> <waitForElementVisible selector="{{StorefrontHeaderSection.storeViewDropdown}}" stepKey="waitForStoreViewDropdown"/> <click selector="{{StorefrontHeaderSection.storeViewOption(storeView.code)}}" stepKey="clickSelectStoreView"/> <waitForPageLoad stepKey="waitForPageLoad"/> </actionGroup> - - <actionGroup name="StorefrontSwitchDefaultStoreViewActionGroup" extends="StorefrontSwitchStoreViewActionGroup"> - <annotations> - <description>EXTENDS: StorefrontSwitchStoreViewActionGroup. Clicks on the Default Store View.</description> - </annotations> - - <remove keyForRemoval="clickSelectStoreView"/> - <click selector="{{StorefrontHeaderSection.storeViewOption('default')}}" stepKey="clickSelectDefaultStoreView" after="waitForStoreViewDropdown"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml index 982d829b57153..bdb1842cf2959 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml @@ -197,4 +197,13 @@ <data key="name">third_store_view</data> <data key="code">third_store_view</data> </entity> + <entity name="storeViewChinese" type="store"> + <data key="group_id">1</data> + <data key="name">Chinese</data> + <data key="code">chinese</data> + <data key="is_active">1</data> + <data key="store_id">null</data> + <data key="store_type">store</data> + <data key="store_action">add</data> + </entity> </entities> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StorePaymentMethodsData.xml b/app/code/Magento/Store/Test/Mftf/Data/StorePaymentMethodsData.xml index 912399142fa61..27a6150b95798 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StorePaymentMethodsData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StorePaymentMethodsData.xml @@ -5,7 +5,8 @@ * See COPYING.txt for license details. */ --> -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="PaymentMethodsSettingConfig" type="zero_subtotal_checkout_config_state"> <requiredEntity type="active">active</requiredEntity> <requiredEntity type="order_status">orderStatus</requiredEntity> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreShippingMethodsData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreShippingMethodsData.xml index 11b8931618f70..3b749928308bd 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreShippingMethodsData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreShippingMethodsData.xml @@ -5,7 +5,8 @@ * See COPYING.txt for license details. */ --> -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd"> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="FreeShippingMethodsSettingConfig" type="free_shipping_config_state"> <requiredEntity type="active">active</requiredEntity> </entity> diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/store_payment_methods-meta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/store_payment_methods-meta.xml index cbad7265cbbd6..1995dceb3bf0b 100644 --- a/app/code/Magento/Store/Test/Mftf/Metadata/store_payment_methods-meta.xml +++ b/app/code/Magento/Store/Test/Mftf/Metadata/store_payment_methods-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="EnableZeroSubtotalCheckoutConfigState" dataType="zero_subtotal_checkout_config_state" type="create" auth="adminFormKey" url="/admin/system_config/save/section/payment/" method="POST"> <object key="groups" dataType="zero_subtotal_checkout_config_state"> <object key="free" dataType="zero_subtotal_checkout_config_state"> diff --git a/app/code/Magento/Store/Test/Mftf/Metadata/store_shipping_methods-meta.xml b/app/code/Magento/Store/Test/Mftf/Metadata/store_shipping_methods-meta.xml index 6f88bca760204..091d0ae673f7a 100644 --- a/app/code/Magento/Store/Test/Mftf/Metadata/store_shipping_methods-meta.xml +++ b/app/code/Magento/Store/Test/Mftf/Metadata/store_shipping_methods-meta.xml @@ -7,7 +7,7 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataOperation.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> <operation name="EnableFreeShippingConfigState" dataType="free_shipping_config_state" type="create" auth="adminFormKey" url="/admin/system_config/save/section/carriers/" method="POST"> <object key="groups" dataType="free_shipping_config_state"> <object key="freeshipping" dataType="free_shipping_config_state"> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyAbsenceOfDeleteButtonTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyAbsenceOfDeleteButtonTest.xml index 7cd44d4d0ae88..5d4ac8de74680 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyAbsenceOfDeleteButtonTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyAbsenceOfDeleteButtonTest.xml @@ -39,7 +39,7 @@ </actionGroup> <!--Save the above store view and verify AssertStoreViewSuccessSaveMessage--> - <actionGroup ref="AdminCreateStoreViewActionSaveGroup" stepKey="verifyAssertStoreViewSuccessSaveMessage"/> + <actionGroup ref="AdminCreateStoreViewSaveActionGroup" stepKey="verifyAssertStoreViewSuccessSaveMessage"/> <!--Search store view(from above step) in grid--> <actionGroup ref="AssertStoreViewInGridActionGroup" stepKey="searchStoreViewInGrid"> @@ -56,4 +56,4 @@ <!--Go to store view form page and verify AssertStoreNoDeleteButton--> <dontSee selector="{{AdminNewStoreViewActionsSection.delete}}" stepKey="AssertStoreNoDeleteButton"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml index 18485abadc008..2ba8c675b3b2a 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateCustomStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml @@ -39,7 +39,7 @@ </actionGroup> <!--Save the above store view and verify AssertStoreViewSuccessSaveMessage--> - <actionGroup ref="AdminCreateStoreViewActionSaveGroup" stepKey="verifyAssertStoreViewSuccessSaveMessage"/> + <actionGroup ref="AdminCreateStoreViewSaveActionGroup" stepKey="verifyAssertStoreViewSuccessSaveMessage"/> <!--Search store view(from above step) in grid and verify AssertStoreInGrid--> <actionGroup ref="AssertStoreViewInGridActionGroup" stepKey="searchStoreViewInGrid"> @@ -66,4 +66,4 @@ <argument name="customStore" value="{{customStore.name}}"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateNewLocalizedStoreViewStatusEnabledTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateNewLocalizedStoreViewStatusEnabledTest.xml index 8e9fe1e71ceb4..6c60445107b28 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateNewLocalizedStoreViewStatusEnabledTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateNewLocalizedStoreViewStatusEnabledTest.xml @@ -33,11 +33,11 @@ </actionGroup> <!--Save the above store view and verify AssertStoreViewSuccessSaveMessage--> - <actionGroup ref="AdminCreateStoreViewActionSaveGroup" stepKey="verifyAssertStoreViewSuccessSaveMessage"/> + <actionGroup ref="AdminCreateStoreViewSaveActionGroup" stepKey="verifyAssertStoreViewSuccessSaveMessage"/> <!--Search store view(from above step) in grid and verify AssertStoreInGrid--> <actionGroup ref="AssertStoreViewInGridActionGroup" stepKey="searchStoreViewInGrid"> <argument name="storeViewName" value="{{storeViewGermany.name}}"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupTest.xml deleted file mode 100644 index e93fd62a74999..0000000000000 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupTest.xml +++ /dev/null @@ -1,47 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<!-- Test XML Example --> -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminCreateStoreGroupTest"> - <annotations> - <features value="Store"/> - <stories value="Create a store group in admin"/> - <title value="Admin should be able to create a store group"/> - <description value="Admin should be able to create a store group"/> - <group value="store"/> - <severity value="AVERAGE"/> - </annotations> - <before> - <createData stepKey="b1" entity="customStoreGroup"/> - <createData stepKey="b2" entity="customStoreGroup"/> - </before> - <after> - <actionGroup ref="logout" stepKey="adminLogout"/> - </after> - - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - - <amOnPage stepKey="s9" url="{{AdminSystemStorePage.url}}"/> - <waitForPageLoad stepKey="waitForPageLoad" /> - - <click stepKey="s11" selector="{{AdminStoresGridSection.resetButton}}"/> - <waitForPageLoad stepKey="s15" time="10"/> - - <fillField stepKey="s17" selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" userInput="$$b1.group[name]$$"/> - <click stepKey="s19" selector="{{AdminStoresGridSection.searchButton}}"/> - <waitForPageLoad stepKey="s21" time="10"/> - <see stepKey="s23" selector="{{AdminStoresGridSection.storeGrpNameInFirstRow}}" userInput="$$b1.group[name]$$"/> - - <click stepKey="s31" selector="{{AdminStoresGridSection.resetButton}}"/> - <waitForPageLoad stepKey="s35" time="10"/> - <fillField stepKey="s37" selector="{{AdminStoresGridSection.storeGrpFilterTextField}}" userInput="$$b2.group[name]$$"/> - <click stepKey="s39" selector="{{AdminStoresGridSection.searchButton}}"/> - <waitForPageLoad stepKey="s41" time="10"/> - <see stepKey="s43" selector="{{AdminStoresGridSection.storeGrpNameInFirstRow}}" userInput="$$b2.group[name]$$"/> - </test> -</tests> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndDefaultCategoryTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndDefaultCategoryTest.xml index 8e8f31eaca865..4892556929f80 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndDefaultCategoryTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndDefaultCategoryTest.xml @@ -34,19 +34,19 @@ </after> <!--Create custom store group with custom website and default category and verify AssertStoreGroupSuccessSaveMessage--> - <actionGroup ref="CreateCustomStore" stepKey="createCustomStoreGroup"> + <actionGroup ref="CreateCustomStoreActionGroup" stepKey="createCustomStoreGroup"> <argument name="website" value="{{customWebsite.name}}"/> <argument name="store" value="{{customStoreGroup.name}}"/> <argument name="rootCategory" value="Default Category"/> </actionGroup> <!--Search created store group(from above step) in grid and verify AssertStoreGroupInGrid message--> - <actionGroup ref="AssertStoreGroupInGrid" stepKey="seeCreatedStoreGroupInGrid"> + <actionGroup ref="AssertStoreGroupInGridActionGroup" stepKey="seeCreatedStoreGroupInGrid"> <argument name="storeGroupName" value="{{customStoreGroup.name}}"/> </actionGroup> <!--Go to store group form page and verify AssertStoreGroupForm--> - <actionGroup ref="AssertStoreGroupForm" stepKey="seeCreatedStoreGroupForm"> + <actionGroup ref="AssertStoreGroupFormActionGroup" stepKey="seeCreatedStoreGroupForm"> <argument name="website" value="{{customWebsite.name}}"/> <argument name="storeGroupName" value="{{customStoreGroup.name}}"/> <argument name="storeGroupCode" value="{{customStoreGroup.code}}"/> @@ -55,4 +55,4 @@ <!--Also verify absence of delete button on store group form page(AssertStoreGroupNoDeleteButton)--> <dontSee selector="{{AdminStoresMainActionsSection.deleteButton}}" stepKey="AssertStoreGroupNoDeleteButton"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndRootCategoryTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndRootCategoryTest.xml index 18f9822145dec..27037f45f3835 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndRootCategoryTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithCustomWebsiteAndRootCategoryTest.xml @@ -42,23 +42,23 @@ </after> <!--Create custom store group with custom website and root category and verify AssertStoreGroupSuccessSaveMessage--> - <actionGroup ref="CreateCustomStore" stepKey="createCustomStoreGroup"> + <actionGroup ref="CreateCustomStoreActionGroup" stepKey="createCustomStoreGroup"> <argument name="website" value="{{customWebsite.name}}"/> <argument name="store" value="{{customStoreGroup.name}}"/> <argument name="rootCategory" value="$$rootCategory.name$$"/> </actionGroup> <!--Search created store group(from above step) in grid and verify AssertStoreGroupInGrid--> - <actionGroup ref="AssertStoreGroupInGrid" stepKey="seeCreatedStoreGroupInGrid"> + <actionGroup ref="AssertStoreGroupInGridActionGroup" stepKey="seeCreatedStoreGroupInGrid"> <argument name="storeGroupName" value="{{customStoreGroup.name}}"/> </actionGroup> <!--Go to store group form page and verify AssertStoreGroupForm and AssertStoreGroupOnStoreViewForm--> - <actionGroup ref="AssertStoreGroupForm" stepKey="seeCreatedStoreGroupInForm"> + <actionGroup ref="AssertStoreGroupFormActionGroup" stepKey="seeCreatedStoreGroupInForm"> <argument name="website" value="{{customWebsite.name}}"/> <argument name="storeGroupName" value="{{customStoreGroup.name}}"/> <argument name="storeGroupCode" value="{{customStoreGroup.code}}"/> <argument name="rootCategory" value="$$rootCategory.name$$"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithDefaultWebsiteAndDefaultCategoryTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithDefaultWebsiteAndDefaultCategoryTest.xml index ddc5d061c1db2..0db65501b4112 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithDefaultWebsiteAndDefaultCategoryTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreGroupWithDefaultWebsiteAndDefaultCategoryTest.xml @@ -36,16 +36,16 @@ </actionGroup> <!--Search created store group(from above step) in grid and verify AssertStoreGroupInGrid--> - <actionGroup ref="AssertStoreGroupInGrid" stepKey="seeCreatedStoreGroupInGrid"> + <actionGroup ref="AssertStoreGroupInGridActionGroup" stepKey="seeCreatedStoreGroupInGrid"> <argument name="storeGroupName" value="{{SecondStoreGroupUnique.name}}"/> </actionGroup> <!--Go to store group form page and verify AssertStoreGroupForm and AssertStoreGroupOnStoreViewForm--> - <actionGroup ref="AssertStoreGroupForm" stepKey="seeCreatedStoreGroupForm"> + <actionGroup ref="AssertStoreGroupFormActionGroup" stepKey="seeCreatedStoreGroupForm"> <argument name="website" value="{{_defaultWebsite.name}}"/> <argument name="storeGroupName" value="{{SecondStoreGroupUnique.name}}"/> <argument name="storeGroupCode" value="{{SecondStoreGroupUnique.code}}"/> <argument name="rootCategory" value="Default Category"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusDisabledVerifyBackendAndFrontendTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusDisabledVerifyBackendAndFrontendTest.xml index 6ccab965b7727..2d8fbc72263d4 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusDisabledVerifyBackendAndFrontendTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusDisabledVerifyBackendAndFrontendTest.xml @@ -27,14 +27,14 @@ </after> <!--Create store view--> - <actionGroup ref="CreateStoreView" stepKey="createStoreView"> + <actionGroup ref="CreateStoreViewActionGroup" stepKey="createStoreView"> <argument name="storeView" value="storeViewDataDisabled"/> <argument name="storeGroupName" value="_defaultStoreGroup.name"/> <argument name="storeViewStatus" value="storeViewDataDisabled.is_active"/> </actionGroup> <!--Save the above store view and verify AssertStoreViewSuccessSaveMessage--> - <actionGroup ref="AdminCreateStoreViewActionSaveGroup" stepKey="verifyAssertStoreViewSuccessSaveMessage"/> + <actionGroup ref="AdminCreateStoreViewSaveActionGroup" stepKey="verifyAssertStoreViewSuccessSaveMessage"/> <!--Go to store configuration page and verify AssertStoreBackend--> <actionGroup ref="AssertStoreConfigurationBackendActionGroup" stepKey="verifyValuesOnStoreBackend"> @@ -46,4 +46,4 @@ <argument name="store" value="{{storeViewDataDisabled.name}"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml index c2f12bd2673f7..150e1082352cf 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateStoreViewStatusEnabledVerifyBackendAndFrontendTest.xml @@ -33,7 +33,7 @@ </actionGroup> <!--Save the above store view and verify AssertStoreViewSuccessSaveMessage--> - <actionGroup ref="AdminCreateStoreViewActionSaveGroup" stepKey="verifyAssertStoreViewSuccessSaveMessage"/> + <actionGroup ref="AdminCreateStoreViewSaveActionGroup" stepKey="verifyAssertStoreViewSuccessSaveMessage"/> <!--Go to store configuration page and verify AssertStoreBackend--> <actionGroup ref="AssertStoreConfigurationBackendActionGroup" stepKey="verifyValuesOnStoreBackend"> @@ -45,4 +45,4 @@ <argument name="store" value="{{storeViewData.name}}"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml index 1608d0b7b5a25..a8782acb1eb07 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateWebsiteTest.xml @@ -19,7 +19,7 @@ </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> @@ -35,12 +35,12 @@ </actionGroup> <!--Search created website in grid and verify AssertWebsiteInGrid--> - <actionGroup ref="AssertWebsiteInGrid" stepKey="seeWebsiteInGrid"> + <actionGroup ref="AssertWebsiteInGridActionGroup" stepKey="seeWebsiteInGrid"> <argument name="websiteName" value="{{customWebsite.name}}"/> </actionGroup> <!--Verify website name and websitecode on website form (AssertWebsiteForm and AssertWebsiteOnStoreForm)--> - <actionGroup ref="AssertWebsiteForm" stepKey="seeWebsiteForm"> + <actionGroup ref="AssertWebsiteFormActionGroup" stepKey="seeWebsiteForm"> <argument name="websiteName" value="{{customWebsite.name}}"/> <argument name="websiteCode" value="{{customWebsite.code}}"/> </actionGroup> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreGroupTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreGroupTest.xml index 652537f7864cd..185cf87531d9a 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreGroupTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreGroupTest.xml @@ -39,7 +39,7 @@ </actionGroup> <!--Verify deleted Store group is not present in grid and verify AssertStoreGroupNotInGrid message--> - <actionGroup ref="AssertStoreNotInGrid" stepKey="verifyDeletedStoreGroupNotInGrid"> + <actionGroup ref="AssertStoreNotInGridActionGroup" stepKey="verifyDeletedStoreGroupNotInGrid"> <argument name="storeGroupName" value="{{customStore.name}}"/> </actionGroup> @@ -52,4 +52,4 @@ <argument name="backup" value="WebSetupWizardBackup"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml index fc1dcb5ee1a24..df6fc391b2972 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminDeleteStoreViewTest.xml @@ -37,7 +37,7 @@ </actionGroup> <!--Verify deleted store view not present in grid and verify AssertStoreNotInGrid Message--> - <actionGroup ref="AssertStoreViewNotInGrid" stepKey="verifyDeletedStoreViewNotInGrid"> + <actionGroup ref="AssertStoreViewNotInGridActionGroup" stepKey="verifyDeletedStoreViewNotInGrid"> <argument name="storeViewName" value="{{storeViewData.name}}"/> </actionGroup> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminMoveStoreToOtherGroupSameWebsiteTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminMoveStoreToOtherGroupSameWebsiteTest.xml index b86b99936dbe2..f1983dca53bf7 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminMoveStoreToOtherGroupSameWebsiteTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminMoveStoreToOtherGroupSameWebsiteTest.xml @@ -62,7 +62,7 @@ <argument name="storeDropdown" value="{{customStore.name}}"/> </actionGroup> <!--Save the above store view and verify AssertStoreViewSuccessSaveMessage--> - <actionGroup ref="AdminCreateStoreViewActionSaveGroup" stepKey="verifyAssertStoreViewSuccessSaveMessage"/> + <actionGroup ref="AdminCreateStoreViewSaveActionGroup" stepKey="verifyAssertStoreViewSuccessSaveMessage"/> <!--Search moved store view(from above step) in grid and verify AssertStoreInGrid--> <actionGroup ref="AssertStoreViewInGridActionGroup" stepKey="searchMovedStoreViewInGrid"> @@ -92,4 +92,4 @@ <argument name="storeView2" value="{{storeViewData2.name}}"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAcceptAlertAndVerifyStoreViewFormTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAcceptAlertAndVerifyStoreViewFormTest.xml index 9c84388d86f99..16f830224f7f4 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAcceptAlertAndVerifyStoreViewFormTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAcceptAlertAndVerifyStoreViewFormTest.xml @@ -31,7 +31,7 @@ <argument name="websiteCode" value="{{customWebsite.code}}"/> </actionGroup> <!--Create custom store group--> - <actionGroup ref="CreateCustomStore" stepKey="createCustomStoreGroup"> + <actionGroup ref="CreateCustomStoreActionGroup" stepKey="createCustomStoreGroup"> <argument name="website" value="{{_defaultWebsite.name}}"/> <argument name="store" value="{{staticStoreGroup.name}}"/> <argument name="rootCategory" value="Default Category"/> @@ -73,4 +73,4 @@ <argument name="rootCategory" value="$$rootCategory.name$$"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAndVerifyStoreViewFormTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAndVerifyStoreViewFormTest.xml index 3d85a34901434..ab204560f11c6 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAndVerifyStoreViewFormTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreGroupAndVerifyStoreViewFormTest.xml @@ -44,7 +44,7 @@ <click selector="{{AdminStoresGridSection.firstRow}}" stepKey="clickFirstRow"/> <waitForPageLoad stepKey="AdminSystemStoreGroupPageToOpen"/> <!--Update created Store group as per requirement--> - <actionGroup ref="CreateCustomStore" stepKey="createNewCustomStoreGroup"> + <actionGroup ref="CreateCustomStoreActionGroup" stepKey="createNewCustomStoreGroup"> <argument name="website" value="{{_defaultWebsite.name}}"/> <argument name="store" value="{{customStoreGroup.name}}"/> <argument name="rootCategory" value="Default Category"/> @@ -63,4 +63,4 @@ <argument name="rootCategory" value="Default Category"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreViewTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreViewTest.xml index 054ee789fbdc5..26dee6c632928 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreViewTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateStoreViewTest.xml @@ -47,7 +47,7 @@ <argument name="customStore" value="SecondStoreUnique"/> </actionGroup> <!--Save the updated Store view and verify AssertStoreViewSuccessSaveMessage--> - <actionGroup ref="AdminCreateStoreViewActionSaveGroup" stepKey="verifyAssertStoreViewSuccessSaveMessage"> + <actionGroup ref="AdminCreateStoreViewSaveActionGroup" stepKey="verifyAssertStoreViewSuccessSaveMessage"> </actionGroup> <!--Search updated store view in grid and verify AssertStoreViewInGridMessage--> @@ -78,4 +78,4 @@ <see selector="{{StorefrontHeaderSection.storeViewDropdown}}" userInput="{{storeViewData.name}}" stepKey="seeAssertStoreViewOnStorefront"/> <see selector="{{StorefrontHeaderSection.storeViewDropdown}}" userInput="{{SecondStoreUnique.name}}" stepKey="seeAssertUpdatedStoreViewOnStorefront"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateWebsiteTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateWebsiteTest.xml index 6b666126569ae..5c4ecb87dda53 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateWebsiteTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/AdminUpdateWebsiteTest.xml @@ -34,7 +34,7 @@ </after> <!--Search created custom website in grid--> - <actionGroup ref="AssertWebsiteInGrid" stepKey="seeWebsiteInGrid"> + <actionGroup ref="AssertWebsiteInGridActionGroup" stepKey="seeWebsiteInGrid"> <argument name="websiteName" value="{{customWebsite.name}}"/> </actionGroup> <click selector="{{AdminStoresGridSection.websiteName(customWebsite.name)}}" stepKey="clickWebsiteFirstRowInGrid"/> @@ -46,14 +46,14 @@ </actionGroup> <!--Search updated custom website(from above step) in grid and verify AssertWebsiteInGrid--> - <actionGroup ref="AssertWebsiteInGrid" stepKey="seeUpdatedWebsiteInGrid"> + <actionGroup ref="AssertWebsiteInGridActionGroup" stepKey="seeUpdatedWebsiteInGrid"> <argument name="websiteName" value="{{updateCustomWebsite.name}}"/> </actionGroup> <!--Verify updated website name and updated websitecode on website form (AssertWebsiteForm and AssertWebsiteOnStoreForm)--> - <actionGroup ref="AssertWebsiteForm" stepKey="seeUpdatedWebsiteForm"> + <actionGroup ref="AssertWebsiteFormActionGroup" stepKey="seeUpdatedWebsiteForm"> <argument name="websiteName" value="{{updateCustomWebsite.name}}"/> <argument name="websiteCode" value="{{updateCustomWebsite.code}}"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Store/etc/di.xml b/app/code/Magento/Store/etc/di.xml index 62f6f41424025..8f4151b8fc966 100644 --- a/app/code/Magento/Store/etc/di.xml +++ b/app/code/Magento/Store/etc/di.xml @@ -436,7 +436,7 @@ <item name="cleanTargetUrl" xsi:type="object">Magento\Store\Model\StoreSwitcher\CleanTargetUrl</item> <item name="manageStoreCookie" xsi:type="object">Magento\Store\Model\StoreSwitcher\ManageStoreCookie</item> <item name="managePrivateContent" xsi:type="object">Magento\Store\Model\StoreSwitcher\ManagePrivateContent</item> - <item name="hashGenerator" xsi:type="object">Magento\Store\Model\StoreSwitcher\HashGenerator</item> + <item name="hashProcessor" xsi:type="object" sortOrder="1000">Magento\Store\Model\StoreSwitcher\HashProcessor</item> </argument> </arguments> </type> diff --git a/app/code/Magento/Store/registration.php b/app/code/Magento/Store/registration.php index 62be25fbdf6ab..64b5f9df52c0c 100644 --- a/app/code/Magento/Store/registration.php +++ b/app/code/Magento/Store/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Store', __DIR__); diff --git a/app/code/Magento/StoreGraphQl/Controller/HttpRequestValidator/StoreValidator.php b/app/code/Magento/StoreGraphQl/Controller/HttpRequestValidator/StoreValidator.php index 144905d728141..5d0a4edd44b73 100644 --- a/app/code/Magento/StoreGraphQl/Controller/HttpRequestValidator/StoreValidator.php +++ b/app/code/Magento/StoreGraphQl/Controller/HttpRequestValidator/StoreValidator.php @@ -42,16 +42,30 @@ public function validate(HttpRequestInterface $request): void { $headerValue = $request->getHeader('Store'); if (!empty($headerValue)) { - $storeCode = ltrim(rtrim($headerValue)); - $stores = $this->storeManager->getStores(false, true); - if (!isset($stores[$storeCode])) { - if (strtolower($storeCode) !== 'default') { - $this->storeManager->setCurrentStore(null); - throw new GraphQlInputException( - __("Requested store is not found") - ); - } + $storeCode = trim($headerValue); + if (!$this->isStoreActive($storeCode)) { + $this->storeManager->setCurrentStore(null); + throw new GraphQlInputException(__('Requested store is not found')); } } } + + /** + * Check if provided store code corresponds to an active store + * + * @param string $storeCode + * @return bool + */ + private function isStoreActive(string $storeCode): bool + { + $stores = $this->storeManager->getStores(false, true); + if (strtolower($storeCode) === 'default') { + return true; + } + if (isset($stores[$storeCode])) { + return (bool)$stores[$storeCode]->getIsActive(); + } + + return false; + } } diff --git a/app/code/Magento/StoreGraphQl/etc/schema.graphqls b/app/code/Magento/StoreGraphQl/etc/schema.graphqls index aaef3aa13dbaf..919c94684eb21 100644 --- a/app/code/Magento/StoreGraphQl/etc/schema.graphqls +++ b/app/code/Magento/StoreGraphQl/etc/schema.graphqls @@ -4,13 +4,13 @@ type Query { storeConfig : StoreConfig @resolver(class: "Magento\\StoreGraphQl\\Model\\Resolver\\StoreConfigResolver") @doc(description: "The store config query") @cache(cacheable: false) } -type Website @doc(description: "The type contains information about a website") { - id : Int @doc(description: "The ID number assigned to the website") - name : String @doc(description: "The website name. Websites use this name to identify it easier.") - code : String @doc(description: "A code assigned to the website to identify it") - sort_order : Int @doc(description: "The attribute to use for sorting websites") - default_group_id : String @doc(description: "The default group ID that the website has") - is_default : Boolean @doc(description: "Specifies if this is the default website") +type Website @doc(description: "Website is deprecated because it is should not be used on storefront. The type contains information about a website") { + id : Int @deprecated(reason: "The field should not be used on the storefront.") @doc(description: "The ID number assigned to the website") + name : String @deprecated(reason: "The field should not be used on the storefront.") @doc(description: "The website name. Websites use this name to identify it easier.") + code : String @deprecated(reason: "The field should not be used on the storefront.") @doc(description: "A code assigned to the website to identify it") + sort_order : Int @deprecated(reason: "The field should not be used on the storefront.") @doc(description: "The attribute to use for sorting websites") + default_group_id : String @deprecated(reason: "The field should not be used on the storefront.") @doc(description: "The default group ID that the website has") + is_default : Boolean @deprecated(reason: "The field should not be used on the storefront.") @doc(description: "Specifies if this is the default website") } type StoreConfig @doc(description: "The type contains information about a store config") { diff --git a/app/code/Magento/Swagger/registration.php b/app/code/Magento/Swagger/registration.php index 7d7b8d2662a1f..562f7d2e5196d 100644 --- a/app/code/Magento/Swagger/registration.php +++ b/app/code/Magento/Swagger/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Swagger', __DIR__); diff --git a/app/code/Magento/Swagger/view/frontend/layout/swagger_index_index.xml b/app/code/Magento/Swagger/view/frontend/layout/swagger_index_index.xml index 5a592b9b7c987..059b9ad445806 100644 --- a/app/code/Magento/Swagger/view/frontend/layout/swagger_index_index.xml +++ b/app/code/Magento/Swagger/view/frontend/layout/swagger_index_index.xml @@ -30,6 +30,7 @@ <!--Remove Magento page content--> <referenceContainer name="page.wrapper" remove="true"/> <referenceBlock name="translate" remove="true"/> + <referenceBlock name="theme.active.editor" remove="true" /> <referenceBlock name="requirejs-config" remove="true"/> <referenceContainer name="root"> <block name="swaggerUiContent" class="Magento\Swagger\Block\Index" template="Magento_Swagger::swagger-ui/index.phtml" /> diff --git a/app/code/Magento/SwaggerWebapi/registration.php b/app/code/Magento/SwaggerWebapi/registration.php index 595497df7cfeb..bc551c2ea3cd5 100644 --- a/app/code/Magento/SwaggerWebapi/registration.php +++ b/app/code/Magento/SwaggerWebapi/registration.php @@ -6,6 +6,6 @@ declare(strict_types=1); -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_SwaggerWebapi', __DIR__); diff --git a/app/code/Magento/SwaggerWebapiAsync/registration.php b/app/code/Magento/SwaggerWebapiAsync/registration.php index 8805b81ca28a1..6af92fe040fd5 100644 --- a/app/code/Magento/SwaggerWebapiAsync/registration.php +++ b/app/code/Magento/SwaggerWebapiAsync/registration.php @@ -6,6 +6,6 @@ declare(strict_types=1); -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_SwaggerWebapiAsync', __DIR__); diff --git a/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php b/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php index 2e4980c2fbfd0..6e0a1e8d01360 100644 --- a/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php +++ b/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php @@ -9,7 +9,6 @@ use Magento\Catalog\Helper\Product as CatalogProduct; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Layer\Resolver; -use Magento\Catalog\Model\Layer\Category as CategoryLayer; use Magento\ConfigurableProduct\Helper\Data; use Magento\ConfigurableProduct\Model\ConfigurableAttributeData; use Magento\Customer\Helper\Session\CurrentCustomer; @@ -154,7 +153,7 @@ public function getJsonConfig() $this->unsetData('allow_products'); return parent::getJsonConfig(); } - + /** * Composes configuration for js price format * @@ -242,9 +241,12 @@ private function getLayeredAttributesIfExists(Product $configurableProduct, arra $layeredAttributes = []; - $configurableAttributes = array_map(function ($attribute) { - return $attribute->getAttributeCode(); - }, $configurableAttributes); + $configurableAttributes = array_map( + function ($attribute) { + return $attribute->getAttributeCode(); + }, + $configurableAttributes + ); $commonAttributeCodes = array_intersect( $configurableAttributes, @@ -257,16 +259,4 @@ private function getLayeredAttributesIfExists(Product $configurableProduct, arra return $layeredAttributes; } - - /** - * @inheritdoc - */ - public function getCacheKeyInfo() - { - $cacheKeyInfo = parent::getCacheKeyInfo(); - /** @var CategoryLayer $catalogLayer */ - $catalogLayer = $this->layerResolver->get(); - $cacheKeyInfo[] = $catalogLayer->getStateKey(); - return $cacheKeyInfo; - } } diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddSwatchToProductActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddSwatchToProductActionGroup.xml deleted file mode 100644 index 4a67c0dfbe8e4..0000000000000 --- a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddSwatchToProductActionGroup.xml +++ /dev/null @@ -1,132 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AddVisualSwatchToProductActionGroup"> - <annotations> - <description>Adds the provided Visual Swatch Attribute and Options (2) to a Product on the Admin Product creation/edit page. Clicks on Save. Validates that the Success Message is present.</description> - </annotations> - <arguments> - <argument name="attribute" defaultValue="visualSwatchAttribute"/> - <argument name="option1" defaultValue="visualSwatchOption1"/> - <argument name="option2" defaultValue="visualSwatchOption2"/> - </arguments> - - <seeInCurrentUrl url="{{ProductCatalogPage.url}}" stepKey="seeOnProductEditPage"/> - <conditionalClick selector="{{AdminProductFormConfigurationsSection.sectionHeader}}" dependentSelector="{{AdminProductFormConfigurationsSection.createConfigurations}}" visible="false" stepKey="openConfigurationSection"/> - <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="openConfigurationPanel"/> - <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewAttribute}}" stepKey="waitForSlideOut"/> - <click selector="{{AdminCreateProductConfigurationsPanel.createNewAttribute}}" stepKey="clickCreateNewAttribute"/> - <waitForPageLoad stepKey="waitForIFrame"/> - - <switchToIFrame selector="{{AdminNewAttributePanel.newAttributeIFrame}}" stepKey="switchToNewAttributeIFrame"/> - <fillField selector="{{AdminNewAttributePanel.defaultLabel}}" userInput="{{attribute.default_label}}" stepKey="fillDefaultLabel"/> - <selectOption selector="{{AdminNewAttributePanel.inputType}}" userInput="{{attribute.input_type}}" stepKey="selectInputType"/> - <!--Add swatch options--> - <click selector="{{AdminNewAttributePanel.addVisualSwatchOption}}" stepKey="clickAddSwatch1"/> - <waitForElementVisible selector="{{AdminNewAttributePanel.visualSwatchOptionAdminValue('0')}}" stepKey="waitForOption1Row"/> - <fillField selector="{{AdminNewAttributePanel.visualSwatchOptionAdminValue('0')}}" userInput="{{option1.admin_label}}" stepKey="fillAdminLabel1"/> - <fillField selector="{{AdminNewAttributePanel.visualSwatchOptionDefaultStoreValue('0')}}" userInput="{{option1.default_label}}" stepKey="fillDefaultStoreLabel1"/> - <click selector="{{AdminNewAttributePanel.addVisualSwatchOption}}" stepKey="clickAddSwatch2"/> - <waitForElementVisible selector="{{AdminNewAttributePanel.visualSwatchOptionAdminValue('1')}}" stepKey="waitForOption2Row"/> - <fillField selector="{{AdminNewAttributePanel.visualSwatchOptionAdminValue('1')}}" userInput="{{option2.admin_label}}" stepKey="fillAdminLabel2"/> - <fillField selector="{{AdminNewAttributePanel.visualSwatchOptionDefaultStoreValue('1')}}" userInput="{{option2.default_label}}" stepKey="fillDefaultStoreLabel2"/> - - <!--Save attribute--> - <click selector="{{AdminNewAttributePanel.saveAttribute}}" stepKey="clickOnNewAttributePanel"/> - <waitForPageLoad stepKey="waitForSaveAttribute"/> - <switchToIFrame stepKey="switchOutOfIFrame"/> - - <!--Find attribute in grid and select--> - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="clickOnFilters"/> - <fillField selector="{{AdminDataGridHeaderSection.attributeCodeFilterInput}}" userInput="{{attribute.default_label}}" stepKey="fillFilterAttributeCodeField"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> - <click selector="{{AdminDataGridTableSection.rowCheckbox('1')}}" stepKey="clickOnFirstCheckbox"/> - - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextStep1"/> - - <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute(attribute.default_label)}}" stepKey="clickSelectAll"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextStep2"/> - - <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> - <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="100" stepKey="enterAttributeQuantity"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextStep3"/> - <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="generateProducts"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> - <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/> - <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSaveProductMessage"/> - </actionGroup> - - <actionGroup name="AddVisualSwatchToProductWithStorefrontConfigActionGroup" extends="AddVisualSwatchToProductActionGroup"> - <annotations> - <description>EXTENDS: AddVisualSwatchToProductActionGroup. Add the provided Visual Swatch Attribute and Options (2) to a Product with Storefront Configurations.</description> - </annotations> - <arguments> - <argument name="attribute" defaultValue="visualSwatchAttribute"/> - <argument name="option1" defaultValue="visualSwatchOption1"/> - <argument name="option2" defaultValue="visualSwatchOption2"/> - </arguments> - - <!-- Go to Storefront Properties tab --> - <click selector="{{AdminNewAttributePanel.storefrontPropertiesTab}}" stepKey="goToStorefrontPropertiesTab" after="fillDefaultStoreLabel2"/> - <waitForElementVisible selector="{{AdminNewAttributePanel.storefrontPropertiesTitle}}" stepKey="waitTabLoad" after="goToStorefrontPropertiesTab"/> - <selectOption selector="{{AdminNewAttributePanel.useInSearch}}" stepKey="switchOnUsInSearch" userInput="Yes" after="waitTabLoad"/> - <selectOption selector="{{AdminNewAttributePanel.visibleInAdvancedSearch}}" stepKey="switchOnVisibleInAdvancedSearch" userInput="Yes" after="switchOnUsInSearch"/> - <selectOption selector="{{AdminNewAttributePanel.comparableOnStorefront}}" stepKey="switchOnComparableOnStorefront" userInput="Yes" after="switchOnVisibleInAdvancedSearch"/> - <selectOption selector="{{AdminNewAttributePanel.useInLayeredNavigation}}" stepKey="selectUseInLayer" userInput="Filterable (with results)" after="switchOnComparableOnStorefront"/> - <selectOption selector="{{AdminNewAttributePanel.visibleOnCatalogPagesOnStorefront}}" stepKey="switchOnVisibleOnCatalogPagesOnStorefront" userInput="Yes" after="selectUseInLayer"/> - <selectOption selector="{{AdminNewAttributePanel.useInProductListing}}" stepKey="switchOnUsedInProductListing" userInput="Yes" after="switchOnVisibleOnCatalogPagesOnStorefront"/> - </actionGroup> - - <actionGroup name="AddVisualSwatchWithProductWithStorefrontPreviewImageConfigActionGroup" extends="AddVisualSwatchToProductActionGroup"> - <selectOption selector="{{AdminNewAttributePanel.updateProductPreviewImage}}" userInput="Yes" stepKey="selectUpdatePreviewImage" after="selectInputType"/> - <click selector="{{AdminNewAttributePanel.storefrontPropertiesTab}}" stepKey="goToStorefrontPropertiesTab" after="fillDefaultStoreLabel2"/> - <waitForElementVisible selector="{{AdminNewAttributePanel.storefrontPropertiesTitle}}" after="goToStorefrontPropertiesTab" stepKey="waitTabLoad"/> - <selectOption selector="{{AdminNewAttributePanel.useInLayeredNavigation}}" userInput="Filterable (with results)" stepKey="selectUseInLayer" after="waitTabLoad"/> - <selectOption selector="{{AdminNewAttributePanel.useInProductListing}}" userInput="Yes" stepKey="switchOnUsedInProductListing" after="selectUseInLayer"/> - <selectOption selector="{{AdminNewAttributePanel.usedForStoringInProductListing}}" userInput="Yes" stepKey="switchOnUsedForStoringInProductListing" after="switchOnUsedInProductListing"/> - </actionGroup> - - <actionGroup name="AddTextSwatchToProductActionGroup"> - <annotations> - <description>Add text swatch property attribute.</description> - </annotations> - <arguments> - <argument name="attributeName" defaultValue="{{textSwatchAttribute.default_label}}" type="string"/> - <argument name="attributeCode" defaultValue="{{textSwatchAttribute.attribute_code}}" type="string"/> - <argument name="option1" defaultValue="textSwatchOption1" type="string"/> - <argument name="option2" defaultValue="textSwatchOption2" type="string"/> - <argument name="option3" defaultValue="textSwatchOption3" type="string"/> - <argument name="usedInProductListing" defaultValue="No" type="string"/> - </arguments> - - <amOnPage url="{{ProductAttributePage.url}}" stepKey="goToNewProductAttributePage"/> - <waitForPageLoad stepKey="waitForNewProductAttributePage"/> - <fillField selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{attributeName}}" stepKey="fillDefaultLabel"/> - <selectOption selector="{{AttributePropertiesSection.InputType}}" userInput="{{textSwatchAttribute.input_type}}" stepKey="selectInputType"/> - <click selector="{{AdminManageSwatchSection.addSwatchText}}" stepKey="clickAddSwatch1"/> - <fillField selector="{{AdminManageSwatchSection.swatchTextByIndex('0')}}" userInput="{{option1}}" stepKey="fillSwatch1"/> - <fillField selector="{{AdminManageSwatchSection.swatchAdminDescriptionByIndex('0')}}" userInput="{{option1}}" stepKey="fillSwatch1Description"/> - <click selector="{{AdminManageSwatchSection.addSwatchText}}" stepKey="clickAddSwatch2"/> - <fillField selector="{{AdminManageSwatchSection.swatchTextByIndex('1')}}" userInput="{{option2}}" stepKey="fillSwatch2"/> - <fillField selector="{{AdminManageSwatchSection.swatchAdminDescriptionByIndex('1')}}" userInput="{{option2}}" stepKey="fillSwatch2Description"/> - <click selector="{{AdminManageSwatchSection.addSwatchText}}" stepKey="clickAddSwatch3"/> - <fillField selector="{{AdminManageSwatchSection.swatchTextByIndex('2')}}" userInput="{{option3}}" stepKey="fillSwatch3"/> - <fillField selector="{{AdminManageSwatchSection.swatchAdminDescriptionByIndex('2')}}" userInput="{{option3}}" stepKey="fillSwatch3Description"/> - <click selector="{{AttributePropertiesSection.AdvancedProperties}}" stepKey="expandAdvancedProperties"/> - <selectOption selector="{{AttributePropertiesSection.Scope}}" userInput="1" stepKey="selectGlobalScope"/> - <fillField selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{attributeCode}}" stepKey="fillAttributeCodeField"/> - <scrollToTopOfPage stepKey="scrollToTabs"/> - <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="clickStorefrontPropertiesTab"/> - <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.UseInProductListing}}" stepKey="waitForTabSwitch"/> - <selectOption selector="{{AdvancedAttributePropertiesSection.UseInProductListing}}" userInput="{{usedInProductListing}}" stepKey="useInProductListing"/> - <click selector="{{AttributePropertiesSection.SaveAndEdit}}" stepKey="clickSave"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddTextSwatchToProductActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddTextSwatchToProductActionGroup.xml new file mode 100644 index 0000000000000..97a391137d8e3 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddTextSwatchToProductActionGroup.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddTextSwatchToProductActionGroup"> + <annotations> + <description>Add text swatch property attribute.</description> + </annotations> + <arguments> + <argument name="attributeName" defaultValue="{{textSwatchAttribute.default_label}}" type="string"/> + <argument name="attributeCode" defaultValue="{{textSwatchAttribute.attribute_code}}" type="string"/> + <argument name="option1" defaultValue="textSwatchOption1" type="string"/> + <argument name="option2" defaultValue="textSwatchOption2" type="string"/> + <argument name="option3" defaultValue="textSwatchOption3" type="string"/> + <argument name="usedInProductListing" defaultValue="No" type="string"/> + </arguments> + + <amOnPage url="{{ProductAttributePage.url}}" stepKey="goToNewProductAttributePage"/> + <waitForPageLoad stepKey="waitForNewProductAttributePage"/> + <fillField selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{attributeName}}" stepKey="fillDefaultLabel"/> + <selectOption selector="{{AttributePropertiesSection.InputType}}" userInput="{{textSwatchAttribute.input_type}}" stepKey="selectInputType"/> + <click selector="{{AdminManageSwatchSection.addSwatchText}}" stepKey="clickAddSwatch1"/> + <fillField selector="{{AdminManageSwatchSection.swatchTextByIndex('0')}}" userInput="{{option1}}" stepKey="fillSwatch1"/> + <fillField selector="{{AdminManageSwatchSection.swatchAdminDescriptionByIndex('0')}}" userInput="{{option1}}" stepKey="fillSwatch1Description"/> + <click selector="{{AdminManageSwatchSection.addSwatchText}}" stepKey="clickAddSwatch2"/> + <fillField selector="{{AdminManageSwatchSection.swatchTextByIndex('1')}}" userInput="{{option2}}" stepKey="fillSwatch2"/> + <fillField selector="{{AdminManageSwatchSection.swatchAdminDescriptionByIndex('1')}}" userInput="{{option2}}" stepKey="fillSwatch2Description"/> + <click selector="{{AdminManageSwatchSection.addSwatchText}}" stepKey="clickAddSwatch3"/> + <fillField selector="{{AdminManageSwatchSection.swatchTextByIndex('2')}}" userInput="{{option3}}" stepKey="fillSwatch3"/> + <fillField selector="{{AdminManageSwatchSection.swatchAdminDescriptionByIndex('2')}}" userInput="{{option3}}" stepKey="fillSwatch3Description"/> + <click selector="{{AttributePropertiesSection.AdvancedProperties}}" stepKey="expandAdvancedProperties"/> + <selectOption selector="{{AttributePropertiesSection.Scope}}" userInput="1" stepKey="selectGlobalScope"/> + <fillField selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{attributeCode}}" stepKey="fillAttributeCodeField"/> + <scrollToTopOfPage stepKey="scrollToTabs"/> + <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="clickStorefrontPropertiesTab"/> + <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.UseInProductListing}}" stepKey="waitForTabSwitch"/> + <selectOption selector="{{AdvancedAttributePropertiesSection.UseInProductListing}}" userInput="{{usedInProductListing}}" stepKey="useInProductListing"/> + <click selector="{{AttributePropertiesSection.SaveAndEdit}}" stepKey="clickSave"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddVisualSwatchToProductActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddVisualSwatchToProductActionGroup.xml new file mode 100644 index 0000000000000..670c343b609cf --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddVisualSwatchToProductActionGroup.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddVisualSwatchToProductActionGroup"> + <annotations> + <description>Adds the provided Visual Swatch Attribute and Options (2) to a Product on the Admin Product creation/edit page. Clicks on Save. Validates that the Success Message is present.</description> + </annotations> + <arguments> + <argument name="attribute" defaultValue="visualSwatchAttribute"/> + <argument name="option1" defaultValue="visualSwatchOption1"/> + <argument name="option2" defaultValue="visualSwatchOption2"/> + </arguments> + + <seeInCurrentUrl url="{{ProductCatalogPage.url}}" stepKey="seeOnProductEditPage"/> + <conditionalClick selector="{{AdminProductFormConfigurationsSection.sectionHeader}}" dependentSelector="{{AdminProductFormConfigurationsSection.createConfigurations}}" visible="false" stepKey="openConfigurationSection"/> + <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="openConfigurationPanel"/> + <waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewAttribute}}" stepKey="waitForSlideOut"/> + <click selector="{{AdminCreateProductConfigurationsPanel.createNewAttribute}}" stepKey="clickCreateNewAttribute"/> + <waitForPageLoad stepKey="waitForIFrame"/> + + <switchToIFrame selector="{{AdminNewAttributePanel.newAttributeIFrame}}" stepKey="switchToNewAttributeIFrame"/> + <fillField selector="{{AdminNewAttributePanel.defaultLabel}}" userInput="{{attribute.default_label}}" stepKey="fillDefaultLabel"/> + <selectOption selector="{{AdminNewAttributePanel.inputType}}" userInput="{{attribute.input_type}}" stepKey="selectInputType"/> + <!--Add swatch options--> + <click selector="{{AdminNewAttributePanel.addVisualSwatchOption}}" stepKey="clickAddSwatch1"/> + <waitForElementVisible selector="{{AdminNewAttributePanel.visualSwatchOptionAdminValue('0')}}" stepKey="waitForOption1Row"/> + <fillField selector="{{AdminNewAttributePanel.visualSwatchOptionAdminValue('0')}}" userInput="{{option1.admin_label}}" stepKey="fillAdminLabel1"/> + <fillField selector="{{AdminNewAttributePanel.visualSwatchOptionDefaultStoreValue('0')}}" userInput="{{option1.default_label}}" stepKey="fillDefaultStoreLabel1"/> + <click selector="{{AdminNewAttributePanel.addVisualSwatchOption}}" stepKey="clickAddSwatch2"/> + <waitForElementVisible selector="{{AdminNewAttributePanel.visualSwatchOptionAdminValue('1')}}" stepKey="waitForOption2Row"/> + <fillField selector="{{AdminNewAttributePanel.visualSwatchOptionAdminValue('1')}}" userInput="{{option2.admin_label}}" stepKey="fillAdminLabel2"/> + <fillField selector="{{AdminNewAttributePanel.visualSwatchOptionDefaultStoreValue('1')}}" userInput="{{option2.default_label}}" stepKey="fillDefaultStoreLabel2"/> + + <!--Save attribute--> + <click selector="{{AdminNewAttributePanel.saveAttribute}}" stepKey="clickOnNewAttributePanel"/> + <waitForPageLoad stepKey="waitForSaveAttribute"/> + <switchToIFrame stepKey="switchOutOfIFrame"/> + + <!--Find attribute in grid and select--> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="clickOnFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.attributeCodeFilterInput}}" userInput="{{attribute.default_label}}" stepKey="fillFilterAttributeCodeField"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminDataGridTableSection.rowCheckbox('1')}}" stepKey="clickOnFirstCheckbox"/> + + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextStep1"/> + + <click selector="{{AdminCreateProductConfigurationsPanel.selectAllByAttribute(attribute.default_label)}}" stepKey="clickSelectAll"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickNextStep2"/> + + <click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}" stepKey="clickOnApplySingleQuantityToEachSku"/> + <fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}" userInput="100" stepKey="enterAttributeQuantity"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextStep3"/> + <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="generateProducts"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> + <click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/> + <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSaveProductMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddVisualSwatchToProductWithStorefrontConfigActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddVisualSwatchToProductWithStorefrontConfigActionGroup.xml new file mode 100644 index 0000000000000..71ce5ebd7df30 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddVisualSwatchToProductWithStorefrontConfigActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddVisualSwatchToProductWithStorefrontConfigActionGroup" extends="AddVisualSwatchToProductActionGroup"> + <annotations> + <description>EXTENDS: AddVisualSwatchToProductActionGroup. Add the provided Visual Swatch Attribute and Options (2) to a Product with Storefront Configurations.</description> + </annotations> + <arguments> + <argument name="attribute" defaultValue="visualSwatchAttribute"/> + <argument name="option1" defaultValue="visualSwatchOption1"/> + <argument name="option2" defaultValue="visualSwatchOption2"/> + </arguments> + + <!-- Go to Storefront Properties tab --> + <click selector="{{AdminNewAttributePanel.storefrontPropertiesTab}}" stepKey="goToStorefrontPropertiesTab" after="fillDefaultStoreLabel2"/> + <waitForElementVisible selector="{{AdminNewAttributePanel.storefrontPropertiesTitle}}" stepKey="waitTabLoad" after="goToStorefrontPropertiesTab"/> + <selectOption selector="{{AdminNewAttributePanel.useInSearch}}" stepKey="switchOnUsInSearch" userInput="Yes" after="waitTabLoad"/> + <selectOption selector="{{AdminNewAttributePanel.visibleInAdvancedSearch}}" stepKey="switchOnVisibleInAdvancedSearch" userInput="Yes" after="switchOnUsInSearch"/> + <selectOption selector="{{AdminNewAttributePanel.comparableOnStorefront}}" stepKey="switchOnComparableOnStorefront" userInput="Yes" after="switchOnVisibleInAdvancedSearch"/> + <selectOption selector="{{AdminNewAttributePanel.useInLayeredNavigation}}" stepKey="selectUseInLayer" userInput="Filterable (with results)" after="switchOnComparableOnStorefront"/> + <selectOption selector="{{AdminNewAttributePanel.visibleOnCatalogPagesOnStorefront}}" stepKey="switchOnVisibleOnCatalogPagesOnStorefront" userInput="Yes" after="selectUseInLayer"/> + <selectOption selector="{{AdminNewAttributePanel.useInProductListing}}" stepKey="switchOnUsedInProductListing" userInput="Yes" after="switchOnVisibleOnCatalogPagesOnStorefront"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddVisualSwatchWithProductWithStorefrontPreviewImageConfigActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddVisualSwatchWithProductWithStorefrontPreviewImageConfigActionGroup.xml new file mode 100644 index 0000000000000..64c298bdcb16c --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddVisualSwatchWithProductWithStorefrontPreviewImageConfigActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AddVisualSwatchWithProductWithStorefrontPreviewImageConfigActionGroup" extends="AddVisualSwatchToProductActionGroup"> + <selectOption selector="{{AdminNewAttributePanel.updateProductPreviewImage}}" userInput="Yes" stepKey="selectUpdatePreviewImage" after="selectInputType"/> + <click selector="{{AdminNewAttributePanel.storefrontPropertiesTab}}" stepKey="goToStorefrontPropertiesTab" after="fillDefaultStoreLabel2"/> + <waitForElementVisible selector="{{AdminNewAttributePanel.storefrontPropertiesTitle}}" after="goToStorefrontPropertiesTab" stepKey="waitTabLoad"/> + <selectOption selector="{{AdminNewAttributePanel.useInLayeredNavigation}}" userInput="Filterable (with results)" stepKey="selectUseInLayer" after="waitTabLoad"/> + <selectOption selector="{{AdminNewAttributePanel.useInProductListing}}" userInput="Yes" stepKey="switchOnUsedInProductListing" after="selectUseInLayer"/> + <selectOption selector="{{AdminNewAttributePanel.usedForStoringInProductListing}}" userInput="Yes" stepKey="switchOnUsedForStoringInProductListing" after="switchOnUsedInProductListing"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AdminAddSwatchOptionAndFillFieldsActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AdminAddSwatchOptionAndFillFieldsActionGroup.xml new file mode 100644 index 0000000000000..f31753438569c --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AdminAddSwatchOptionAndFillFieldsActionGroup.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"> + <!--You are on ProductAttributePage--> + <!--Add new swatch options and fill fields for product attribute --> + <actionGroup name="AdminAddSwatchOptionAndFillFieldsActionGroup"> + <arguments> + <argument name="swatchOption" defaultValue="visualSwatchOption1"/> + </arguments> + <click selector="{{AdminNewAttributePanel.addVisualSwatchOption}}" stepKey="clickAddSwatch"/> + <waitForElementVisible selector="{{AdminNewAttributePanel.lastVisualSwatchOptionAdminValue}}" stepKey="waitForOption1Row"/> + <fillField selector="{{AdminNewAttributePanel.lastVisualSwatchOptionAdminValue}}" userInput="{{swatchOption.admin_label}}" stepKey="fillAdminLabel"/> + <fillField selector="{{AdminNewAttributePanel.lastVisualSwatchOptionDefaultStoreValue}}" userInput="{{swatchOption.default_label}}" stepKey="fillDefaultStoreLabel"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AdminEditPropertiesTabForSwatchProductAtributeActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AdminEditPropertiesTabForSwatchProductAtributeActionGroup.xml deleted file mode 100644 index 05ab5a53468a2..0000000000000 --- a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AdminEditPropertiesTabForSwatchProductAtributeActionGroup.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - - <!--You are on ProductAttributePage--> - <!--Add new swatch options and fill fields for product attribute --> - <actionGroup name="AdminAddSwatchOptionAndFillFieldsActionGroup"> - <arguments> - <argument name="swatchOption" defaultValue="visualSwatchOption1"/> - </arguments> - <click selector="{{AdminNewAttributePanel.addVisualSwatchOption}}" stepKey="clickAddSwatch"/> - <waitForElementVisible selector="{{AdminNewAttributePanel.lastVisualSwatchOptionAdminValue}}" stepKey="waitForOption1Row"/> - <fillField selector="{{AdminNewAttributePanel.lastVisualSwatchOptionAdminValue}}" userInput="{{swatchOption.admin_label}}" stepKey="fillAdminLabel"/> - <fillField selector="{{AdminNewAttributePanel.lastVisualSwatchOptionDefaultStoreValue}}" userInput="{{swatchOption.default_label}}" stepKey="fillDefaultStoreLabel"/> - </actionGroup> - - <!--You are on ProductAttributePage--> - <!--Select value for option "Update Product Preview Image"--> - <actionGroup name="AdminUpdateProductPreviewImageActionGroup"> - <arguments> - <argument name="value" type="string" defaultValue="Yes"/> - </arguments> - <selectOption selector="{{AttributePropertiesSection.UpdateProductPreviewImage}}" userInput="{{value}}" stepKey="setUpdateProductPreviewImage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AdminUpdateProductPreviewImageActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AdminUpdateProductPreviewImageActionGroup.xml new file mode 100644 index 0000000000000..34d053a47a00f --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AdminUpdateProductPreviewImageActionGroup.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="AdminUpdateProductPreviewImageActionGroup"> + <arguments> + <argument name="value" type="string" defaultValue="Yes"/> + </arguments> + <selectOption selector="{{AttributePropertiesSection.UpdateProductPreviewImage}}" userInput="{{value}}" stepKey="setUpdateProductPreviewImage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AssertStorefrontSwatchColorActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AssertStorefrontSwatchColorActionGroup.xml new file mode 100644 index 0000000000000..b48494a85ae2c --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AssertStorefrontSwatchColorActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontSwatchColorActionGroup"> + <annotations> + <description>Validates that the Storefront Product has the provided Swatch with the provided Color.</description> + </annotations> + <arguments> + <argument name="nthSwatch" type="string" defaultValue="1"/> + <argument name="expectedRgb" type="string" defaultValue="rgb(231, 77, 60)"/> + </arguments> + + <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOption(nthSwatch)}}" userInput="style" stepKey="grabStyle1"/> + <assertEquals stepKey="assertStyle1"> + <actualResult type="string">{$grabStyle1}</actualResult> + <expectedResult type="string">background: center center no-repeat {{expectedRgb}};</expectedResult> + </assertEquals> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AssertSwatchColorActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AssertSwatchColorActionGroup.xml new file mode 100644 index 0000000000000..f978afd7d2ca0 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AssertSwatchColorActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertSwatchColorActionGroup"> + <annotations> + <description>Validates that the provided Color Picker contains the provided Style.</description> + </annotations> + <arguments> + <argument name="nthSwatch" type="string" defaultValue="1"/> + <argument name="expectedStyle" type="string" defaultValue="background: rgb(0, 0, 0);"/> + </arguments> + + <grabAttributeFrom selector="{{AdminManageSwatchSection.nthSwatch(nthSwatch)}}" userInput="style" stepKey="grabStyle1"/> + <assertEquals stepKey="assertStyle1"> + <actualResult type="string">{$grabStyle1}</actualResult> + <expectedResult type="string">{{expectedStyle}}</expectedResult> + </assertEquals> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml deleted file mode 100644 index 7cad0048261eb..0000000000000 --- a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/ColorPickerActionGroup.xml +++ /dev/null @@ -1,68 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="setColorPickerByHex"> - <annotations> - <description>Sets the provided HEX value in the provided Color Picker.</description> - </annotations> - <arguments> - <argument name="nthColorPicker" type="string" defaultValue="1"/> - <argument name="hexColor" type="string" defaultValue="e74c3c"/> - </arguments> - - <!-- This 6x backspace stuff is some magic that is necessary to interact with this field correctly --> - <pressKey selector="{{AdminColorPickerSection.hexByIndex(nthColorPicker)}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,'{{hexColor}}']" stepKey="fillHex1"/> - <click selector="{{AdminColorPickerSection.submitByIndex(nthColorPicker)}}" stepKey="submitColor1"/> - </actionGroup> - - <actionGroup name="assertSwatchColor"> - <annotations> - <description>Validates that the provided Color Picker contains the provided Style.</description> - </annotations> - <arguments> - <argument name="nthSwatch" type="string" defaultValue="1"/> - <argument name="expectedStyle" type="string" defaultValue="background: rgb(0, 0, 0);"/> - </arguments> - - <grabAttributeFrom selector="{{AdminManageSwatchSection.nthSwatch(nthSwatch)}}" userInput="style" stepKey="grabStyle1"/> - <assertEquals stepKey="assertStyle1"> - <actualResult type="string">{$grabStyle1}</actualResult> - <expectedResult type="string">{{expectedStyle}}</expectedResult> - </assertEquals> - </actionGroup> - - <actionGroup name="assertStorefrontSwatchColor"> - <annotations> - <description>Validates that the Storefront Product has the provided Swatch with the provided Color.</description> - </annotations> - <arguments> - <argument name="nthSwatch" type="string" defaultValue="1"/> - <argument name="expectedRgb" type="string" defaultValue="rgb(231, 77, 60)"/> - </arguments> - - <grabAttributeFrom selector="{{StorefrontProductInfoMainSection.nthSwatchOption(nthSwatch)}}" userInput="style" stepKey="grabStyle1"/> - <assertEquals stepKey="assertStyle1"> - <actualResult type="string">{$grabStyle1}</actualResult> - <expectedResult type="string">background: center center no-repeat {{expectedRgb}};</expectedResult> - </assertEquals> - </actionGroup> - - <actionGroup name="openSwatchMenuByIndex"> - <annotations> - <description>Options the Swatch Menu based on the provided Index.</description> - </annotations> - <arguments> - <argument name="index" type="string" defaultValue="0"/> - </arguments> - - <!-- I had to use executeJS to perform the click to get around the use of CSS ::before and ::after --> - <executeJS function="jQuery('#swatch_window_option_option_{{index}}').click()" stepKey="clickSwatch1"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/OpenSwatchMenuByIndexActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/OpenSwatchMenuByIndexActionGroup.xml new file mode 100644 index 0000000000000..b721596789f92 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/OpenSwatchMenuByIndexActionGroup.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="OpenSwatchMenuByIndexActionGroup"> + <annotations> + <description>Options the Swatch Menu based on the provided Index.</description> + </annotations> + <arguments> + <argument name="index" type="string" defaultValue="0"/> + </arguments> + + <!-- I had to use executeJS to perform the click to get around the use of CSS ::before and ::after --> + <executeJS function="jQuery('#swatch_window_option_option_{{index}}').click()" stepKey="clickSwatch1"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/SetColorPickerByHexActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/SetColorPickerByHexActionGroup.xml new file mode 100644 index 0000000000000..396c1cc87c5c4 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/SetColorPickerByHexActionGroup.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="SetColorPickerByHexActionGroup"> + <annotations> + <description>Sets the provided HEX value in the provided Color Picker.</description> + </annotations> + <arguments> + <argument name="nthColorPicker" type="string" defaultValue="1"/> + <argument name="hexColor" type="string" defaultValue="e74c3c"/> + </arguments> + + <!-- This 6x backspace stuff is some magic that is necessary to interact with this field correctly --> + <pressKey selector="{{AdminColorPickerSection.hexByIndex(nthColorPicker)}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,\Facebook\WebDriver\WebDriverKeys::BACKSPACE,'{{hexColor}}']" stepKey="fillHex1"/> + <click selector="{{AdminColorPickerSection.submitByIndex(nthColorPicker)}}" stepKey="submitColor1"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/StorefrontAddProductWithSwatchAttributeToTheCartActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/StorefrontAddProductWithSwatchAttributeToTheCartActionGroup.xml deleted file mode 100644 index 9bd58bff69e5a..0000000000000 --- a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/StorefrontAddProductWithSwatchAttributeToTheCartActionGroup.xml +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!-- Add Configurable Product with Swatch attribute to the cart --> - <actionGroup name="StorefrontAddProductWithSwatchesToTheCartActionGroup" extends="StorefrontAddProductToCartWithQtyActionGroup"> - <arguments> - <argument name="product"/> - <argument name="productOption" type="string"/> - </arguments> - <remove keyForRemoval="seeSuccessSaveMessage"/> - <click selector="{{StorefrontProductInfoMainSection.visualSwatchOption(productOption)}}" after="waitForStorefrontProductPageLoad" stepKey="clickSelectOption"/> - <see selector="{{StorefrontMessagesSection.success}}" userInput="You added {{product.name}} to your shopping cart." after="waitForSuccessMessage" stepKey="seeAddToCartSuccessMessage"/> - </actionGroup> - - <!-- Update Configurable Product with Swatch attribute in the cart --> - <actionGroup name="StorefrontUpdateCartConfigurableProductWithSwatches"> - <arguments> - <argument name="product"/> - <argument name="productOption" type="string"/> - </arguments> - <click selector="{{CheckoutCartProductSection.nthEditButton('1')}}" stepKey="clickEditConfigurableProductButton"/> - <waitForPageLoad stepKey="waitForStorefrontProductPageLoad"/> - <click selector="{{StorefrontProductInfoMainSection.visualSwatchOption(productOption)}}" stepKey="changeSwatchAttributeOption"/> - <click selector="{{StorefrontProductInfoMainSection.updateCart}}" stepKey="clickUpdateCartButton"/> - <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> - <see selector="{{StorefrontMessagesSection.success}}" userInput="{{product.name}} was updated in your shopping cart." stepKey="assertSuccessMessage"/> - </actionGroup> -</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/StorefrontAddProductWithSwatchesToTheCartActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/StorefrontAddProductWithSwatchesToTheCartActionGroup.xml new file mode 100644 index 0000000000000..64933e184a86d --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/StorefrontAddProductWithSwatchesToTheCartActionGroup.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"> + <!-- Add Configurable Product with Swatch attribute to the cart --> + <actionGroup name="StorefrontAddProductWithSwatchesToTheCartActionGroup" extends="StorefrontAddProductToCartWithQtyActionGroup"> + <arguments> + <argument name="product"/> + <argument name="productOption" type="string"/> + </arguments> + <remove keyForRemoval="seeSuccessSaveMessage"/> + <click selector="{{StorefrontProductInfoMainSection.visualSwatchOption(productOption)}}" after="waitForStorefrontProductPageLoad" stepKey="clickSelectOption"/> + <see selector="{{StorefrontMessagesSection.success}}" userInput="You added {{product.name}} to your shopping cart." after="waitForSuccessMessage" stepKey="seeAddToCartSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/StorefrontAssertSwatchOptionPriceActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/StorefrontAssertSwatchOptionPriceActionGroup.xml new file mode 100644 index 0000000000000..b4a3708c15659 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/StorefrontAssertSwatchOptionPriceActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAssertSwatchOptionPriceActionGroup"> + <arguments> + <argument name="optionName" type="string"/> + <argument name="optionPrice" type="string"/> + </arguments> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel(optionName)}}" stepKey="clickOnOption"/> + <see userInput="{{optionPrice}}" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeOptionPrice"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml deleted file mode 100644 index 6ca0c220778d6..0000000000000 --- a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/StorefrontProductActionGroup.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Click a swatch option on product page--> - <actionGroup name="StorefrontSelectSwatchOptionOnProductPage"> - <arguments> - <argument name="optionName" type="string"/> - </arguments> - <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel(optionName)}}" stepKey="clickSwatchOption"/> - </actionGroup> - <actionGroup name="StorefrontAssertSwatchOptionPrice"> - <arguments> - <argument name="optionName" type="string"/> - <argument name="optionPrice" type="string"/> - </arguments> - <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel(optionName)}}" stepKey="clickOnOption"/> - <see userInput="{{optionPrice}}" selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="seeOptionPrice"/> - </actionGroup> - - <!--Click a swatch option on product page and check active image--> - <actionGroup name="StorefrontSelectSwatchOptionOnProductPageAndCheckImage" extends="StorefrontSelectSwatchOptionOnProductPage"> - <arguments> - <argument name="fileName" type="string" defaultValue="magento-logo"/> - </arguments> - <seeElement selector="{{StorefrontProductMediaSection.productImageActive(fileName)}}" stepKey="seeActiveImageDefault"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/StorefrontSelectSwatchOptionOnProductPageActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/StorefrontSelectSwatchOptionOnProductPageActionGroup.xml new file mode 100644 index 0000000000000..53c4adb5e1ab3 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/StorefrontSelectSwatchOptionOnProductPageActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!--Click a swatch option on product page--> + <actionGroup name="StorefrontSelectSwatchOptionOnProductPageActionGroup"> + <arguments> + <argument name="optionName" type="string"/> + </arguments> + <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel(optionName)}}" stepKey="clickSwatchOption"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/StorefrontSelectSwatchOptionOnProductPageAndCheckImageActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/StorefrontSelectSwatchOptionOnProductPageAndCheckImageActionGroup.xml new file mode 100644 index 0000000000000..67fd0178be9fa --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/StorefrontSelectSwatchOptionOnProductPageAndCheckImageActionGroup.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="StorefrontSelectSwatchOptionOnProductPageAndCheckImageActionGroup" extends="StorefrontSelectSwatchOptionOnProductPageActionGroup"> + <arguments> + <argument name="fileName" type="string" defaultValue="magento-logo"/> + </arguments> + <seeElement selector="{{StorefrontProductMediaSection.productImageActive(fileName)}}" stepKey="seeActiveImageDefault"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/StorefrontUpdateCartConfigurableProductWithSwatchesActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/StorefrontUpdateCartConfigurableProductWithSwatchesActionGroup.xml new file mode 100644 index 0000000000000..2a4f7fd2db62d --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/StorefrontUpdateCartConfigurableProductWithSwatchesActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontUpdateCartConfigurableProductWithSwatchesActionGroup"> + <arguments> + <argument name="product"/> + <argument name="productOption" type="string"/> + </arguments> + <click selector="{{CheckoutCartProductSection.nthEditButton('1')}}" stepKey="clickEditConfigurableProductButton"/> + <waitForPageLoad stepKey="waitForStorefrontProductPageLoad"/> + <click selector="{{StorefrontProductInfoMainSection.visualSwatchOption(productOption)}}" stepKey="changeSwatchAttributeOption"/> + <click selector="{{StorefrontProductInfoMainSection.updateCart}}" stepKey="clickUpdateCartButton"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <see selector="{{StorefrontMessagesSection.success}}" userInput="{{product.name}} was updated in your shopping cart." stepKey="assertSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml index 0e294153e881e..8a2683af83dc1 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml @@ -40,7 +40,7 @@ <!-- Set swatch image #1 --> <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch1"/> - <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch1"> + <actionGroup ref="OpenSwatchMenuByIndexActionGroup" stepKey="clickSwatch1"> <argument name="index" value="0"/> </actionGroup> <click selector="{{AdminManageSwatchSection.nthUploadFile('1')}}" stepKey="clickUploadFile1"/> @@ -49,7 +49,7 @@ <!-- Set swatch image #2 --> <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch2"/> - <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch2"> + <actionGroup ref="OpenSwatchMenuByIndexActionGroup" stepKey="clickSwatch2"> <argument name="index" value="1"/> </actionGroup> <click selector="{{AdminManageSwatchSection.nthUploadFile('2')}}" stepKey="clickUploadFile2"/> @@ -58,7 +58,7 @@ <!-- Set swatch image #3 --> <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch3"/> - <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch3"> + <actionGroup ref="OpenSwatchMenuByIndexActionGroup" stepKey="clickSwatch3"> <argument name="index" value="2"/> </actionGroup> <click selector="{{AdminManageSwatchSection.nthUploadFile('3')}}" stepKey="clickUploadFile3"/> @@ -98,10 +98,10 @@ <!-- Create a configurable product to verify the storefront with --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad time="30" stepKey="waitForProductGrid"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateConfigurableProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateConfigurableProduct"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> - <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="fillCategory"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml index 87d3f0bb5bcb9..4685670fbfdd2 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateTextSwatchTest.xml @@ -54,12 +54,12 @@ <!-- Create a configurable product to verify the storefront with --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad time="30" stepKey="waitForProductGrid"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateConfigurableProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateConfigurableProduct"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/> <fillField userInput="{{_defaultProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/> - <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml index 65f0e2b09b82a..1c8e86b3765d4 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchTest.xml @@ -50,33 +50,33 @@ <!-- Set swatch #1 using the color picker --> <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch1"/> - <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch1"> + <actionGroup ref="OpenSwatchMenuByIndexActionGroup" stepKey="clickSwatch1"> <argument name="index" value="0"/> </actionGroup> <click selector="{{AdminManageSwatchSection.nthChooseColor('1')}}" stepKey="clickChooseColor1"/> - <actionGroup ref="setColorPickerByHex" stepKey="fillHex1"> + <actionGroup ref="SetColorPickerByHexActionGroup" stepKey="fillHex1"> <argument name="nthColorPicker" value="1"/> <argument name="hexColor" value="e74c3c"/> </actionGroup> <fillField selector="{{AdminManageSwatchSection.adminInputByIndex('0')}}" userInput="red" stepKey="fillAdmin1"/> <!-- Set swatch #2 using the color picker --> <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch2"/> - <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch2"> + <actionGroup ref="OpenSwatchMenuByIndexActionGroup" stepKey="clickSwatch2"> <argument name="index" value="1"/> </actionGroup> <click selector="{{AdminManageSwatchSection.nthChooseColor('2')}}" stepKey="clickChooseColor2"/> - <actionGroup ref="setColorPickerByHex" stepKey="fillHex2"> + <actionGroup ref="SetColorPickerByHexActionGroup" stepKey="fillHex2"> <argument name="nthColorPicker" value="2"/> <argument name="hexColor" value="2ecc71"/> </actionGroup> <fillField selector="{{AdminManageSwatchSection.adminInputByIndex('1')}}" userInput="green" stepKey="fillAdmin2"/> <!-- Set swatch #3 using the color picker --> <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch3"/> - <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch3"> + <actionGroup ref="OpenSwatchMenuByIndexActionGroup" stepKey="clickSwatch3"> <argument name="index" value="2"/> </actionGroup> <click selector="{{AdminManageSwatchSection.nthChooseColor('3')}}" stepKey="clickChooseColor3"/> - <actionGroup ref="setColorPickerByHex" stepKey="fillHex3"> + <actionGroup ref="SetColorPickerByHexActionGroup" stepKey="fillHex3"> <argument name="nthColorPicker" value="3"/> <argument name="hexColor" value="3498db"/> </actionGroup> @@ -88,15 +88,15 @@ <waitForElementVisible selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccess"/> <!-- Assert that the Save was successful after round trip to server --> - <actionGroup ref="assertSwatchColor" stepKey="assertSwatch1"> + <actionGroup ref="AssertSwatchColorActionGroup" stepKey="assertSwatch1"> <argument name="nthSwatch" value="1"/> <argument name="expectedStyle" value="background: rgb(231, 77, 60);"/> </actionGroup> - <actionGroup ref="assertSwatchColor" stepKey="assertSwatch2"> + <actionGroup ref="AssertSwatchColorActionGroup" stepKey="assertSwatch2"> <argument name="nthSwatch" value="2"/> <argument name="expectedStyle" value="background: rgb(46, 204, 112);"/> </actionGroup> - <actionGroup ref="assertSwatchColor" stepKey="assertSwatch3"> + <actionGroup ref="AssertSwatchColorActionGroup" stepKey="assertSwatch3"> <argument name="nthSwatch" value="3"/> <argument name="expectedStyle" value="background: rgb(52, 152, 219);"/> </actionGroup> @@ -143,15 +143,15 @@ <waitForPageLoad stepKey="waitForProductPage"/> <!-- Verify that the storefront shows the swatches too --> - <actionGroup ref="assertStorefrontSwatchColor" stepKey="assertSwatch4"> + <actionGroup ref="AssertStorefrontSwatchColorActionGroup" stepKey="assertSwatch4"> <argument name="nthSwatch" value="1"/> <argument name="expectedRgb" value="rgb(231, 77, 60)"/> </actionGroup> - <actionGroup ref="assertStorefrontSwatchColor" stepKey="assertSwatch5"> + <actionGroup ref="AssertStorefrontSwatchColorActionGroup" stepKey="assertSwatch5"> <argument name="nthSwatch" value="2"/> <argument name="expectedRgb" value="rgb(46, 204, 112)"/> </actionGroup> - <actionGroup ref="assertStorefrontSwatchColor" stepKey="assertSwatch6"> + <actionGroup ref="AssertStorefrontSwatchColorActionGroup" stepKey="assertSwatch6"> <argument name="nthSwatch" value="3"/> <argument name="expectedRgb" value="rgb(52, 152, 219)"/> </actionGroup> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchWithNonValidOptionsTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchWithNonValidOptionsTest.xml index b03f771875957..8e63a14413f2f 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchWithNonValidOptionsTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateVisualSwatchWithNonValidOptionsTest.xml @@ -21,7 +21,7 @@ </before> <after> <!-- Remove attribute --> - <actionGroup ref="deleteProductAttribute" stepKey="deleteAttribute"> + <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteAttribute"> <argument name="ProductAttribute" value="visualSwatchAttribute"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> @@ -47,11 +47,11 @@ <!-- Add new swatch option without label --> <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch1"/> - <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch1"> + <actionGroup ref="OpenSwatchMenuByIndexActionGroup" stepKey="clickSwatch1"> <argument name="index" value="0"/> </actionGroup> <click selector="{{AdminManageSwatchSection.nthChooseColor('1')}}" stepKey="clickChooseColor1"/> - <actionGroup ref="setColorPickerByHex" stepKey="fillHex1"> + <actionGroup ref="SetColorPickerByHexActionGroup" stepKey="fillHex1"> <argument name="nthColorPicker" value="1"/> <argument name="hexColor" value="ff0000"/> </actionGroup> @@ -66,11 +66,11 @@ <!-- Add 2 additional new swatch options --> <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch2"/> - <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch2"> + <actionGroup ref="OpenSwatchMenuByIndexActionGroup" stepKey="clickSwatch2"> <argument name="index" value="1"/> </actionGroup> <click selector="{{AdminManageSwatchSection.nthChooseColor('2')}}" stepKey="clickChooseColor2"/> - <actionGroup ref="setColorPickerByHex" stepKey="fillHex2"> + <actionGroup ref="SetColorPickerByHexActionGroup" stepKey="fillHex2"> <argument name="nthColorPicker" value="2"/> <argument name="hexColor" value="00ff00"/> </actionGroup> @@ -78,11 +78,11 @@ userInput="green" stepKey="fillAdmin2"/> <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch3"/> - <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch3"> + <actionGroup ref="OpenSwatchMenuByIndexActionGroup" stepKey="clickSwatch3"> <argument name="index" value="2"/> </actionGroup> <click selector="{{AdminManageSwatchSection.nthChooseColor('3')}}" stepKey="clickChooseColor3"/> - <actionGroup ref="setColorPickerByHex" stepKey="fillHex3"> + <actionGroup ref="SetColorPickerByHexActionGroup" stepKey="fillHex3"> <argument name="nthColorPicker" value="3"/> <argument name="hexColor" value="0000ff"/> </actionGroup> @@ -103,10 +103,10 @@ <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> - <actionGroup ref="navigateToCreatedProductAttribute" stepKey="navigateToAttribute"> + <actionGroup ref="NavigateToCreatedProductAttributeActionGroup" stepKey="navigateToAttribute"> <argument name="ProductAttribute" value="visualSwatchAttribute"/> </actionGroup> <!-- Check attribute data --> <seeCheckboxIsChecked selector="{{AdminManageSwatchSection.nthIsDefault('2')}}" stepKey="CheckDefaultOption"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminDisablingSwatchTooltipsTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminDisablingSwatchTooltipsTest.xml index 3d69895b0c895..b44c04d2c1b46 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminDisablingSwatchTooltipsTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminDisablingSwatchTooltipsTest.xml @@ -11,6 +11,7 @@ <test name="AdminDisablingSwatchTooltipsTest"> <annotations> <features value="Swatches"/> + <stories value="Swatch Tooltip Status Change"/> <title value="Admin disabling swatch tooltips test."/> <description value="Verify possibility to disable/enable swatch tooltips."/> <severity value="AVERAGE"/> @@ -60,11 +61,11 @@ <!-- Set swatch using the color picker --> <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch1"/> - <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch1"> + <actionGroup ref="OpenSwatchMenuByIndexActionGroup" stepKey="clickSwatch1"> <argument name="index" value="0"/> </actionGroup> <click selector="{{AdminManageSwatchSection.nthChooseColor('1')}}" stepKey="clickChooseColor1"/> - <actionGroup ref="setColorPickerByHex" stepKey="fillHex1"> + <actionGroup ref="SetColorPickerByHexActionGroup" stepKey="fillHex1"> <argument name="nthColorPicker" value="1"/> <argument name="hexColor" value="e74c3c"/> </actionGroup> @@ -76,7 +77,7 @@ <waitForElementVisible selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccess"/> <!-- Assert that the Save was successful after round trip to server --> - <actionGroup ref="assertSwatchColor" stepKey="assertSwatchAdmin"> + <actionGroup ref="AssertSwatchColorActionGroup" stepKey="assertSwatchAdmin"> <argument name="nthSwatch" value="1"/> <argument name="expectedStyle" value="background: rgb(231, 77, 60);"/> </actionGroup> @@ -136,7 +137,7 @@ <waitForPageLoad stepKey="waitForProductPage"/> <!-- Verify that the storefront shows the swatches too --> - <actionGroup ref="assertStorefrontSwatchColor" stepKey="assertSwatchStorefront"> + <actionGroup ref="AssertStorefrontSwatchColorActionGroup" stepKey="assertSwatchStorefront"> <argument name="nthSwatch" value="1"/> <argument name="expectedRgb" value="rgb(231, 77, 60)"/> </actionGroup> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminSaveConfigurableProductWithAttributesImagesAndSwatchesTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminSaveConfigurableProductWithAttributesImagesAndSwatchesTest.xml index f94314fe94806..d034faeefbdc0 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminSaveConfigurableProductWithAttributesImagesAndSwatchesTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminSaveConfigurableProductWithAttributesImagesAndSwatchesTest.xml @@ -33,18 +33,18 @@ <!-- Add a few Swatches and add images to Manage Swatch (Values of Your Attribute) 1. Set swatch #1 using the color picker --> <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddFirstSwatch"/> - <actionGroup ref="openSwatchMenuByIndex" stepKey="clickFirstSwatch"> + <actionGroup ref="OpenSwatchMenuByIndexActionGroup" stepKey="clickFirstSwatch"> <argument name="index" value="0"/> </actionGroup> <click selector="{{AdminManageSwatchSection.nthChooseColor('1')}}" stepKey="clickChooseColor"/> - <actionGroup ref="setColorPickerByHex" stepKey="fillFirstHex"> + <actionGroup ref="SetColorPickerByHexActionGroup" stepKey="fillFirstHex"> <argument name="nthColorPicker" value="1"/> <argument name="hexColor" value="e74c3c"/> </actionGroup> <fillField selector="{{AdminManageSwatchSection.adminInputByIndex('0')}}" userInput="red" stepKey="fillFirstAdminField"/> <!-- Set swatch #2 using upload file --> <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSecondSwatch"/> - <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch2"> + <actionGroup ref="OpenSwatchMenuByIndexActionGroup" stepKey="clickSwatch2"> <argument name="index" value="1"/> </actionGroup> <click selector="{{AdminManageSwatchSection.nthUploadFile('2')}}" stepKey="clickUploadFile2"/> @@ -59,9 +59,13 @@ </before> <after> <!-- Delete product attribute and clear grid filter --> - <actionGroup ref="deleteProductAttributeByAttributeCode" stepKey="deleteProductAttribute"> - <argument name="ProductAttributeCode" value="{{VisualSwatchProductAttribute.attribute_code}}"/> + <actionGroup ref="OpenProductAttributeFromSearchResultInGridActionGroup" stepKey="openProductAttributeFromSearchResultInGrid"> + <argument name="productAttributeCode" value="{{VisualSwatchProductAttribute.attribute_code}}"/> </actionGroup> + <actionGroup ref="DeleteProductAttributeByAttributeCodeActionGroup" stepKey="deleteProductAttributeByAttributeCode"> + <argument name="productAttributeCode" value="{{VisualSwatchProductAttribute.attribute_code}}"/> + </actionGroup> + <actionGroup ref="AssertProductAttributeRemovedSuccessfullyActionGroup" stepKey="deleteProductAttributeSuccess"/> <actionGroup ref="AdminGridFilterResetActionGroup" stepKey="clearAttributesGridFilter"/> <!--Clear products grid filter--> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPage"/> @@ -73,23 +77,23 @@ <!-- Add created product attribute to the Default set --> <actionGroup ref="AdminOpenAttributeSetGridPageActionGroup" stepKey="openAttributeSetPage"/> <actionGroup ref="AdminOpenAttributeSetByNameActionGroup" stepKey="openDefaultAttributeSet"/> - <actionGroup ref="AssignAttributeToGroup" stepKey="assignAttributeToGroup"> + <actionGroup ref="AssignAttributeToGroupActionGroup" stepKey="assignAttributeToGroup"> <argument name="group" value="Product Details"/> <argument name="attribute" value="{{VisualSwatchProductAttribute.attribute_code}}"/> </actionGroup> - <actionGroup ref="SaveAttributeSet" stepKey="saveAttributeSet"/> + <actionGroup ref="SaveAttributeSetActionGroup" stepKey="saveAttributeSet"/> <!-- Create configurable product --> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPage"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateConfigurableProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateConfigurableProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!-- Fill all the necessary information such as weight, name, SKU etc --> - <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!-- Click "Create Configurations" button, select created product attribute using the same Quantity for all products. Click "Generate products" button --> - <actionGroup ref="generateConfigurationsByAttributeCode" stepKey="addAttributeToProduct"> + <actionGroup ref="GenerateConfigurationsByAttributeCodeActionGroup" stepKey="addAttributeToProduct"> <argument name="attributeCode" value="{{VisualSwatchProductAttribute.attribute_code}}"/> </actionGroup> <!-- Using this action to concatenate 2 strings to have unique identifier for grid --> @@ -100,10 +104,10 @@ <attachFile selector="{{AdminDataGridTableSection.rowTemplate({$attributeCodeAdobeSmall})}}{{AdminProductFormConfigurationsSection.fileUploaderInput}}" userInput="{{TestImageAdobe.file}}" stepKey="uploadImageForSecondProduct"/> <!-- Click "Save" button --> - <actionGroup ref="saveProductForm" stepKey="clickSaveButton"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="clickSaveButton"/> <!-- Delete all created product --> - <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProducts"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteCreatedProducts"> <argument name="sku" value="{{ApiConfigurableProduct.sku}}"/> </actionGroup> </test> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml new file mode 100644 index 0000000000000..569952019b29b --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.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="AdminSetUpWatermarkForSwatchImageTest"> + <annotations> + <features value="Swatches"/> + <title value="Possibility to set up watermark for a swatch image type"/> + <description value="Possibility to set up watermark for a swatch image type"/> + <severity value="MAJOR"/> + <testCaseId value="MC-17607"/> + <useCaseId value="MC-15523"/> + <group value="swatches"/> + </annotations> + <before> + <!-- Login as Admin --> + <comment userInput="Login as Admin" stepKey="commentLoginAsAdmin"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!-- Log out --> + <comment userInput="Log out" stepKey="commentLogOut"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Go to Admin > Content > Configuration page --> + <comment userInput="Go to Configuration Page" stepKey="commentOpenConfigurationPage"/> + <amOnPage url="{{DesignConfigPage.url}}" stepKey="navigateToDesignConfigPage"/> + <actionGroup ref="AdminFilterStoreViewActionGroup" stepKey="filterDefaultStoreView"> + <argument name="customStore" value="'Default'" /> + </actionGroup> + <!-- Select Edit next to the Default Store View --> + <comment userInput="Select Edit next to the Default Store View" stepKey="commentEditDefaultView"/> + <click selector="{{AdminCustomerGridSection.firstRowEditLink}}" stepKey="clickToEditDefaultStoreView"/> + <waitForPageLoad stepKey="waitForDefaultStorePage"/> + <!-- Expand the Product Image Watermarks section--> + <comment userInput="Expand the Product Image Watermarks section" stepKey="commentOpenWatermarksSection"/> + <click selector="{{AdminDesignConfigSection.watermarkSectionHeader}}" stepKey="clickToProductImageWatermarks"/> + <waitForPageLoad stepKey="waitForWatermarksPage"/> + <!-- See Base, Thumbnail, Small image types are displayed --> + <comment userInput="See Base, Thumbnail, Small image types are displayed" stepKey="commentSeeImageTypes"/> + <seeElement selector="{{AdminDesignConfigSection.imageWatermarkType('Base')}}" stepKey="seeElementBaseWatermark"/> + <waitForElementVisible selector="{{AdminDesignConfigSection.imageWatermarkType('Thumbnail')}}" stepKey="waitForThumbnailVisible" /> + <seeElement selector="{{AdminDesignConfigSection.imageWatermarkType('Thumbnail')}}" stepKey="seeElementThumbnailWatermark"/> + <waitForElementVisible selector="{{AdminDesignConfigSection.imageWatermarkType('Small')}}" stepKey="waitForSmallVisible" /> + <seeElement selector="{{AdminDesignConfigSection.imageWatermarkType('Small')}}" stepKey="seeElementSmallWatermark"/> + <!-- See Swatch Image type is absent --> + <comment userInput="See Swatch Image type is absent" stepKey="commentSeeTypeAbsent"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <dontSeeElement selector="{{AdminDesignConfigSection.imageWatermarkType('Swatch')}}" stepKey="dontSeeImageWatermarkSwatchImage"/> + </test> +</tests> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminWatermarkUploadTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminWatermarkUploadTest.xml deleted file mode 100644 index e9df186bae5e6..0000000000000 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminWatermarkUploadTest.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminWatermarkUploadTest"> - <waitForElement selector="{{AdminDesignConfigSection.imageUploadInputByFieldsetName('Swatch Image')}}" stepKey="waitForInputVisible4" after="waitForPreviewImage3"/> - <attachFile selector="{{AdminDesignConfigSection.imageUploadInputByFieldsetName('Swatch Image')}}" userInput="adobe-small.jpg" stepKey="attachFile4" after="waitForInputVisible4"/> - <waitForElementVisible selector="{{AdminDesignConfigSection.imageUploadPreviewByFieldsetName('Swatch Image')}}" stepKey="waitForPreviewImage4" after="attachFile4"/> - </test> -</tests> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest.xml index 0a98e7a721c17..1cfa7da6f3410 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontConfigurableProductSwatchMinimumPriceTest.xml @@ -26,17 +26,17 @@ </before> <after> <!-- Delete configurable product and all child products --> - <actionGroup ref="deleteProductsByKeyword" stepKey="deleteProductsByKeyword"> + <actionGroup ref="DeleteProductsByKeywordActionGroup" stepKey="deleteProductsByKeyword"> <argument name="keyword" value="{{_defaultProduct.sku}}"/> </actionGroup> <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategoryAttribute"/> <!-- Delete color attribute --> - <actionGroup ref="deleteProductAttribute" stepKey="deleteColorAttribute"> + <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteColorAttribute"> <argument name="ProductAttribute" value="ProductColorAttribute"/> </actionGroup> <!-- Delete size attribute --> - <actionGroup ref="deleteProductAttribute" stepKey="deleteSizeAttribute"> + <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteSizeAttribute"> <argument name="ProductAttribute" value="ProductSizeAttribute"/> </actionGroup> <!-- Logout --> @@ -60,59 +60,59 @@ <argument name="option3" value="Large"/> </actionGroup> <!--Create configurable product with two attributes: Color and Size--> - <actionGroup ref="createConfigurableProductWithTwoAttributes" stepKey="createProduct"> + <actionGroup ref="CreateConfigurableProductWithTwoAttributesActionGroup" stepKey="createProduct"> <argument name="product" value="_defaultProduct"/> <argument name="category" value="$$createCategory$$"/> <argument name="attribute1" value="ProductColorAttribute"/> <argument name="attribute2" value="ProductSizeAttribute"/> </actionGroup> <!--Set Black-Small product price to 10--> - <actionGroup ref="changeConfigurableProductChildProductPrice" stepKey="changeBlackSmallPrice"> + <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeBlackSmallPrice"> <argument name="productAttributes" value="Color: Black, Size: Small"/> <argument name="productPrice" value="10"/> </actionGroup> <!--Set Black-Medium product price to 11--> - <actionGroup ref="changeConfigurableProductChildProductPrice" stepKey="changeBlackMediumPrice"> + <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeBlackMediumPrice"> <argument name="productAttributes" value="Color: Black, Size: Medium"/> <argument name="productPrice" value="11"/> </actionGroup> <!--Set Black-Large product price to 12--> - <actionGroup ref="changeConfigurableProductChildProductPrice" stepKey="changeBlackLargePrice"> + <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeBlackLargePrice"> <argument name="productAttributes" value="Color: Black, Size: Large"/> <argument name="productPrice" value="12"/> </actionGroup> <!--Set White-Small product price to 14--> - <actionGroup ref="changeConfigurableProductChildProductPrice" stepKey="changeWhiteSmallPrice"> + <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeWhiteSmallPrice"> <argument name="productAttributes" value="Color: White, Size: Small"/> <argument name="productPrice" value="14"/> </actionGroup> <!--Set White-Medium product price to 13--> - <actionGroup ref="changeConfigurableProductChildProductPrice" stepKey="changeWhiteMediumPrice"> + <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeWhiteMediumPrice"> <argument name="productAttributes" value="Color: White, Size: Medium"/> <argument name="productPrice" value="13"/> </actionGroup> <!--Set White-Large product price to 15--> - <actionGroup ref="changeConfigurableProductChildProductPrice" stepKey="changeWhiteLargePrice"> + <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeWhiteLargePrice"> <argument name="productAttributes" value="Color: White, Size: Large"/> <argument name="productPrice" value="15"/> </actionGroup> <!--Set Blue-Small product price to 18--> - <actionGroup ref="changeConfigurableProductChildProductPrice" stepKey="changeBlueSmallPrice"> + <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeBlueSmallPrice"> <argument name="productAttributes" value="Color: Blue, Size: Small"/> <argument name="productPrice" value="18"/> </actionGroup> <!--Set Blue-Medium product price to 17--> - <actionGroup ref="changeConfigurableProductChildProductPrice" stepKey="changeBlueMediumPrice"> + <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeBlueMediumPrice"> <argument name="productAttributes" value="Color: Blue, Size: Medium"/> <argument name="productPrice" value="17"/> </actionGroup> <!--Set Blue-Large product price to 16--> - <actionGroup ref="changeConfigurableProductChildProductPrice" stepKey="changeBlueLargePrice"> + <actionGroup ref="ChangeConfigurableProductChildProductPriceActionGroup" stepKey="changeBlueLargePrice"> <argument name="productAttributes" value="Color: Blue, Size: Large"/> <argument name="productPrice" value="16"/> </actionGroup> <!--Save configurable product--> - <actionGroup ref="saveConfigurableProduct" stepKey="saveProduct"> + <actionGroup ref="SaveConfigurableProductActionGroup" stepKey="saveProduct"> <argument name="product" value="_defaultProduct"/> </actionGroup> @@ -126,19 +126,19 @@ </actionGroup> <!--Verify that Black option's minimum price is 16--> - <actionGroup ref="StorefrontAssertSwatchOptionPrice" stepKey="assertMinimumPriceForBlackOption"> + <actionGroup ref="StorefrontAssertSwatchOptionPriceActionGroup" stepKey="assertMinimumPriceForBlackOption"> <argument name="optionName" value="Black"/> <argument name="optionPrice" value="10.00"/> </actionGroup> <!--Verify that White option's minimum price is 16--> - <actionGroup ref="StorefrontAssertSwatchOptionPrice" stepKey="assertMinimumPriceForWhiteOption"> + <actionGroup ref="StorefrontAssertSwatchOptionPriceActionGroup" stepKey="assertMinimumPriceForWhiteOption"> <argument name="optionName" value="White"/> <argument name="optionPrice" value="13.00"/> </actionGroup> <!--Verify that Blue option's minimum price is 16--> - <actionGroup ref="StorefrontAssertSwatchOptionPrice" stepKey="assertMinimumPriceForBlueOption"> + <actionGroup ref="StorefrontAssertSwatchOptionPriceActionGroup" stepKey="assertMinimumPriceForBlueOption"> <argument name="optionName" value="Blue"/> <argument name="optionPrice" value="16.00"/> </actionGroup> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontCustomerCanChangeProductOptionsUsingSwatchesTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontCustomerCanChangeProductOptionsUsingSwatchesTest.xml index f5bcccd4aa9b2..32e447f6463c0 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontCustomerCanChangeProductOptionsUsingSwatchesTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontCustomerCanChangeProductOptionsUsingSwatchesTest.xml @@ -45,7 +45,7 @@ <!-- Go to shopping cart and update option of configurable product --> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="openShoppingCartPage"/> - <actionGroup ref="StorefrontUpdateCartConfigurableProductWithSwatches" stepKey="updateConfigurableProductInTheCart"> + <actionGroup ref="StorefrontUpdateCartConfigurableProductWithSwatchesActionGroup" stepKey="updateConfigurableProductInTheCart"> <argument name="product" value="_defaultProduct"/> <argument name="productOption" value="e74d3c"/> </actionGroup> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontDisplayAllCharactersOnTextSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontDisplayAllCharactersOnTextSwatchTest.xml index 1bcdd6fcf9a3a..0999b43c48820 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontDisplayAllCharactersOnTextSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontDisplayAllCharactersOnTextSwatchTest.xml @@ -63,7 +63,7 @@ <click selector="{{StorefrontCategorySidebarSection.attributeNthOption(ProductAttributeFrontendLabel.label, '4')}}" stepKey="filterBySwatch4"/> <!-- Deletes the created configurable product--> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteConfigurableProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteConfigurableProduct"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml index c9602ddcd127c..0c179e92adc8d 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml @@ -49,7 +49,7 @@ <!-- Set swatch #1 image using file upload --> <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch1"/> - <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch1"> + <actionGroup ref="OpenSwatchMenuByIndexActionGroup" stepKey="clickSwatch1"> <argument name="index" value="0"/> </actionGroup> <click selector="{{AdminManageSwatchSection.nthUploadFile('1')}}" stepKey="clickUploadFile1"/> @@ -58,7 +58,7 @@ <!-- Set swatch #2 image using the file upload --> <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch2"/> - <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch2"> + <actionGroup ref="OpenSwatchMenuByIndexActionGroup" stepKey="clickSwatch2"> <argument name="index" value="1"/> </actionGroup> <click selector="{{AdminManageSwatchSection.nthUploadFile('2')}}" stepKey="clickUploadFile2"/> @@ -81,24 +81,24 @@ <!-- Create a configurable product to verify the storefront with --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad time="30" stepKey="waitForProductGrid"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateConfigurableProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateConfigurableProduct"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!-- Add image to configurable product --> - <actionGroup ref="addProductImage" stepKey="addFirstImageForProductConfigurable"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addFirstImageForProductConfigurable"> <argument name="image" value="MagentoLogo"/> </actionGroup> <!-- Add image to configurable product --> - <actionGroup ref="addProductImage" stepKey="addSecondImageForProductConfigurable"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addSecondImageForProductConfigurable"> <argument name="image" value="TestImageNew"/> </actionGroup> - <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="fillCategory"/> <!-- Create configurations based off the visual swatch we created earlier --> - <actionGroup ref="createConfigurationsForAttributeWithImages" stepKey="createConfigurations"> + <actionGroup ref="CreateConfigurationsForAttributeWithImagesActionGroup" stepKey="createConfigurations"> <argument name="attributeCode" value="{{ProductAttributeFrontendLabel.label}}"/> <argument name="image" value="TestImageAdobe"/> </actionGroup> @@ -130,18 +130,18 @@ <dontSee selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" userInput="$$createSimpleProduct.name$$" stepKey="dontSeeSimpleProduct"/> <!-- Assert configurable product in storefront product page --> - <actionGroup ref="AssertProductInStorefrontProductPage" stepKey="AssertProductInStorefrontProductPage"> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="AssertProductInStorefrontProductPage"> <argument name="product" value="ApiConfigurableProduct"/> </actionGroup> <!-- Assert configurable product image in storefront product page --> - <actionGroup ref="assertProductImageStorefrontProductPage" stepKey="assertProductImageStorefrontProductPage"> + <actionGroup ref="AssertProductImageStorefrontProductPageActionGroup" stepKey="assertProductImageStorefrontProductPage"> <argument name="product" value="ApiConfigurableProduct"/> <argument name="image" value="MagentoLogo"/> </actionGroup> <!-- Assert configurable product image in storefront product page --> - <actionGroup ref="assertProductImageStorefrontProductPage" stepKey="assertProductSecondImageStorefrontProductPage"> + <actionGroup ref="AssertProductImageStorefrontProductPageActionGroup" stepKey="assertProductSecondImageStorefrontProductPage"> <argument name="product" value="ApiConfigurableProduct"/> <argument name="image" value="TestImageNew"/> </actionGroup> @@ -150,7 +150,7 @@ <click selector="{{StorefrontProductInfoMainSection.swatchOptionByLabel('adobe-thumb')}}" stepKey="clickSwatchOption"/> <!-- Assert swatch option image for configurable product image in storefront product page --> - <actionGroup ref="assertProductImageStorefrontProductPage" stepKey="assertSwatchImageStorefrontProductPage"> + <actionGroup ref="AssertProductImageStorefrontProductPageActionGroup" stepKey="assertSwatchImageStorefrontProductPage"> <argument name="product" value="ApiConfigurableProduct"/> <argument name="image" value="TestImageAdobe"/> </actionGroup> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml index 7bf63d25417e3..0024a95360ef2 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByTextSwatchTest.xml @@ -69,16 +69,16 @@ <!-- Create a configurable product to verify the storefront with --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad stepKey="waitForProductGrid"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateConfigurableProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateConfigurableProduct"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> - <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="fillCategory"/> <!-- Create configurations based off the text swatch we created earlier --> - <actionGroup ref="createConfigurationsForAttribute" stepKey="createConfigurations"> + <actionGroup ref="CreateConfigurationsForAttributeActionGroup" stepKey="createConfigurations"> <argument name="attributeCode" value="{{ProductAttributeFrontendLabel.label}}"/> </actionGroup> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml index fd38c48919416..3cd2e13c9d75d 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByVisualSwatchTest.xml @@ -43,11 +43,11 @@ <!-- Set swatch #1 using the color picker --> <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch1"/> - <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch1"> + <actionGroup ref="OpenSwatchMenuByIndexActionGroup" stepKey="clickSwatch1"> <argument name="index" value="0"/> </actionGroup> <click selector="{{AdminManageSwatchSection.nthChooseColor('1')}}" stepKey="clickChooseColor1"/> - <actionGroup ref="setColorPickerByHex" stepKey="fillHex1"> + <actionGroup ref="SetColorPickerByHexActionGroup" stepKey="fillHex1"> <argument name="nthColorPicker" value="1"/> <argument name="hexColor" value="e74c3c"/> </actionGroup> @@ -55,11 +55,11 @@ <!-- Set swatch #2 using the color picker --> <click selector="{{AdminManageSwatchSection.addSwatch}}" stepKey="clickAddSwatch2"/> - <actionGroup ref="openSwatchMenuByIndex" stepKey="clickSwatch2"> + <actionGroup ref="OpenSwatchMenuByIndexActionGroup" stepKey="clickSwatch2"> <argument name="index" value="1"/> </actionGroup> <click selector="{{AdminManageSwatchSection.nthChooseColor('2')}}" stepKey="clickChooseColor2"/> - <actionGroup ref="setColorPickerByHex" stepKey="fillHex2"> + <actionGroup ref="SetColorPickerByHexActionGroup" stepKey="fillHex2"> <argument name="nthColorPicker" value="2"/> <argument name="hexColor" value="3498db"/> </actionGroup> @@ -81,16 +81,16 @@ <!-- Create a configurable product to verify the storefront with --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> <waitForPageLoad time="30" stepKey="waitForProductGrid"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateConfigurableProduct"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateConfigurableProduct"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> - <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="fillCategory"/> <!-- Create configurations based off the visual watch we created earlier --> - <actionGroup ref="createConfigurationsForAttribute" stepKey="createConfigurations"> + <actionGroup ref="CreateConfigurationsForAttributeActionGroup" stepKey="createConfigurations"> <argument name="attributeCode" value="{{ProductAttributeFrontendLabel.label}}"/> </actionGroup> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml index 2928011662864..208f5cecc6fa9 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontImageColorWhenFilterByColorFilterTest.xml @@ -30,15 +30,15 @@ <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <actionGroup ref="deleteProductAttributeByLabel" stepKey="deleteAttribute"> + <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteAttribute"> <argument name="ProductAttribute" value="visualSwatchAttribute"/> </actionGroup> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPage"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilter"/> - <actionGroup ref="adminDataGridSelectPerPage" stepKey="selectNumberOfProductsPerPage"> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/> + <actionGroup ref="AdminDataGridSelectPerPageActionGroup" stepKey="selectNumberOfProductsPerPage"> <argument name="perPage" value="100"/> </actionGroup> - <actionGroup ref="deleteProductsIfTheyExist" stepKey="deleteAllProducts"/> + <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteAllProducts"/> <actionGroup ref="logout" stepKey="logout"/> </after> <amOnPage url="{{AdminProductEditPage.url($$createConfigProduct.id$$)}}" stepKey="navigateToConfigProductPage"/> @@ -54,19 +54,19 @@ <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/> <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/> <!--Add images to product attribute options--> - <actionGroup ref="addUniqueImageToConfigurableProductOption" stepKey="addImageToConfigurableProductOptionOne"> + <actionGroup ref="AddUniqueImageToConfigurableProductOptionActionGroup" stepKey="addImageToConfigurableProductOptionOne"> <argument name="image" value="MagentoLogo"/> <argument name="frontend_label" value="{{visualSwatchAttribute.default_label}}"/> <argument name="label" value="{{visualSwatchOption1.default_label}}"/> </actionGroup> - <actionGroup ref="addUniqueImageToConfigurableProductOption" stepKey="addImageToConfigurableProductOptionTwo"> + <actionGroup ref="AddUniqueImageToConfigurableProductOptionActionGroup" stepKey="addImageToConfigurableProductOptionTwo"> <argument name="image" value="TestImageNew"/> <argument name="frontend_label" value="{{visualSwatchAttribute.default_label}}"/> <argument name="label" value="{{visualSwatchOption2.default_label}}"/> </actionGroup> <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton"/> <click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnGenerateProductsButton"/> - <actionGroup ref="saveProductForm" stepKey="saveProductForm"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductForm"/> <!-- Perform reindex and flush cache --> <magentoCLI command="indexer:reindex" stepKey="reindex"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSeeProductImagesMatchingProductSwatchesTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSeeProductImagesMatchingProductSwatchesTest.xml index 1717b424e4597..d3f6a2e6ccff7 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSeeProductImagesMatchingProductSwatchesTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSeeProductImagesMatchingProductSwatchesTest.xml @@ -26,14 +26,14 @@ <after> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <actionGroup ref="deleteProductAttribute" stepKey="deleteAttribute"> + <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteAttribute"> <argument name="ProductAttribute" value="VisualSwatchProductAttribute"/> </actionGroup> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearProductAttributeGridFilter"/> - <actionGroup ref="deleteAllDuplicateProductUsingProductGrid" stepKey="deleteAllChildrenProducts"> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductAttributeGridFilter"/> + <actionGroup ref="DeleteAllDuplicateProductUsingProductGridActionGroup" stepKey="deleteAllChildrenProducts"> <argument name="product" value="$$createSimpleProduct$$"/> </actionGroup> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearProductGridFilter"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductGridFilter"/> <actionGroup ref="logout" stepKey="logout"/> </after> @@ -63,58 +63,58 @@ </actionGroup> <!-- Edit configurable product --> - <actionGroup ref="goToProductPageViaID" stepKey="openProductEditPage"> + <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="openProductEditPage"> <argument name="productId" value="$$createSimpleProduct.id$$"/> </actionGroup> <!-- Add images to configurable product --> - <actionGroup ref="addProductImage" stepKey="addFirstImageForProductConfigurable"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addFirstImageForProductConfigurable"> <argument name="image" value="MagentoLogo"/> </actionGroup> - <actionGroup ref="addProductImage" stepKey="addSecondImageForProductConfigurable"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addSecondImageForProductConfigurable"> <argument name="image" value="TestImageNew"/> </actionGroup> <!-- Create configurations based off the visual swatch we created earlier --> - <actionGroup ref="StartCreateConfigurationsForAttribute" stepKey="createConfigurations"> + <actionGroup ref="StartCreateConfigurationsForAttributeActionGroup" stepKey="createConfigurations"> <argument name="attributeCode" value="{{VisualSwatchProductAttribute.attribute_code}}"/> </actionGroup> <!--Add images to configurable product attribute options--> - <actionGroup ref="addUniqueImageToConfigurableProductOption" stepKey="addImageToConfigurableProductOptionOne"> + <actionGroup ref="AddUniqueImageToConfigurableProductOptionActionGroup" stepKey="addImageToConfigurableProductOptionOne"> <argument name="image" value="TestImageAdobe"/> <argument name="frontend_label" value="{{VisualSwatchProductAttribute.attribute_code}}"/> <argument name="label" value="{{visualSwatchOption1.default_label}}"/> </actionGroup> - <actionGroup ref="addUniqueImageToConfigurableProductOption" stepKey="addImageToConfigurableProductOptionTwo"> + <actionGroup ref="AddUniqueImageToConfigurableProductOptionActionGroup" stepKey="addImageToConfigurableProductOptionTwo"> <argument name="image" value="ImageUpload3"/> <argument name="frontend_label" value="{{VisualSwatchProductAttribute.attribute_code}}"/> <argument name="label" value="{{visualSwatchOption2.default_label}}"/> </actionGroup> - <actionGroup ref="GenerateAndSaveConfiguredProductAfterSettingOptions" stepKey="saveProductForm"/> + <actionGroup ref="GenerateAndSaveConfiguredProductAfterSettingOptionsActionGroup" stepKey="saveProductForm"/> <!-- Go to the category page --> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.custom_attributes[url_key]$$)}}" stepKey="goToProductPage"/> - <actionGroup ref="StorefrontAssertActiveProductImage" stepKey="StorefrontAssertActiveProductImage"/> + <actionGroup ref="StorefrontAssertActiveProductImageActionGroup" stepKey="StorefrontAssertActiveProductImage"/> <!--Click a swatch and expect to see the image from the swatch from the configurable product --> - <actionGroup ref="StorefrontSelectSwatchOptionOnProductPageAndCheckImage" stepKey="clickSwatchOption"> + <actionGroup ref="StorefrontSelectSwatchOptionOnProductPageAndCheckImageActionGroup" stepKey="clickSwatchOption"> <argument name="optionName" value="{{visualSwatchOption1.default_label}}"/> <argument name="fileName" value="{{TestImageAdobe.filename}}"/> </actionGroup> - <actionGroup ref="StorefrontAssertFotoramaImageAvailablity" stepKey="seeFirstImageBaseProductInSwatchOption"/> - <actionGroup ref="StorefrontAssertFotoramaImageAvailablity" stepKey="seeSecondImageBaseProductInSwatchOption"> + <actionGroup ref="StorefrontAssertFotoramaImageAvailabilityActionGroup" stepKey="seeFirstImageBaseProductInSwatchOption"/> + <actionGroup ref="StorefrontAssertFotoramaImageAvailabilityActionGroup" stepKey="seeSecondImageBaseProductInSwatchOption"> <argument name="fileName" value="{{TestImageNew.filename}}"/> </actionGroup> - <actionGroup ref="StorefrontSelectSwatchOptionOnProductPageAndCheckImage" stepKey="clickOnSwatchOption2"> + <actionGroup ref="StorefrontSelectSwatchOptionOnProductPageAndCheckImageActionGroup" stepKey="clickOnSwatchOption2"> <argument name="optionName" value="{{visualSwatchOption2.default_label}}"/> <argument name="fileName" value="{{ImageUpload3.filename}}"/> </actionGroup> - <actionGroup ref="StorefrontAssertFotoramaImageAvailablity" stepKey="seeFirstImageBaseProductInSwatchOption2"/> - <actionGroup ref="StorefrontAssertFotoramaImageAvailablity" stepKey="seeSecondImageBaseProductInSwatchOption2"> + <actionGroup ref="StorefrontAssertFotoramaImageAvailabilityActionGroup" stepKey="seeFirstImageBaseProductInSwatchOption2"/> + <actionGroup ref="StorefrontAssertFotoramaImageAvailabilityActionGroup" stepKey="seeSecondImageBaseProductInSwatchOption2"> <argument name="fileName" value="{{TestImageNew.filename}}"/> </actionGroup> </test> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml index 323b10507fe28..119a645252685 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchAttributesDisplayInWidgetCMSTest.xml @@ -30,13 +30,13 @@ <after> <!--delete created configurable product--> - <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteProduct"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> <waitForPageLoad stepKey="waitForAdminProductGridLoad"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> - <actionGroup ref="deleteProductAttributeByLabel" stepKey="deleteAttribute"> + <actionGroup ref="DeleteProductAttributeByLabelActionGroup" stepKey="deleteAttribute"> <argument name="ProductAttribute" value="visualSwatchAttribute"/> </actionGroup> <!--delete root category--> @@ -58,10 +58,10 @@ <!--Create a configurable swatch product via the UI --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> <waitForPageLoad stepKey="waitForProductPage"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProductPage"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> - <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createRootCategory.name$$]" stepKey="searchAndSelectCategory"/> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml index 5e712ebc38292..99b38bd3b34d6 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontSwatchProductWithFileCustomOptionTest.xml @@ -34,19 +34,19 @@ <!-- Create a configurable swatch product via the UI --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> <waitForPageLoad stepKey="waitForProductPage"/> - <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateProductPage"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> - <actionGroup ref="fillMainProductForm" stepKey="fillProductForm"> + <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="searchAndSelectCategory"/> <!--Add swatch attribute to configurable product--> <actionGroup ref="AddVisualSwatchToProductActionGroup" stepKey="addSwatchToProduct"/> <!--Add custom option to configurable product--> - <actionGroup ref="AddProductCustomOptionFile" stepKey="addCustomOptionToProduct"/> + <actionGroup ref="AddProductCustomOptionFileActionGroup" stepKey="addCustomOptionToProduct"/> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/> - + <!--Go to storefront--> <amOnPage url="" stepKey="goToHomePage"/> <waitForPageLoad stepKey="waitForHomePageLoad"/> @@ -85,7 +85,7 @@ <click selector="{{CheckoutCartProductSection.RemoveItem}}" stepKey="deleteCartItem"/> <!--Delete product--> - <actionGroup ref="deleteProductBySku" stepKey="deleteProduct"> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteProduct"> <argument name="sku" value="{{BaseConfigurableProduct.sku}}"/> </actionGroup> </test> diff --git a/app/code/Magento/Swatches/Test/Unit/Block/Adminhtml/Attribute/Edit/Options/TextTest.php b/app/code/Magento/Swatches/Test/Unit/Block/Adminhtml/Attribute/Edit/Options/TextTest.php new file mode 100644 index 0000000000000..e72ebdd4507f4 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Unit/Block/Adminhtml/Attribute/Edit/Options/TextTest.php @@ -0,0 +1,93 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Swatches\Test\Unit\Block\Adminhtml\Attribute\Edit\Options; + +use Magento\Swatches\Block\Adminhtml\Attribute\Edit\Options\Text; + +/** + * Class \Magento\Swatches\Test\Unit\Block\Adminhtml\Attribute\Edit\Options\TextTest + */ +class TextTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject|Text + */ + private $model; + + /** + * Setup environment for test + */ + protected function setUp() + { + $this->model = $this->getMockBuilder(Text::class) + ->disableOriginalConstructor() + ->setMethods(['getReadOnly', 'canManageOptionDefaultOnly', 'getOptionValues']) + ->getMock(); + } + + /** + * Test getJsonConfig with getReadOnly() is true and canManageOptionDefaultOnly() is false + */ + public function testGetJsonConfigDataSet1() + { + $testCase1 = [ + 'dataSet' => [ + 'read_only' => true, + 'can_manage_option_default_only' => false, + 'option_values' => [ + new \Magento\Framework\DataObject(['value' => 6, 'label' => 'red']), + new \Magento\Framework\DataObject(['value' => 6, 'label' => 'blue']), + ] + ], + 'expectedResult' => '{"attributesData":[{"value":6,"label":"red"},{"value":6,"label":"blue"}],' . + '"isSortable":0,"isReadOnly":1}' + + ]; + + $this->executeTest($testCase1); + } + + /** + * Test getJsonConfig with getReadOnly() is false and canManageOptionDefaultOnly() is false + */ + public function testGetJsonConfigDataSet2() + { + $testCase2 = [ + 'dataSet' => [ + 'read_only' => false, + 'can_manage_option_default_only' => false, + 'option_values' => [ + new \Magento\Framework\DataObject(['value' => 6, 'label' => 'red']), + new \Magento\Framework\DataObject(['value' => 6, 'label' => 'blue']), + ] + ], + 'expectedResult' => '{"attributesData":[{"value":6,"label":"red"},{"value":6,"label":"blue"}],' . + '"isSortable":1,"isReadOnly":0}' + + ]; + + $this->executeTest($testCase2); + } + + /** + * Execute test for getJsonConfig() function + */ + public function executeTest($testCase) + { + $this->model->expects($this->any())->method('getReadOnly') + ->willReturn($testCase['dataSet']['read_only']); + $this->model->expects($this->any())->method('canManageOptionDefaultOnly') + ->willReturn($testCase['dataSet']['can_manage_option_default_only']); + $this->model->expects($this->any())->method('getOptionValues')->willReturn( + $testCase['dataSet']['option_values'] + ); + + $this->assertEquals($testCase['expectedResult'], $this->model->getJsonConfig()); + } +} diff --git a/app/code/Magento/Swatches/Test/Unit/Block/Adminhtml/Attribute/Edit/Options/VisualTest.php b/app/code/Magento/Swatches/Test/Unit/Block/Adminhtml/Attribute/Edit/Options/VisualTest.php new file mode 100644 index 0000000000000..f78fedea6afb7 --- /dev/null +++ b/app/code/Magento/Swatches/Test/Unit/Block/Adminhtml/Attribute/Edit/Options/VisualTest.php @@ -0,0 +1,96 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Swatches\Test\Unit\Block\Adminhtml\Attribute\Edit\Options; + +use Magento\Swatches\Block\Adminhtml\Attribute\Edit\Options\Visual; + +/** + * Class \Magento\Swatches\Test\Unit\Block\Adminhtml\Attribute\Edit\Options\VisualTest + */ +class VisualTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject|Visual + */ + private $model; + + /** + * Setup environment for test + */ + protected function setUp() + { + $this->model = $this->getMockBuilder(Visual::class) + ->disableOriginalConstructor() + ->setMethods(['getReadOnly', 'canManageOptionDefaultOnly', 'getOptionValues', 'getUrl']) + ->getMock(); + } + + /** + * Test getJsonConfig with getReadOnly() is true and canManageOptionDefaultOnly() is false + */ + public function testGetJsonConfigDataSet1() + { + $testCase1 = [ + 'dataSet' => [ + 'read_only' => true, + 'can_manage_option_default_only' => false, + 'upload_action_url' => 'http://magento.com/admin/swatches/iframe/show', + 'option_values' => [ + new \Magento\Framework\DataObject(['value' => 6, 'label' => 'red']), + new \Magento\Framework\DataObject(['value' => 6, 'label' => 'blue']), + ] + ], + 'expectedResult' => '{"attributesData":[{"value":6,"label":"red"},{"value":6,"label":"blue"}],' . + '"uploadActionUrl":"http:\/\/magento.com\/admin\/swatches\/iframe\/show","isSortable":0,"isReadOnly":1}' + + ]; + + $this->executeTest($testCase1); + } + + /** + * Test getJsonConfig with getReadOnly() is false and canManageOptionDefaultOnly() is false + */ + public function testGetJsonConfigDataSet2() + { + $testCase1 = [ + 'dataSet' => [ + 'read_only' => false, + 'can_manage_option_default_only' => false, + 'upload_action_url' => 'http://magento.com/admin/swatches/iframe/show', + 'option_values' => [ + new \Magento\Framework\DataObject(['value' => 6, 'label' => 'red']), + new \Magento\Framework\DataObject(['value' => 6, 'label' => 'blue']), + ] + ], + 'expectedResult' => '{"attributesData":[{"value":6,"label":"red"},{"value":6,"label":"blue"}],' . + '"uploadActionUrl":"http:\/\/magento.com\/admin\/swatches\/iframe\/show","isSortable":1,"isReadOnly":0}' + ]; + + $this->executeTest($testCase1); + } + + /** + * Execute test for getJsonConfig() function + */ + public function executeTest($testCase) + { + $this->model->expects($this->any())->method('getReadOnly') + ->willReturn($testCase['dataSet']['read_only']); + $this->model->expects($this->any())->method('canManageOptionDefaultOnly') + ->willReturn($testCase['dataSet']['can_manage_option_default_only']); + $this->model->expects($this->any())->method('getOptionValues')->willReturn( + $testCase['dataSet']['option_values'] + ); + $this->model->expects($this->any())->method('getUrl') + ->willReturn($testCase['dataSet']['upload_action_url']); + + $this->assertEquals($testCase['expectedResult'], $this->model->getJsonConfig()); + } +} diff --git a/app/code/Magento/Swatches/etc/config.xml b/app/code/Magento/Swatches/etc/config.xml index 4140acc4974d6..9d36d9692b295 100644 --- a/app/code/Magento/Swatches/etc/config.xml +++ b/app/code/Magento/Swatches/etc/config.xml @@ -22,10 +22,5 @@ </input_types> </validator_data> </general> - <design> - <watermark> - <swatch_image_position>stretch</swatch_image_position> - </watermark> - </design> </default> </config> diff --git a/app/code/Magento/Swatches/etc/di.xml b/app/code/Magento/Swatches/etc/di.xml index 585cef924e928..593786b993560 100644 --- a/app/code/Magento/Swatches/etc/di.xml +++ b/app/code/Magento/Swatches/etc/di.xml @@ -40,39 +40,6 @@ </argument> </arguments> </type> - <type name="Magento\Theme\Model\Design\Config\MetadataProvider"> - <arguments> - <argument name="metadata" xsi:type="array"> - <item name="watermark_swatch_image_size" xsi:type="array"> - <item name="path" xsi:type="string">design/watermark/swatch_image_size</item> - <item name="fieldset" xsi:type="string">other_settings/watermark/swatch_image</item> - </item> - <item name="watermark_swatch_image_imageOpacity" xsi:type="array"> - <item name="path" xsi:type="string">design/watermark/swatch_image_imageOpacity</item> - <item name="fieldset" xsi:type="string">other_settings/watermark/swatch_image</item> - </item> - <item name="watermark_swatch_image_image" xsi:type="array"> - <item name="path" xsi:type="string">design/watermark/swatch_image_image</item> - <item name="fieldset" xsi:type="string">other_settings/watermark/swatch_image</item> - <item name="backend_model" xsi:type="string">Magento\Theme\Model\Design\Backend\Image</item> - <item name="upload_dir" xsi:type="array"> - <item name="config" xsi:type="string">system/filesystem/media</item> - <item name="scope_info" xsi:type="string">1</item> - <item name="value" xsi:type="string">catalog/product/watermark</item> - </item> - <item name="base_url" xsi:type="array"> - <item name="type" xsi:type="string">media</item> - <item name="scope_info" xsi:type="string">1</item> - <item name="value" xsi:type="string">catalog/product/watermark</item> - </item> - </item> - <item name="watermark_swatch_image_position" xsi:type="array"> - <item name="path" xsi:type="string">design/watermark/swatch_image_position</item> - <item name="fieldset" xsi:type="string">other_settings/watermark/swatch_image</item> - </item> - </argument> - </arguments> - </type> <type name="Magento\Swatches\Model\SwatchAttributeCodes"> <arguments> <argument name="cacheKey" xsi:type="string">swatch-attribute-list</argument> @@ -84,4 +51,13 @@ <type name="Magento\Catalog\Model\Product\Attribute\OptionManagement"> <plugin name="swatches_product_attribute_optionmanagement_plugin" type="Magento\Swatches\Plugin\Eav\Model\Entity\Attribute\OptionManagement"/> </type> + <type name="Magento\Eav\Model\Config"> + <arguments> + <argument name="attributesForPreload" xsi:type="array"> + <item name="catalog_product" xsi:type="array"> + <item name="swatch_image" xsi:type="string">catalog_product</item> + </item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Swatches/registration.php b/app/code/Magento/Swatches/registration.php index 8589c4b71ea47..7f1f20aa18cc2 100644 --- a/app/code/Magento/Swatches/registration.php +++ b/app/code/Magento/Swatches/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Swatches', __DIR__); diff --git a/app/code/Magento/Swatches/view/adminhtml/ui_component/design_config_form.xml b/app/code/Magento/Swatches/view/adminhtml/ui_component/design_config_form.xml deleted file mode 100644 index b38e8ecc6e201..0000000000000 --- a/app/code/Magento/Swatches/view/adminhtml/ui_component/design_config_form.xml +++ /dev/null @@ -1,72 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd"> - <fieldset name="other_settings"> - <fieldset name="watermark"> - <fieldset name="swatch_image" sortOrder="40"> - <settings> - <level>2</level> - <label translate="true">Swatch Image</label> - </settings> - <field name="watermark_swatch_image_image" formElement="imageUploader"> - <settings> - <label translate="true">Image</label> - <componentType>imageUploader</componentType> - </settings> - <formElements> - <imageUploader> - <settings> - <allowedExtensions>jpg jpeg gif png</allowedExtensions> - <maxFileSize>2097152</maxFileSize> - <uploaderConfig> - <param xsi:type="string" name="url">theme/design_config_fileUploader/save</param> - </uploaderConfig> - </settings> - </imageUploader> - </formElements> - </field> - <field name="watermark_swatch_image_imageOpacity" formElement="input"> - <settings> - <validation> - <rule name="validate-number" xsi:type="boolean">true</rule> - </validation> - <dataType>text</dataType> - <addAfter>%</addAfter> - <label translate="true">Image Opacity</label> - <dataScope>watermark_swatch_image_imageOpacity</dataScope> - </settings> - </field> - <field name="watermark_swatch_image_size" component="Magento_Catalog/component/image-size-field" formElement="input"> - <settings> - <notice translate="true">Example format: 200x300.</notice> - <validation> - <rule name="validate-image-size-range" xsi:type="boolean">true</rule> - </validation> - <dataType>text</dataType> - <label translate="true">Image Size</label> - <dataScope>watermark_swatch_image_size</dataScope> - </settings> - </field> - <field name="watermark_swatch_image_position" formElement="select"> - <settings> - <dataType>text</dataType> - <label translate="true">Image Position</label> - <dataScope>watermark_swatch_image_position</dataScope> - </settings> - <formElements> - <select> - <settings> - <options class="Magento\Catalog\Model\Config\Source\Watermark\Position"/> - </settings> - </select> - </formElements> - </field> - </fieldset> - </fieldset> - </fieldset> -</form> diff --git a/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js b/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js index f795f99e8112d..9e48af20ee945 100644 --- a/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js +++ b/app/code/Magento/Swatches/view/adminhtml/web/js/product-attributes.js @@ -31,6 +31,7 @@ define([ defaultValueText: $('#default_value_text'), defaultValueTextarea: $('#default_value_textarea'), defaultValueDate: $('#default_value_date'), + defaultValueDatetime: $('#default_value_datetime'), defaultValueYesno: $('#default_value_yesno'), isGlobal: $('#is_global'), useProductImageForSwatch: $('#use_product_image_for_swatch'), @@ -178,6 +179,7 @@ define([ defaultValueTextVisibility = false, defaultValueTextareaVisibility = false, defaultValueDateVisibility = false, + defaultValueDatetimeVisibility = false, defaultValueYesnoVisibility = false, scopeVisibility = true, useProductImageForSwatch = false, @@ -203,6 +205,10 @@ define([ defaultValueDateVisibility = true; break; + case 'datetime': + defaultValueDatetimeVisibility = true; + break; + case 'boolean': defaultValueYesnoVisibility = true; break; @@ -256,6 +262,7 @@ define([ defaultValueTextVisibility = false; defaultValueTextareaVisibility = false; defaultValueDateVisibility = false; + defaultValueDatetimeVisibility = false; defaultValueYesnoVisibility = false; break; @@ -279,6 +286,7 @@ define([ this.setRowVisibility(this.defaultValueText, defaultValueTextVisibility); this.setRowVisibility(this.defaultValueTextarea, defaultValueTextareaVisibility); this.setRowVisibility(this.defaultValueDate, defaultValueDateVisibility); + this.setRowVisibility(this.defaultValueDatetime, defaultValueDatetimeVisibility); this.setRowVisibility(this.defaultValueYesno, defaultValueYesnoVisibility); this.setRowVisibility(this.isGlobal, scopeVisibility); diff --git a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js b/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js similarity index 99% rename from app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js rename to app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js index 250141a942b10..ee55beb440f59 100644 --- a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js +++ b/app/code/Magento/Swatches/view/base/web/js/swatch-renderer.js @@ -296,6 +296,12 @@ define([ * @private */ _init: function () { + // Don't render the same set of swatches twice + if ($(this.element).attr('data-rendered')) { + return; + } + $(this.element).attr('data-rendered', true); + if (_.isEmpty(this.options.jsonConfig.images)) { this.options.useAjax = true; // creates debounced variant of _LoadProductMedia() @@ -946,6 +952,8 @@ define([ isShow = typeof result != 'undefined' && result.oldPrice.amount !== result.finalPrice.amount; + $productPrice.find('span:first').toggleClass('special-price', isShow); + $product.find(this.options.slyOldPriceSelector)[isShow ? 'show' : 'hide'](); if (typeof result != 'undefined' && result.tierPrices && result.tierPrices.length) { diff --git a/app/code/Magento/SwatchesGraphQl/Model/Resolver/Product/Options/DataProvider/SwatchDataProvider.php b/app/code/Magento/SwatchesGraphQl/Model/Resolver/Product/Options/DataProvider/SwatchDataProvider.php new file mode 100644 index 0000000000000..e7cd4c567da7f --- /dev/null +++ b/app/code/Magento/SwatchesGraphQl/Model/Resolver/Product/Options/DataProvider/SwatchDataProvider.php @@ -0,0 +1,66 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SwatchesGraphQl\Model\Resolver\Product\Options\DataProvider; + +use Magento\Swatches\Helper\Data as SwatchData; +use Magento\Swatches\Helper\Media as SwatchesMedia; +use Magento\Swatches\Model\Swatch; + +/** + * Data provider for options swatches. + */ +class SwatchDataProvider +{ + /** + * @var SwatchData + */ + private $swatchHelper; + + /** + * @var SwatchesMedia + */ + private $swatchMediaHelper; + + /** + * SwatchDataProvider constructor. + * + * @param SwatchData $swatchHelper + * @param SwatchesMedia $swatchMediaHelper + */ + public function __construct( + SwatchData $swatchHelper, + SwatchesMedia $swatchMediaHelper + ) { + $this->swatchHelper = $swatchHelper; + $this->swatchMediaHelper = $swatchMediaHelper; + } + + /** + * Returns swatch data by option ID. + * + * @param string $optionId + * @return array|null + */ + public function getData(string $optionId): ?array + { + $swatches = $this->swatchHelper->getSwatchesByOptionsId([$optionId]); + if (!isset($swatches[$optionId]['type'], $swatches[$optionId]['value'])) { + return null; + } + $type = (int)$swatches[$optionId]['type']; + $value = $swatches[$optionId]['value']; + $data = ['value' => $value, 'type' => $type]; + if ($type === Swatch::SWATCH_TYPE_VISUAL_IMAGE) { + $data['thumbnail'] = $this->swatchMediaHelper->getSwatchAttributeImage( + Swatch::SWATCH_THUMBNAIL_NAME, + $value + ); + } + return $data; + } +} diff --git a/app/code/Magento/SwatchesGraphQl/Model/Resolver/Product/Options/SwatchData.php b/app/code/Magento/SwatchesGraphQl/Model/Resolver/Product/Options/SwatchData.php new file mode 100644 index 0000000000000..980f779e8e9f6 --- /dev/null +++ b/app/code/Magento/SwatchesGraphQl/Model/Resolver/Product/Options/SwatchData.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SwatchesGraphQl\Model\Resolver\Product\Options; + +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\SwatchesGraphQl\Model\Resolver\Product\Options\DataProvider\SwatchDataProvider; + +/** + * Class SwatchData + * + * Product swatch data resolver, used for GraphQL request processing + */ +class SwatchData implements ResolverInterface +{ + /** + * @var SwatchDataProvider + */ + private $swatchDataProvider; + + /** + * SwatchData constructor. + * + * @param SwatchDataProvider $swatchDataProvider + */ + public function __construct( + SwatchDataProvider $swatchDataProvider + ) { + $this->swatchDataProvider = $swatchDataProvider; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + return $this->swatchDataProvider->getData($value['value_index']); + } +} diff --git a/app/code/Magento/SwatchesGraphQl/Model/Resolver/Product/Options/SwatchDataTypeResolver.php b/app/code/Magento/SwatchesGraphQl/Model/Resolver/Product/Options/SwatchDataTypeResolver.php new file mode 100644 index 0000000000000..add6f7123b921 --- /dev/null +++ b/app/code/Magento/SwatchesGraphQl/Model/Resolver/Product/Options/SwatchDataTypeResolver.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SwatchesGraphQl\Model\Resolver\Product\Options; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Query\Resolver\TypeResolverInterface; +use Magento\Swatches\Model\Swatch; + +/** + * Resolver for swatch data interface. + */ +class SwatchDataTypeResolver implements TypeResolverInterface +{ + /** + * @inheritdoc + */ + public function resolveType(array $data): string + { + switch ($data['type']) { + case Swatch::SWATCH_TYPE_TEXTUAL: + return 'TextSwatchData'; + case Swatch::SWATCH_TYPE_VISUAL_COLOR: + return 'ColorSwatchData'; + case Swatch::SWATCH_TYPE_VISUAL_IMAGE: + return 'ImageSwatchData'; + default: + throw new LocalizedException(__('Unsupported swatch type')); + } + } +} diff --git a/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php b/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php index 14c4ba62c8a47..c14ec68b9ab38 100644 --- a/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php +++ b/app/code/Magento/SwatchesGraphQl/Plugin/Filters/DataProviderPlugin.php @@ -36,6 +36,7 @@ class DataProviderPlugin * * @param FiltersProvider $filtersProvider * @param \Magento\Swatches\Helper\Data $swatchHelper + * @param \Magento\Swatches\Block\LayeredNavigation\RenderLayered $renderLayered */ public function __construct( FiltersProvider $filtersProvider, @@ -53,12 +54,19 @@ public function __construct( * @param Filters $subject * @param \Closure $proceed * @param string $layerType + * @param array|null $attributesToFilter * @return array + * @throws \Magento\Framework\Exception\LocalizedException * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * phpcs:disable Generic.Metrics.NestingLevel */ - public function aroundGetData(Filters $subject, \Closure $proceed, string $layerType) : array - { + public function aroundGetData( + Filters $subject, + \Closure $proceed, + string $layerType, + $attributesToFilter = null + ) : array { $swatchFilters = []; /** @var AbstractFilter $filter */ foreach ($this->filtersProvider->getFilters($layerType) as $filter) { @@ -69,7 +77,7 @@ public function aroundGetData(Filters $subject, \Closure $proceed, string $layer } } - $filtersData = $proceed($layerType); + $filtersData = $proceed($layerType, $attributesToFilter); foreach ($filtersData as $groupKey => $filterGroup) { /** @var AbstractFilter $swatchFilter */ @@ -92,4 +100,5 @@ public function aroundGetData(Filters $subject, \Closure $proceed, string $layer return $filtersData; } + //phpcs:enable } diff --git a/app/code/Magento/SwatchesGraphQl/etc/graphql/di.xml b/app/code/Magento/SwatchesGraphQl/etc/graphql/di.xml index 34f65d8e30e57..07391aead332b 100644 --- a/app/code/Magento/SwatchesGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/SwatchesGraphQl/etc/graphql/di.xml @@ -16,4 +16,4 @@ </argument> </arguments> </type> -</config> \ No newline at end of file +</config> diff --git a/app/code/Magento/SwatchesGraphQl/etc/schema.graphqls b/app/code/Magento/SwatchesGraphQl/etc/schema.graphqls index bdd2631e7aa10..c51468ccd2856 100644 --- a/app/code/Magento/SwatchesGraphQl/etc/schema.graphqls +++ b/app/code/Magento/SwatchesGraphQl/etc/schema.graphqls @@ -26,4 +26,24 @@ type SwatchLayerFilterItem implements LayerFilterItemInterface, SwatchLayerFilte type SwatchData { type: String @doc(description: "Type of swatch filter item: 1 - text, 2 - image") value: String @doc(description: "Value for swatch item (text or image link)") -} \ No newline at end of file +} + +type ConfigurableProductOptionsValues { + swatch_data: SwatchDataInterface @doc(description: "Swatch data for configurable product option") @resolver(class: "Magento\\SwatchesGraphQl\\Model\\Resolver\\Product\\Options\\SwatchData") +} + +interface SwatchDataInterface @typeResolver(class: "Magento\\SwatchesGraphQl\\Model\\Resolver\\Product\\Options\\SwatchDataTypeResolver") { + value: String @doc(description: "Value of swatch item (HEX color code, image link or textual value)") +} + +type ImageSwatchData implements SwatchDataInterface { + thumbnail: String @doc(description: "Thumbnail swatch image URL") +} + +type TextSwatchData implements SwatchDataInterface { + +} + +type ColorSwatchData implements SwatchDataInterface { + +} diff --git a/app/code/Magento/SwatchesLayeredNavigation/registration.php b/app/code/Magento/SwatchesLayeredNavigation/registration.php index f773212378ce1..8ddcbb0753810 100644 --- a/app/code/Magento/SwatchesLayeredNavigation/registration.php +++ b/app/code/Magento/SwatchesLayeredNavigation/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_SwatchesLayeredNavigation', __DIR__); diff --git a/app/code/Magento/Tax/Block/Sales/Order/Tax.php b/app/code/Magento/Tax/Block/Sales/Order/Tax.php index 0adaec9311ee6..9c992185b4e1d 100644 --- a/app/code/Magento/Tax/Block/Sales/Order/Tax.php +++ b/app/code/Magento/Tax/Block/Sales/Order/Tax.php @@ -106,8 +106,8 @@ protected function _addTax($after = 'discount') { $taxTotal = new \Magento\Framework\DataObject(['code' => 'tax', 'block_name' => $this->getNameInLayout()]); $totals = $this->getParentBlock()->getTotals(); - if ($totals['grand_total']) { - $this->getParentBlock()->addTotalBefore($taxTotal, 'grand_total'); + if (isset($totals['grand_total_incl'])) { + $this->getParentBlock()->addTotal($taxTotal, 'grand_total'); } $this->getParentBlock()->addTotal($taxTotal, $after); return $this; @@ -214,6 +214,7 @@ protected function _initSubtotal() protected function _initShipping() { $store = $this->getStore(); + /** @var \Magento\Sales\Block\Order\Totals $parent */ $parent = $this->getParentBlock(); $shipping = $parent->getTotal('shipping'); if (!$shipping) { @@ -232,12 +233,14 @@ protected function _initShipping() $baseShippingIncl = $baseShipping + (double)$this->_source->getBaseShippingTaxAmount(); } + $couponDescription = $this->getCouponDescription(); + $totalExcl = new \Magento\Framework\DataObject( [ 'code' => 'shipping', 'value' => $shipping, 'base_value' => $baseShipping, - 'label' => __('Shipping & Handling (Excl.Tax)'), + 'label' => __('Shipping & Handling (Excl.Tax)') . $couponDescription, ] ); $totalIncl = new \Magento\Framework\DataObject( @@ -245,7 +248,7 @@ protected function _initShipping() 'code' => 'shipping_incl', 'value' => $shippingIncl, 'base_value' => $baseShippingIncl, - 'label' => __('Shipping & Handling (Incl.Tax)'), + 'label' => __('Shipping & Handling (Incl.Tax)') . $couponDescription, ] ); $parent->addTotal($totalExcl, 'shipping'); @@ -319,8 +322,8 @@ protected function _initGrandTotal() 'label' => __('Grand Total (Incl.Tax)'), ] ); - $parent->addTotal($totalExcl, 'grand_total'); - $parent->addTotal($totalIncl, 'tax'); + $parent->addTotal($totalIncl, 'grand_total'); + $parent->addTotal($totalExcl, 'tax'); $this->_addTax('grand_total'); } return $this; @@ -355,4 +358,25 @@ public function getValueProperties() { return $this->getParentBlock()->getValueProperties(); } + + /** + * Returns additional information about coupon code if it is not displayed in totals. + * + * @return string + */ + private function getCouponDescription(): string + { + $couponDescription = ""; + + /** @var \Magento\Sales\Block\Order\Totals $parent */ + $parent = $this->getParentBlock(); + $couponCode = $parent->getSource() + ->getCouponCode(); + + if ($couponCode && !$parent->getTotal('discount')) { + $couponDescription = " ({$couponCode})"; + } + + return $couponDescription; + } } diff --git a/app/code/Magento/Tax/Model/Config.php b/app/code/Magento/Tax/Model/Config.php index 09212ce90bf58..201158eae25dd 100644 --- a/app/code/Magento/Tax/Model/Config.php +++ b/app/code/Magento/Tax/Model/Config.php @@ -14,11 +14,15 @@ use Magento\Store\Model\Store; /** + * Class to set flags for tax display setting + * * @SuppressWarnings(PHPMD.ExcessivePublicCount) */ class Config { - // tax notifications + /** + * Tax notifications + */ const XML_PATH_TAX_NOTIFICATION_IGNORE_DISCOUNT = 'tax/notification/ignore_discount'; const XML_PATH_TAX_NOTIFICATION_IGNORE_PRICE_DISPLAY = 'tax/notification/ignore_price_display'; @@ -70,7 +74,11 @@ class Config const XML_PATH_DISPLAY_CART_SHIPPING = 'tax/cart_display/shipping'; - /** @deprecated */ + /** + * Tax cart display discount + * + * @deprecated + */ const XML_PATH_DISPLAY_CART_DISCOUNT = 'tax/cart_display/discount'; const XML_PATH_DISPLAY_CART_GRANDTOTAL = 'tax/cart_display/grandtotal'; @@ -88,7 +96,11 @@ class Config const XML_PATH_DISPLAY_SALES_SHIPPING = 'tax/sales_display/shipping'; - /** @deprecated */ + /** + * Tax sales display discount + * + * @deprecated + */ const XML_PATH_DISPLAY_SALES_DISCOUNT = 'tax/sales_display/discount'; const XML_PATH_DISPLAY_SALES_GRANDTOTAL = 'tax/sales_display/grandtotal'; @@ -231,6 +243,7 @@ public function discountTax($store = null) /** * Get taxes/discounts calculation sequence. + * * This sequence depends on "Apply Customer Tax" and "Apply Discount On Prices" configuration options. * * @param null|int|string|Store $store @@ -353,6 +366,8 @@ public function setShippingPriceIncludeTax($flag) } /** + * Return the flag for display sales for cart prices including tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -366,6 +381,8 @@ public function displayCartPricesInclTax($store = null) } /** + * Return the flag for display sales for cart prices excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -379,6 +396,8 @@ public function displayCartPricesExclTax($store = null) } /** + * Return the flag for display sales for cart prices both including and excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -392,6 +411,8 @@ public function displayCartPricesBoth($store = null) } /** + * Return the flag for display sales for cart subtotal including tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -405,6 +426,8 @@ public function displayCartSubtotalInclTax($store = null) } /** + * Return the flag for display sales for cart subtotal excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -418,6 +441,8 @@ public function displayCartSubtotalExclTax($store = null) } /** + * Return the flag for display sales for cart subtotal both including and excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -431,6 +456,8 @@ public function displayCartSubtotalBoth($store = null) } /** + * Return the flag for display sales for cart shipping including tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -444,6 +471,8 @@ public function displayCartShippingInclTax($store = null) } /** + * Return the flag for display sales for cart shipping excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -457,6 +486,8 @@ public function displayCartShippingExclTax($store = null) } /** + * Return the flag for display sales for shipping both including and excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -470,6 +501,8 @@ public function displayCartShippingBoth($store = null) } /** + * Return the flag for display cart discount for including tax + * * @param null|string|bool|int|Store $store * @return bool * @deprecated 100.1.3 @@ -484,6 +517,8 @@ public function displayCartDiscountInclTax($store = null) } /** + * Return the flag for display cart discount for excluding tax + * * @param null|string|bool|int|Store $store * @return bool * @deprecated 100.1.3 @@ -498,6 +533,8 @@ public function displayCartDiscountExclTax($store = null) } /** + * Return the flag for display cart discount for both including and excluding tax + * * @param null|string|bool|int|Store $store * @return bool * @deprecated 100.1.3 @@ -512,6 +549,8 @@ public function displayCartDiscountBoth($store = null) } /** + * Return the flag for display cart tax with grand total for both including and excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -525,6 +564,8 @@ public function displayCartTaxWithGrandTotal($store = null) } /** + * Return the flag for display cart full summary + * * @param null|string|bool|int|Store $store * @return bool */ @@ -538,6 +579,8 @@ public function displayCartFullSummary($store = null) } /** + * Return the flag for display cart zero tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -551,6 +594,8 @@ public function displayCartZeroTax($store = null) } /** + * Return the flag for display sales prices for including tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -564,6 +609,8 @@ public function displaySalesPricesInclTax($store = null) } /** + * Return the flag for display sales prices for excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -577,6 +624,8 @@ public function displaySalesPricesExclTax($store = null) } /** + * Return the flag for display sales prices for both including and excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -590,6 +639,8 @@ public function displaySalesPricesBoth($store = null) } /** + * Return the flag for display sales subtotal for including tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -603,6 +654,8 @@ public function displaySalesSubtotalInclTax($store = null) } /** + * Return the flag for display sales subtotal for excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -616,6 +669,8 @@ public function displaySalesSubtotalExclTax($store = null) } /** + * Return the flag for display sales subtotal for both including and excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -629,6 +684,8 @@ public function displaySalesSubtotalBoth($store = null) } /** + * Return the flag for display sales for shipping including tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -642,6 +699,8 @@ public function displaySalesShippingInclTax($store = null) } /** + * Return the flag for display sales for shipping excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -655,6 +714,8 @@ public function displaySalesShippingExclTax($store = null) } /** + * Return the flag for display sales for shipping both including and excluding tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -668,6 +729,8 @@ public function displaySalesShippingBoth($store = null) } /** + * Return the flag for display sales discount for including tax + * * @param null|string|bool|int|Store $store * @return bool * @deprecated 100.1.3 @@ -682,6 +745,8 @@ public function displaySalesDiscountInclTax($store = null) } /** + * Return the flag for display sales discount for excluding tax + * * @param null|string|bool|int|Store $store * @return bool * @deprecated 100.1.3 @@ -696,6 +761,8 @@ public function displaySalesDiscountExclTax($store = null) } /** + * Return the flag for display sales discount for both including and excluding tax + * * @param null|string|bool|int|Store $store * @return bool * @deprecated 100.1.3 @@ -710,6 +777,8 @@ public function displaySalesDiscountBoth($store = null) } /** + * Return the flag for display sales tax with grand total + * * @param null|string|bool|int|Store $store * @return bool */ @@ -723,6 +792,8 @@ public function displaySalesTaxWithGrandTotal($store = null) } /** + * Return the flag for display sales full summary + * * @param null|string|bool|int|Store $store * @return bool */ @@ -736,6 +807,8 @@ public function displaySalesFullSummary($store = null) } /** + * Return the flag for display sales zero tax + * * @param null|string|bool|int|Store $store * @return bool */ @@ -829,15 +902,16 @@ public function getInfoUrl($store = null) /** * Check if necessary do product price conversion + * * If it necessary will be returned conversion type (minus or plus) * * @param null|int|string|Store $store - * @return bool|int + * @return bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function needPriceConversion($store = null) { - $res = 0; + $res = false; $priceIncludesTax = $this->priceIncludesTax($store) || $this->getNeedUseShippingExcludeTax(); if ($priceIncludesTax) { switch ($this->getPriceDisplayType($store)) { @@ -845,7 +919,7 @@ public function needPriceConversion($store = null) case self::DISPLAY_TYPE_BOTH: return self::PRICE_CONVERSION_MINUS; case self::DISPLAY_TYPE_INCLUDING_TAX: - $res = false; + $res = $this->displayCartPricesInclTax($store); break; default: break; diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php index 77b3cfa3a08bb..c70c715d32c1b 100644 --- a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php +++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php @@ -383,6 +383,7 @@ public function mapItems( $priceIncludesTax, $useBaseCurrency ); + //phpcs:ignore Magento2.Performance.ForeachArrayMerge $itemDataObjects = array_merge($itemDataObjects, $extraTaxableItems); } } else { @@ -394,6 +395,7 @@ public function mapItems( $priceIncludesTax, $useBaseCurrency ); + //phpcs:ignore Magento2.Performance.ForeachArrayMerge $itemDataObjects = array_merge($itemDataObjects, $extraTaxableItems); } } @@ -592,6 +594,7 @@ protected function processProductItems( $total->setBaseSubtotalTotalInclTax($baseSubtotalInclTax); $total->setBaseSubtotalInclTax($baseSubtotalInclTax); $shippingAssignment->getShipping()->getAddress()->setBaseSubtotalTotalInclTax($baseSubtotalInclTax); + $shippingAssignment->getShipping()->getAddress()->setBaseTaxAmount($baseTax); return $this; } @@ -799,6 +802,7 @@ public function convertAppliedTaxes($appliedTaxes, $baseAppliedTaxes, $extraInfo 'rates' => $rates, ]; if (!empty($extraInfo)) { + //phpcs:ignore Magento2.Performance.ForeachArrayMerge $appliedTaxArray = array_merge($appliedTaxArray, $extraInfo); } diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AddCustomTaxRateActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AddCustomTaxRateActionGroup.xml new file mode 100644 index 0000000000000..97d59a51bb68e --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AddCustomTaxRateActionGroup.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="AddCustomTaxRateActionGroup" extends="addNewTaxRateNoZip"> + <annotations> + <description>EXTENDS: addNewTaxRateNoZip. Removes 'fillZipCode' and 'fillRate'. Fills in the Zip Code and Rate. PLEASE NOTE: The values are Hardcoded.</description> + </annotations> + + <remove keyForRemoval="fillZipCode"/> + <remove keyForRemoval="fillRate"/> + <fillField stepKey="fillZipCode" selector="{{AdminTaxRulesSection.zipCode}}" userInput="US-NY-*-Rate 2" after="fillTaxIdentifier"/> + <fillField stepKey="fillRate" selector="{{AdminTaxRulesSection.rate}}" userInput="0" after="selectCountry"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AddCustomerTaxClassActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AddCustomerTaxClassActionGroup.xml new file mode 100644 index 0000000000000..d627448edf730 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AddCustomerTaxClassActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!--Add Customer Tax Class--> + <actionGroup name="AddCustomerTaxClassActionGroup"> + <annotations> + <description>Adds the provided Customer Tax Class on the Admin Tax Rule creation/edit page.</description> + </annotations> + <arguments> + <argument name="customerTaxClassName" type="string"/> + </arguments> + + <!--Click Additional Settings--> + <click stepKey="clickAdditionalSettings" selector="{{AdminTaxRulesSection.additionalSettings}}"/> + <!--Click Product Add New Tax Class Button--> + <click stepKey="clickCustomerAddNewTaxClassBtn" selector="{{AdminTaxRulesSection.customerAddNewTaxClass}}"/> + <!--Fill field--> + <fillField stepKey="fillCustomerNewTaxClass" selector="{{AdminTaxRulesSection.fieldCustomerNewTaxClass}}" userInput="{{customerTaxClassName}}"/> + <!-- Save Product tax rate --> + <click stepKey="saveProdTaxRate" selector="{{AdminTaxRulesSection.saveCustomerNewTaxClass}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AddNewTaxRateNoZipActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AddNewTaxRateNoZipActionGroup.xml new file mode 100644 index 0000000000000..8a2945130c925 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AddNewTaxRateNoZipActionGroup.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="AddNewTaxRateNoZipActionGroup"> + <annotations> + <description>Goes to the Admin Tax Rules grid page. Adds the provided Tax Code.</description> + </annotations> + <arguments> + <argument name="taxCode"/> + </arguments> + + <!-- Go to the tax rate page --> + <click stepKey="addNewTaxRate" selector="{{AdminTaxRulesSection.addNewTaxRate}}"/> + + <!-- Fill out a new tax rate --> + <fillField stepKey="fillTaxIdentifier" selector="{{AdminTaxRulesSection.taxIdentifier}}" userInput="{{taxCode.state}}-{{taxCode.rate}}"/> + <fillField stepKey="fillZipCode" selector="{{AdminTaxRulesSection.zipCode}}" userInput="{{taxCode.zip}}"/> + <selectOption stepKey="selectState" selector="{{AdminTaxRulesSection.state}}" userInput="{{taxCode.state}}"/> + <selectOption stepKey="selectCountry" selector="{{AdminTaxRulesSection.country}}" userInput="{{taxCode.country}}"/> + <fillField stepKey="fillRate" selector="{{AdminTaxRulesSection.rate}}" userInput="{{taxCode.rate}}"/> + + <!-- Save the tax rate --> + <click stepKey="saveTaxRate" selector="{{AdminTaxRulesSection.save}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AddNewTaxRuleActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AddNewTaxRuleActionGroup.xml new file mode 100644 index 0000000000000..6bc7ef6f540c3 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AddNewTaxRuleActionGroup.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="AddNewTaxRuleActionGroup"> + <annotations> + <description>Goes to the Admin Tax Rules grid page. Clicks on the Add New Tax Rule button.</description> + </annotations> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AddProductTaxClassActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AddProductTaxClassActionGroup.xml new file mode 100644 index 0000000000000..943d05ee37504 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AddProductTaxClassActionGroup.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="AddProductTaxClassActionGroup"> + <annotations> + <description>Adds the provided Product Tax Class to a Tax Rule. Clicks on Save.</description> + </annotations> + <arguments> + <argument name="prodTaxClassName" type="string"/> + </arguments> + + <!--Click Additional Settings--> + <click stepKey="clickAdditionalSettings" selector="{{AdminTaxRulesSection.additionalSettings}}"/> + <!--Click Product Add New Tax Class Button--> + <click stepKey="clickProdAddNewTaxClassBtn" selector="{{AdminTaxRulesSection.productAddNewTaxClass}}"/> + <!--Fill field--> + <fillField stepKey="fillProdNewTaxClass" selector="{{AdminTaxRulesSection.fieldProdNewTaxClass}}" userInput="{{prodTaxClassName}}"/> + <!-- Save Product tax rate --> + <click stepKey="saveProdTaxRate" selector="{{AdminTaxRulesSection.saveProdNewTaxClass}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminCustomerTaxClassActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminCustomerTaxClassActionGroup.xml deleted file mode 100644 index f711633e1e00b..0000000000000 --- a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminCustomerTaxClassActionGroup.xml +++ /dev/null @@ -1,50 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Add Customer Tax Class--> - <actionGroup name="addCustomerTaxClass"> - <annotations> - <description>Adds the provided Customer Tax Class on the Admin Tax Rule creation/edit page.</description> - </annotations> - <arguments> - <argument name="customerTaxClassName" type="string"/> - </arguments> - - <!--Click Additional Settings--> - <click stepKey="clickAdditionalSettings" selector="{{AdminTaxRulesSection.additionalSettings}}"/> - <!--Click Product Add New Tax Class Button--> - <click stepKey="clickCustomerAddNewTaxClassBtn" selector="{{AdminTaxRulesSection.customerAddNewTaxClass}}"/> - <!--Fill field--> - <fillField stepKey="fillCustomerNewTaxClass" selector="{{AdminTaxRulesSection.fieldCustomerNewTaxClass}}" userInput="{{customerTaxClassName}}"/> - <!-- Save Product tax rate --> - <click stepKey="saveProdTaxRate" selector="{{AdminTaxRulesSection.saveCustomerNewTaxClass}}"/> - </actionGroup> - - <!--Delete Product Tax Class--> - <actionGroup name="deleteCustomerTaxClass"> - <annotations> - <description>Goes to the Admin New Tax Rule page. Deletes the provided Tax Class Name.</description> - </annotations> - <arguments> - <argument name="taxClassName" type="string"/> - </arguments> - - <!-- Go to tax rule page --> - <amOnPage url="{{AdminNewTaxRulePage.url}}" stepKey="goToNewTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="clickAdditionalSettings" selector="{{AdminTaxRulesSection.additionalSettings}}"/> - <scrollTo stepKey="scrollToAdditionalSettings" selector="{{AdminTaxRulesSection.additionalSettings}}"/> - <moveMouseOver stepKey="hoverDeleteElement" selector="{{AdminTaxRulesSection.deleteTaxClassName(taxClassName)}}"/> - <click stepKey="deleteFirstTaxClass" selector="{{AdminTaxRulesSection.deleteTaxClass(taxClassName)}}"/> - <waitForElementVisible selector="{{AdminTaxRulesSection.popUpDialogOK}}" stepKey="waitForElementBecomeVisible"/> - <click stepKey="acceptPopUpDialog" selector="{{AdminTaxRulesSection.popUpDialogOK}}"/> - <waitForPageLoad stepKey="waitForProductTaxClassDeleted"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminTaxActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminTaxActionGroup.xml deleted file mode 100644 index e1674481b8bb9..0000000000000 --- a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminTaxActionGroup.xml +++ /dev/null @@ -1,264 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!-- Change the tax configuration to display in cart and checkout flow --> - <actionGroup name="editTaxConfigurationByUI"> - <annotations> - <description>Goes to the 'Configuration' page for 'Tax'. Enables the display of Taxes in the Storefront Shopping Cart and Checkout page.</description> - </annotations> - - <!-- navigate to the tax configuration page --> - <amOnPage url="{{AdminTaxConfigurationPage.url}}" stepKey="goToAdminTaxPage"/> - <waitForPageLoad stepKey="waitForTaxConfigLoad"/> - - <!-- change the default state to California --> - <scrollTo selector="#tax_defaults-head" x="0" y="-80" stepKey="scrollToTaxDefaults"/> - - <!-- conditionalClick twice to fix some flaky behavior --> - <conditionalClick stepKey="clickCalculationSettings" selector="{{AdminConfigureTaxSection.defaultDestination}}" dependentSelector="#tax_defaults" visible="false"/> - <conditionalClick stepKey="clickCalculationSettingsAgain" selector="{{AdminConfigureTaxSection.defaultDestination}}" dependentSelector="#tax_defaults" visible="false"/> - <uncheckOption stepKey="clickDefaultState" selector="{{AdminConfigureTaxSection.systemValueDefaultState}}"/> - <selectOption stepKey="selectDefaultState" selector="{{AdminConfigureTaxSection.dropdownDefaultState}}" userInput="California"/> - <fillField stepKey="fillDefaultPostCode" selector="{{AdminConfigureTaxSection.defaultPostCode}}" userInput="*"/> - - <!-- change the options for shopping cart display to show tax --> - <scrollTo selector="#tax_cart_display-head" x="0" y="-80" stepKey="scrollToTaxShoppingCartDisplay"/> - - <!-- conditionalClick twice to fix some flaky behavior --> - <conditionalClick stepKey="clickShoppingCartDisplaySettings" selector="{{AdminConfigureTaxSection.shoppingCartDisplay}}" dependentSelector="#tax_cart_display" visible="false"/> - <conditionalClick stepKey="clickShoppingCartDisplaySettingsAgain" selector="{{AdminConfigureTaxSection.shoppingCartDisplay}}" dependentSelector="#tax_cart_display" visible="false"/> - <uncheckOption stepKey="clickTaxTotalCart" selector="{{AdminConfigureTaxSection.systemValueIncludeTaxTotalCart}}"/> - <selectOption stepKey="selectTaxTotalCart" selector="{{AdminConfigureTaxSection.dropdownIncludeTaxTotalCart}}" userInput="Yes"/> - <uncheckOption stepKey="clickDisplayTaxSummaryCart" selector="{{AdminConfigureTaxSection.systemValueDisplayTaxSummaryCart}}"/> - <selectOption stepKey="selectDisplayTaxSummaryCart" selector="{{AdminConfigureTaxSection.dropdownDisplayTaxSummaryCart}}" userInput="Yes"/> - <uncheckOption stepKey="clickDisplayZeroTaxCart" selector="{{AdminConfigureTaxSection.systemValueDisplayZeroTaxCart}}"/> - <selectOption stepKey="selectDisplayZeroTaxCart" selector="{{AdminConfigureTaxSection.dropdownDisplayZeroTaxCart}}" userInput="Yes"/> - - <!-- change the options for orders, invoices, credit memos display to show tax --> - <scrollTo selector="#tax_sales_display-head" x="0" y="-80" stepKey="scrollToTaxSalesDisplay"/> - - <!-- conditionalClick twice to fix some flaky behavior --> - <conditionalClick stepKey="clickOrdersInvoicesCreditSales" selector="{{AdminConfigureTaxSection.ordersInvoicesCreditSales}}" dependentSelector="#tax_sales_display" visible="false"/> - <conditionalClick stepKey="clickOrdersInvoicesCreditSalesAgain" selector="{{AdminConfigureTaxSection.ordersInvoicesCreditSales}}" dependentSelector="#tax_sales_display" visible="false"/> - <uncheckOption stepKey="clickTaxTotalSales" selector="{{AdminConfigureTaxSection.systemValueIncludeTaxTotalSales}}"/> - <selectOption stepKey="selectTaxTotalSales" selector="{{AdminConfigureTaxSection.dropdownIncludeTaxTotalSales}}" userInput="Yes"/> - <uncheckOption stepKey="clickDisplayTaxSummarySales" selector="{{AdminConfigureTaxSection.systemValueDisplayTaxSummarySales}}"/> - <selectOption stepKey="selectDisplayTaxSummarySales" selector="{{AdminConfigureTaxSection.dropdownDisplayTaxSummarySales}}" userInput="Yes"/> - <uncheckOption stepKey="clickDisplayZeroTaxSales" selector="{{AdminConfigureTaxSection.systemValueDisplayZeroTaxSales}}"/> - <selectOption stepKey="selectDisplayZeroTaxSales" selector="{{AdminConfigureTaxSection.dropdownDisplayZeroTaxSales}}" userInput="Yes"/> - - <!-- Save the settings --> - <scrollToTopOfPage stepKey="scrollToTop"/> - <click stepKey="saveTaxOptions" selector="{{AdminCategoryMainActionsSection.SaveButton}}"/> - <waitForPageLoad stepKey="waitForTaxSaved"/> - <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the configuration."/> - </actionGroup> - - <actionGroup name="changeToDefaultTaxConfigurationUI"> - <annotations> - <description>Goes to the 'Configuration' page for 'Tax'. Set the Tax Configuration Settings to the Default values. Clicks on Save. Validates that the Success Message is present and correct.</description> - </annotations> - - <!-- navigate to the tax configuration page --> - <amOnPage url="{{AdminTaxConfigurationPage.url}}" stepKey="goToAdminTaxPage"/> - <waitForPageLoad stepKey="waitForTaxConfigLoad"/> - - <!-- change the default state to none --> - <!-- conditionalClick twice to fix some flaky behavior --> - <conditionalClick stepKey="clickCalculationSettings" selector="{{AdminConfigureTaxSection.defaultDestination}}" dependentSelector="{{AdminConfigureTaxSection.systemValueDefaultState}}" visible="false"/> - <conditionalClick stepKey="clickCalculationSettingsAgain" selector="{{AdminConfigureTaxSection.defaultDestination}}" dependentSelector="{{AdminConfigureTaxSection.systemValueDefaultState}}" visible="false"/> - <checkOption stepKey="clickDefaultState" selector="{{AdminConfigureTaxSection.systemValueDefaultState}}"/> - <selectOption stepKey="selectDefaultState" selector="{{AdminConfigureTaxSection.dropdownDefaultState}}" userInput="California"/> - <fillField stepKey="fillDefaultPostCode" selector="{{AdminConfigureTaxSection.defaultPostCode}}" userInput=""/> - - <!-- change the options for shopping cart display to not show tax --> - <!-- conditionalClick twice to fix some flaky behavior --> - <conditionalClick stepKey="clickShoppingCartDisplaySettings" selector="{{AdminConfigureTaxSection.shoppingCartDisplay}}" dependentSelector="{{AdminConfigureTaxSection.systemValueIncludeTaxTotalCart}}" visible="false"/> - <conditionalClick stepKey="clickShoppingCartDisplaySettingsAgain" selector="{{AdminConfigureTaxSection.shoppingCartDisplay}}" dependentSelector="{{AdminConfigureTaxSection.systemValueIncludeTaxTotalCart}}" visible="false"/> - <checkOption stepKey="clickTaxTotalCart" selector="{{AdminConfigureTaxSection.systemValueIncludeTaxTotalCart}}"/> - <selectOption stepKey="selectTaxTotalCart" selector="{{AdminConfigureTaxSection.dropdownIncludeTaxTotalCart}}" userInput="Yes"/> - <checkOption stepKey="clickDisplayTaxSummaryCart" selector="{{AdminConfigureTaxSection.systemValueDisplayTaxSummaryCart}}"/> - <selectOption stepKey="selectDisplayTaxSummaryCart" selector="{{AdminConfigureTaxSection.dropdownDisplayTaxSummaryCart}}" userInput="Yes"/> - <checkOption stepKey="clickDisplayZeroTaxCart" selector="{{AdminConfigureTaxSection.systemValueDisplayZeroTaxCart}}"/> - <selectOption stepKey="selectDisplayZeroTaxCart" selector="{{AdminConfigureTaxSection.dropdownDisplayZeroTaxCart}}" userInput="Yes"/> - - <!-- change the options for orders, invoices, credit memos display to not show tax --> - <!-- conditionalClick twice to fix some flaky behavior --> - <conditionalClick stepKey="clickOrdersInvoicesCreditSales" selector="{{AdminConfigureTaxSection.ordersInvoicesCreditSales}}" dependentSelector="{{AdminConfigureTaxSection.systemValueIncludeTaxTotalSales}}" visible="false"/> - <conditionalClick stepKey="clickOrdersInvoicesCreditSalesAgain" selector="{{AdminConfigureTaxSection.ordersInvoicesCreditSales}}" dependentSelector="{{AdminConfigureTaxSection.systemValueIncludeTaxTotalSales}}" visible="false"/> - <checkOption stepKey="clickTaxTotalSales" selector="{{AdminConfigureTaxSection.systemValueIncludeTaxTotalSales}}"/> - <selectOption stepKey="selectTaxTotalSales" selector="{{AdminConfigureTaxSection.dropdownIncludeTaxTotalSales}}" userInput="Yes"/> - <checkOption stepKey="clickDisplayTaxSummarySales" selector="{{AdminConfigureTaxSection.systemValueDisplayTaxSummarySales}}"/> - <selectOption stepKey="selectDisplayTaxSummarySales" selector="{{AdminConfigureTaxSection.dropdownDisplayTaxSummarySales}}" userInput="Yes"/> - <checkOption stepKey="clickDisplayZeroTaxSales" selector="{{AdminConfigureTaxSection.systemValueDisplayZeroTaxSales}}"/> - <selectOption stepKey="selectDisplayZeroTaxSales" selector="{{AdminConfigureTaxSection.dropdownDisplayZeroTaxSales}}" userInput="Yes"/> - - <!-- Save the settings --> - <scrollToTopOfPage stepKey="scrollToTop"/> - <click stepKey="saveTaxOptions" selector="{{AdminCategoryMainActionsSection.SaveButton}}"/> - <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the configuration."/> - </actionGroup> - - <actionGroup name="addCustomTaxRate" extends="addNewTaxRateNoZip"> - <annotations> - <description>EXTENDS: addNewTaxRateNoZip. Removes 'fillZipCode' and 'fillRate'. Fills in the Zip Code and Rate. PLEASE NOTE: The values are Hardcoded.</description> - </annotations> - - <remove keyForRemoval="fillZipCode"/> - <remove keyForRemoval="fillRate"/> - <fillField stepKey="fillZipCode" selector="{{AdminTaxRulesSection.zipCode}}" userInput="US-NY-*-Rate 2" after="fillTaxIdentifier"/> - <fillField stepKey="fillRate" selector="{{AdminTaxRulesSection.rate}}" userInput="0" after="selectCountry"/> - </actionGroup> - - <!-- Action group to add a tax rate when on a tax rule configuration page --> - <!-- Must already be on a tax rule configuration page or a new tax rule page --> - <actionGroup name="addNewTaxRateNoZip"> - <annotations> - <description>Goes to the Admin Tax Rules grid page. Adds the provided Tax Code.</description> - </annotations> - <arguments> - <argument name="taxCode"/> - </arguments> - - <!-- Go to the tax rate page --> - <click stepKey="addNewTaxRate" selector="{{AdminTaxRulesSection.addNewTaxRate}}"/> - - <!-- Fill out a new tax rate --> - <fillField stepKey="fillTaxIdentifier" selector="{{AdminTaxRulesSection.taxIdentifier}}" userInput="{{taxCode.state}}-{{taxCode.rate}}"/> - <fillField stepKey="fillZipCode" selector="{{AdminTaxRulesSection.zipCode}}" userInput="{{taxCode.zip}}"/> - <selectOption stepKey="selectState" selector="{{AdminTaxRulesSection.state}}" userInput="{{taxCode.state}}"/> - <selectOption stepKey="selectCountry" selector="{{AdminTaxRulesSection.country}}" userInput="{{taxCode.country}}"/> - <fillField stepKey="fillRate" selector="{{AdminTaxRulesSection.rate}}" userInput="{{taxCode.rate}}"/> - - <!-- Save the tax rate --> - <click stepKey="saveTaxRate" selector="{{AdminTaxRulesSection.save}}"/> - </actionGroup> - - <!--Set Tax Class for Shipping--> - <actionGroup name="changeShippingTaxClass"> - <annotations> - <description>Goes to the 'Configuration' page for 'Tax'. Sets the 'Tax Class for Shipping' configuration setting to 'Taxable Goods'. Clicks on Save. Validates that the Success Message is present and correct.</description> - </annotations> - - <!--Select Configuration menu from Store--> - <click selector="{{AdminMenuSection.stores}}" stepKey="clickOnSTORES"/> - <waitForPageLoad stepKey="waitForConfiguration"/> - <click selector="{{AdminMenuSection.configuration}}" stepKey="clickOnConfigurations"/> - <waitForPageLoad stepKey="waitForSales"/> - - <!--Double click the same to fix flaky issue with redirection to Dashboard--> - <click selector="{{AdminMenuSection.stores}}" stepKey="clickOnSTORES1"/> - <waitForPageLoad stepKey="waitForConfiguration1"/> - <click selector="{{AdminMenuSection.configuration}}" stepKey="clickOnConfigurations1"/> - <waitForPageLoad stepKey="waitForSales1" time="5"/> - - <!--Change default tax class for Shipping on Taxable Goods--> - <click selector="{{ConfigurationListSection.sales}}" stepKey="clickOnSales"/> - <waitForPageLoad stepKey="waitForPaymentMethods"/> - <click selector="{{AdminConfigureTaxSection.salesTax}}" stepKey="clickOnTax"/> - <waitForPageLoad stepKey="waitForTax"/> - <seeInCurrentUrl url="{{AdminTaxConfigurationPage.url}}" stepKey="adminTaxConfiguration"/> - <seeElement selector="{{AdminConfigureTaxSection.taxClasses}}" stepKey="taxClassSectionC"/> - <click selector="{{AdminConfigureTaxSection.taxClasses}}" stepKey="openTaxClassSection"/> - <click selector="{{AdminConfigureTaxSection.taxShippingClassSystem}}" stepKey="uncheckSystemValue"/> - <selectOption selector="{{AdminConfigureTaxSection.taxClassShipping}}" userInput="Taxable Goods" stepKey="setTaxClassForShipping"/> - - <!-- Save the settings --> - <scrollToTopOfPage stepKey="scrollToTop"/> - <click stepKey="saveTaxOptions" selector="{{AdminCategoryMainActionsSection.SaveButton}}"/> - <waitForPageLoad stepKey="waitForTaxSaved"/> - <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the configuration."/> - </actionGroup> - - <actionGroup name="setDefaultShippingTaxClass"> - <annotations> - <description>Goes to the 'Configuration' page via the Admin Side Menu. Sets the 'Tax Class for Shipping' to the System Default. Clicks on Save. Validates that the Success Message is present and correct.</description> - </annotations> - - <!--Select Configuration menu from Store--> - <click selector="{{AdminMenuSection.stores}}" stepKey="clickOnSTORES"/> - <waitForPageLoad stepKey="waitForConfiguration"/> - <click selector="{{AdminMenuSection.configuration}}" stepKey="clickOnConfigurations"/> - <waitForPageLoad stepKey="waitForSales"/> - - <!--Double click the same to fix flaky issue with redirection to Dashboard--> - <click selector="{{AdminMenuSection.stores}}" stepKey="clickOnSTORES1"/> - <waitForPageLoad stepKey="waitForConfiguration1"/> - <click selector="{{AdminMenuSection.configuration}}" stepKey="clickOnConfigurations1"/> - <waitForPageLoad stepKey="waitForSales1"/> - - <!--Change default tax class for Shipping on Taxable Goods--> - <click selector="{{ConfigurationListSection.sales}}" stepKey="clickOnSales"/> - <waitForPageLoad stepKey="waitForPaymentMethods"/> - <click selector="{{AdminConfigureTaxSection.salesTax}}" stepKey="clickOnTax"/> - <waitForPageLoad stepKey="waitForTax"/> - <seeElement selector="{{AdminConfigureTaxSection.taxClasses}}" stepKey="taxClassSectionC"/> - <click selector="{{AdminConfigureTaxSection.taxShippingClassSystem}}" stepKey="checkSystemDefaultValue"/> - <click selector="{{AdminConfigureTaxSection.taxClasses}}" stepKey="closeTaxClassSection"/> - - <!-- Save the settings --> - <scrollToTopOfPage stepKey="scrollToTop"/> - <click stepKey="saveTaxOptions" selector="{{AdminCategoryMainActionsSection.SaveButton}}"/> - <waitForPageLoad stepKey="waitForTaxSaved"/> - <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the configuration."/> - </actionGroup> - - <!--Add Product Tax Class--> - <actionGroup name="addProductTaxClass"> - <annotations> - <description>Adds the provided Product Tax Class to a Tax Rule. Clicks on Save.</description> - </annotations> - <arguments> - <argument name="prodTaxClassName" type="string"/> - </arguments> - - <!--Click Additional Settings--> - <click stepKey="clickAdditionalSettings" selector="{{AdminTaxRulesSection.additionalSettings}}"/> - <!--Click Product Add New Tax Class Button--> - <click stepKey="clickProdAddNewTaxClassBtn" selector="{{AdminTaxRulesSection.productAddNewTaxClass}}"/> - <!--Fill field--> - <fillField stepKey="fillProdNewTaxClass" selector="{{AdminTaxRulesSection.fieldProdNewTaxClass}}" userInput="{{prodTaxClassName}}"/> - <!-- Save Product tax rate --> - <click stepKey="saveProdTaxRate" selector="{{AdminTaxRulesSection.saveProdNewTaxClass}}"/> - </actionGroup> - - <!--Add New Tax Rule --> - <actionGroup name="addNewTaxRuleActionGroup"> - <annotations> - <description>Goes to the Admin Tax Rules grid page. Clicks on the Add New Tax Rule button.</description> - </annotations> - - <!-- Go to tax rule page --> - <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="addNewTaxRate" selector="{{AdminGridMainControls.add}}"/> - </actionGroup> - - <!--Delete Product Tax Class--> - <actionGroup name="deleteProductTaxClass"> - <annotations> - <description>Goes to the Admin Tax Rule creation page. Deletes the provided Tax Class.</description> - </annotations> - <arguments> - <argument name="taxClassName" type="string"/> - </arguments> - - <!-- Go to tax rule page --> - <amOnPage url="{{AdminNewTaxRulePage.url}}" stepKey="goToNewTaxRulePage"/> - <waitForPageLoad stepKey="waitForTaxRatePage"/> - <click stepKey="clickAdditionalSettings" selector="{{AdminTaxRulesSection.additionalSettings}}"/> - <scrollTo stepKey="scrollToAdditionalSettings" selector="{{AdminTaxRulesSection.additionalSettings}}"/> - <moveMouseOver stepKey="hoverDeleteElement" selector="{{AdminTaxRulesSection.deleteTaxClassName(taxClassName)}}"/> - <click stepKey="deleteFirstTaxClass" selector="{{AdminTaxRulesSection.deleteTaxClass(taxClassName)}}"/> - <waitForElementVisible selector="{{AdminTaxRulesSection.popUpDialogOK}}" stepKey="waitForElementBecomeVisible"/> - <click stepKey="acceptPopUpDialog" selector="{{AdminTaxRulesSection.popUpDialogOK}}"/> - <waitForPageLoad stepKey="waitForProductTaxClassDeleted"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/ChangeShippingTaxClassActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/ChangeShippingTaxClassActionGroup.xml new file mode 100644 index 0000000000000..8bd9136831890 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/ChangeShippingTaxClassActionGroup.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ChangeShippingTaxClassActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page for 'Tax'. Sets the 'Tax Class for Shipping' configuration setting to 'Taxable Goods'. Clicks on Save. Validates that the Success Message is present and correct.</description> + </annotations> + + <!--Select Configuration menu from Store--> + <click selector="{{AdminMenuSection.stores}}" stepKey="clickOnSTORES"/> + <waitForPageLoad stepKey="waitForConfiguration"/> + <click selector="{{AdminMenuSection.configuration}}" stepKey="clickOnConfigurations"/> + <waitForPageLoad stepKey="waitForSales"/> + + <!--Double click the same to fix flaky issue with redirection to Dashboard--> + <click selector="{{AdminMenuSection.stores}}" stepKey="clickOnSTORES1"/> + <waitForPageLoad stepKey="waitForConfiguration1"/> + <click selector="{{AdminMenuSection.configuration}}" stepKey="clickOnConfigurations1"/> + <waitForPageLoad stepKey="waitForSales1" time="5"/> + + <!--Change default tax class for Shipping on Taxable Goods--> + <click selector="{{ConfigurationListSection.sales}}" stepKey="clickOnSales"/> + <waitForPageLoad stepKey="waitForPaymentMethods"/> + <click selector="{{AdminConfigureTaxSection.salesTax}}" stepKey="clickOnTax"/> + <waitForPageLoad stepKey="waitForTax"/> + <seeInCurrentUrl url="{{AdminTaxConfigurationPage.url}}" stepKey="adminTaxConfiguration"/> + <seeElement selector="{{AdminConfigureTaxSection.taxClasses}}" stepKey="taxClassSectionC"/> + <click selector="{{AdminConfigureTaxSection.taxClasses}}" stepKey="openTaxClassSection"/> + <click selector="{{AdminConfigureTaxSection.taxShippingClassSystem}}" stepKey="uncheckSystemValue"/> + <selectOption selector="{{AdminConfigureTaxSection.taxClassShipping}}" userInput="Taxable Goods" stepKey="setTaxClassForShipping"/> + + <!-- Save the settings --> + <scrollToTopOfPage stepKey="scrollToTop"/> + <click stepKey="saveTaxOptions" selector="{{AdminCategoryMainActionsSection.SaveButton}}"/> + <waitForPageLoad stepKey="waitForTaxSaved"/> + <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the configuration."/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/ChangeToDefaultTaxConfigurationUIActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/ChangeToDefaultTaxConfigurationUIActionGroup.xml new file mode 100644 index 0000000000000..c93d4187cafa6 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/ChangeToDefaultTaxConfigurationUIActionGroup.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ChangeToDefaultTaxConfigurationUIActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page for 'Tax'. Set the Tax Configuration Settings to the Default values. Clicks on Save. Validates that the Success Message is present and correct.</description> + </annotations> + + <!-- navigate to the tax configuration page --> + <amOnPage url="{{AdminTaxConfigurationPage.url}}" stepKey="goToAdminTaxPage"/> + <waitForPageLoad stepKey="waitForTaxConfigLoad"/> + + <!-- change the default state to none --> + <!-- conditionalClick twice to fix some flaky behavior --> + <conditionalClick stepKey="clickCalculationSettings" selector="{{AdminConfigureTaxSection.defaultDestination}}" dependentSelector="{{AdminConfigureTaxSection.systemValueDefaultState}}" visible="false"/> + <conditionalClick stepKey="clickCalculationSettingsAgain" selector="{{AdminConfigureTaxSection.defaultDestination}}" dependentSelector="{{AdminConfigureTaxSection.systemValueDefaultState}}" visible="false"/> + <checkOption stepKey="clickDefaultState" selector="{{AdminConfigureTaxSection.systemValueDefaultState}}"/> + <selectOption stepKey="selectDefaultState" selector="{{AdminConfigureTaxSection.dropdownDefaultState}}" userInput="California"/> + <fillField stepKey="fillDefaultPostCode" selector="{{AdminConfigureTaxSection.defaultPostCode}}" userInput=""/> + + <!-- change the options for shopping cart display to not show tax --> + <!-- conditionalClick twice to fix some flaky behavior --> + <conditionalClick stepKey="clickShoppingCartDisplaySettings" selector="{{AdminConfigureTaxSection.shoppingCartDisplay}}" dependentSelector="{{AdminConfigureTaxSection.systemValueIncludeTaxTotalCart}}" visible="false"/> + <conditionalClick stepKey="clickShoppingCartDisplaySettingsAgain" selector="{{AdminConfigureTaxSection.shoppingCartDisplay}}" dependentSelector="{{AdminConfigureTaxSection.systemValueIncludeTaxTotalCart}}" visible="false"/> + <checkOption stepKey="clickTaxTotalCart" selector="{{AdminConfigureTaxSection.systemValueIncludeTaxTotalCart}}"/> + <selectOption stepKey="selectTaxTotalCart" selector="{{AdminConfigureTaxSection.dropdownIncludeTaxTotalCart}}" userInput="Yes"/> + <checkOption stepKey="clickDisplayTaxSummaryCart" selector="{{AdminConfigureTaxSection.systemValueDisplayTaxSummaryCart}}"/> + <selectOption stepKey="selectDisplayTaxSummaryCart" selector="{{AdminConfigureTaxSection.dropdownDisplayTaxSummaryCart}}" userInput="Yes"/> + <checkOption stepKey="clickDisplayZeroTaxCart" selector="{{AdminConfigureTaxSection.systemValueDisplayZeroTaxCart}}"/> + <selectOption stepKey="selectDisplayZeroTaxCart" selector="{{AdminConfigureTaxSection.dropdownDisplayZeroTaxCart}}" userInput="Yes"/> + + <!-- change the options for orders, invoices, credit memos display to not show tax --> + <!-- conditionalClick twice to fix some flaky behavior --> + <conditionalClick stepKey="clickOrdersInvoicesCreditSales" selector="{{AdminConfigureTaxSection.ordersInvoicesCreditSales}}" dependentSelector="{{AdminConfigureTaxSection.systemValueIncludeTaxTotalSales}}" visible="false"/> + <conditionalClick stepKey="clickOrdersInvoicesCreditSalesAgain" selector="{{AdminConfigureTaxSection.ordersInvoicesCreditSales}}" dependentSelector="{{AdminConfigureTaxSection.systemValueIncludeTaxTotalSales}}" visible="false"/> + <checkOption stepKey="clickTaxTotalSales" selector="{{AdminConfigureTaxSection.systemValueIncludeTaxTotalSales}}"/> + <selectOption stepKey="selectTaxTotalSales" selector="{{AdminConfigureTaxSection.dropdownIncludeTaxTotalSales}}" userInput="Yes"/> + <checkOption stepKey="clickDisplayTaxSummarySales" selector="{{AdminConfigureTaxSection.systemValueDisplayTaxSummarySales}}"/> + <selectOption stepKey="selectDisplayTaxSummarySales" selector="{{AdminConfigureTaxSection.dropdownDisplayTaxSummarySales}}" userInput="Yes"/> + <checkOption stepKey="clickDisplayZeroTaxSales" selector="{{AdminConfigureTaxSection.systemValueDisplayZeroTaxSales}}"/> + <selectOption stepKey="selectDisplayZeroTaxSales" selector="{{AdminConfigureTaxSection.dropdownDisplayZeroTaxSales}}" userInput="Yes"/> + + <!-- Save the settings --> + <scrollToTopOfPage stepKey="scrollToTop"/> + <click stepKey="saveTaxOptions" selector="{{AdminCategoryMainActionsSection.SaveButton}}"/> + <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the configuration."/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/DeleteCustomerTaxClassActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/DeleteCustomerTaxClassActionGroup.xml new file mode 100644 index 0000000000000..4507265cab1d0 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/DeleteCustomerTaxClassActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteCustomerTaxClassActionGroup"> + <annotations> + <description>Goes to the Admin New Tax Rule page. Deletes the provided Tax Class Name.</description> + </annotations> + <arguments> + <argument name="taxClassName" type="string"/> + </arguments> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminNewTaxRulePage.url}}" stepKey="goToNewTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="clickAdditionalSettings" selector="{{AdminTaxRulesSection.additionalSettings}}"/> + <scrollTo stepKey="scrollToAdditionalSettings" selector="{{AdminTaxRulesSection.additionalSettings}}"/> + <moveMouseOver stepKey="hoverDeleteElement" selector="{{AdminTaxRulesSection.deleteTaxClassName(taxClassName)}}"/> + <click stepKey="deleteFirstTaxClass" selector="{{AdminTaxRulesSection.deleteTaxClass(taxClassName)}}"/> + <waitForElementVisible selector="{{AdminTaxRulesSection.popUpDialogOK}}" stepKey="waitForElementBecomeVisible"/> + <click stepKey="acceptPopUpDialog" selector="{{AdminTaxRulesSection.popUpDialogOK}}"/> + <waitForPageLoad stepKey="waitForProductTaxClassDeleted"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/DeleteProductTaxClassActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/DeleteProductTaxClassActionGroup.xml new file mode 100644 index 0000000000000..b365f68318101 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/DeleteProductTaxClassActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteProductTaxClassActionGroup"> + <annotations> + <description>Goes to the Admin Tax Rule creation page. Deletes the provided Tax Class.</description> + </annotations> + <arguments> + <argument name="taxClassName" type="string"/> + </arguments> + + <!-- Go to tax rule page --> + <amOnPage url="{{AdminNewTaxRulePage.url}}" stepKey="goToNewTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click stepKey="clickAdditionalSettings" selector="{{AdminTaxRulesSection.additionalSettings}}"/> + <scrollTo stepKey="scrollToAdditionalSettings" selector="{{AdminTaxRulesSection.additionalSettings}}"/> + <moveMouseOver stepKey="hoverDeleteElement" selector="{{AdminTaxRulesSection.deleteTaxClassName(taxClassName)}}"/> + <click stepKey="deleteFirstTaxClass" selector="{{AdminTaxRulesSection.deleteTaxClass(taxClassName)}}"/> + <waitForElementVisible selector="{{AdminTaxRulesSection.popUpDialogOK}}" stepKey="waitForElementBecomeVisible"/> + <click stepKey="acceptPopUpDialog" selector="{{AdminTaxRulesSection.popUpDialogOK}}"/> + <waitForPageLoad stepKey="waitForProductTaxClassDeleted"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/EditTaxConfigurationByUIActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/EditTaxConfigurationByUIActionGroup.xml new file mode 100644 index 0000000000000..da56a305d0e94 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/EditTaxConfigurationByUIActionGroup.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="EditTaxConfigurationByUIActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page for 'Tax'. Enables the display of Taxes in the Storefront Shopping Cart and Checkout page.</description> + </annotations> + + <!-- navigate to the tax configuration page --> + <amOnPage url="{{AdminTaxConfigurationPage.url}}" stepKey="goToAdminTaxPage"/> + <waitForPageLoad stepKey="waitForTaxConfigLoad"/> + + <!-- change the default state to California --> + <scrollTo selector="#tax_defaults-head" x="0" y="-80" stepKey="scrollToTaxDefaults"/> + + <!-- conditionalClick twice to fix some flaky behavior --> + <conditionalClick stepKey="clickCalculationSettings" selector="{{AdminConfigureTaxSection.defaultDestination}}" dependentSelector="#tax_defaults" visible="false"/> + <conditionalClick stepKey="clickCalculationSettingsAgain" selector="{{AdminConfigureTaxSection.defaultDestination}}" dependentSelector="#tax_defaults" visible="false"/> + <uncheckOption stepKey="clickDefaultState" selector="{{AdminConfigureTaxSection.systemValueDefaultState}}"/> + <selectOption stepKey="selectDefaultState" selector="{{AdminConfigureTaxSection.dropdownDefaultState}}" userInput="California"/> + <fillField stepKey="fillDefaultPostCode" selector="{{AdminConfigureTaxSection.defaultPostCode}}" userInput="*"/> + + <!-- change the options for shopping cart display to show tax --> + <scrollTo selector="#tax_cart_display-head" x="0" y="-80" stepKey="scrollToTaxShoppingCartDisplay"/> + + <!-- conditionalClick twice to fix some flaky behavior --> + <conditionalClick stepKey="clickShoppingCartDisplaySettings" selector="{{AdminConfigureTaxSection.shoppingCartDisplay}}" dependentSelector="#tax_cart_display" visible="false"/> + <conditionalClick stepKey="clickShoppingCartDisplaySettingsAgain" selector="{{AdminConfigureTaxSection.shoppingCartDisplay}}" dependentSelector="#tax_cart_display" visible="false"/> + <uncheckOption stepKey="clickTaxTotalCart" selector="{{AdminConfigureTaxSection.systemValueIncludeTaxTotalCart}}"/> + <selectOption stepKey="selectTaxTotalCart" selector="{{AdminConfigureTaxSection.dropdownIncludeTaxTotalCart}}" userInput="Yes"/> + <uncheckOption stepKey="clickDisplayTaxSummaryCart" selector="{{AdminConfigureTaxSection.systemValueDisplayTaxSummaryCart}}"/> + <selectOption stepKey="selectDisplayTaxSummaryCart" selector="{{AdminConfigureTaxSection.dropdownDisplayTaxSummaryCart}}" userInput="Yes"/> + <uncheckOption stepKey="clickDisplayZeroTaxCart" selector="{{AdminConfigureTaxSection.systemValueDisplayZeroTaxCart}}"/> + <selectOption stepKey="selectDisplayZeroTaxCart" selector="{{AdminConfigureTaxSection.dropdownDisplayZeroTaxCart}}" userInput="Yes"/> + + <!-- change the options for orders, invoices, credit memos display to show tax --> + <scrollTo selector="#tax_sales_display-head" x="0" y="-80" stepKey="scrollToTaxSalesDisplay"/> + + <!-- conditionalClick twice to fix some flaky behavior --> + <conditionalClick stepKey="clickOrdersInvoicesCreditSales" selector="{{AdminConfigureTaxSection.ordersInvoicesCreditSales}}" dependentSelector="#tax_sales_display" visible="false"/> + <conditionalClick stepKey="clickOrdersInvoicesCreditSalesAgain" selector="{{AdminConfigureTaxSection.ordersInvoicesCreditSales}}" dependentSelector="#tax_sales_display" visible="false"/> + <uncheckOption stepKey="clickTaxTotalSales" selector="{{AdminConfigureTaxSection.systemValueIncludeTaxTotalSales}}"/> + <selectOption stepKey="selectTaxTotalSales" selector="{{AdminConfigureTaxSection.dropdownIncludeTaxTotalSales}}" userInput="Yes"/> + <uncheckOption stepKey="clickDisplayTaxSummarySales" selector="{{AdminConfigureTaxSection.systemValueDisplayTaxSummarySales}}"/> + <selectOption stepKey="selectDisplayTaxSummarySales" selector="{{AdminConfigureTaxSection.dropdownDisplayTaxSummarySales}}" userInput="Yes"/> + <uncheckOption stepKey="clickDisplayZeroTaxSales" selector="{{AdminConfigureTaxSection.systemValueDisplayZeroTaxSales}}"/> + <selectOption stepKey="selectDisplayZeroTaxSales" selector="{{AdminConfigureTaxSection.dropdownDisplayZeroTaxSales}}" userInput="Yes"/> + + <!-- Save the settings --> + <scrollToTopOfPage stepKey="scrollToTop"/> + <click stepKey="saveTaxOptions" selector="{{AdminCategoryMainActionsSection.SaveButton}}"/> + <waitForPageLoad stepKey="waitForTaxSaved"/> + <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the configuration."/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/SetDefaultShippingTaxClassActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/SetDefaultShippingTaxClassActionGroup.xml new file mode 100644 index 0000000000000..25e2b2d376151 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/SetDefaultShippingTaxClassActionGroup.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SetDefaultShippingTaxClassActionGroup"> + <annotations> + <description>Goes to the 'Configuration' page via the Admin Side Menu. Sets the 'Tax Class for Shipping' to the System Default. Clicks on Save. Validates that the Success Message is present and correct.</description> + </annotations> + + <!--Select Configuration menu from Store--> + <click selector="{{AdminMenuSection.stores}}" stepKey="clickOnSTORES"/> + <waitForPageLoad stepKey="waitForConfiguration"/> + <click selector="{{AdminMenuSection.configuration}}" stepKey="clickOnConfigurations"/> + <waitForPageLoad stepKey="waitForSales"/> + + <!--Double click the same to fix flaky issue with redirection to Dashboard--> + <click selector="{{AdminMenuSection.stores}}" stepKey="clickOnSTORES1"/> + <waitForPageLoad stepKey="waitForConfiguration1"/> + <click selector="{{AdminMenuSection.configuration}}" stepKey="clickOnConfigurations1"/> + <waitForPageLoad stepKey="waitForSales1"/> + + <!--Change default tax class for Shipping on Taxable Goods--> + <click selector="{{ConfigurationListSection.sales}}" stepKey="clickOnSales"/> + <waitForPageLoad stepKey="waitForPaymentMethods"/> + <click selector="{{AdminConfigureTaxSection.salesTax}}" stepKey="clickOnTax"/> + <waitForPageLoad stepKey="waitForTax"/> + <seeElement selector="{{AdminConfigureTaxSection.taxClasses}}" stepKey="taxClassSectionC"/> + <click selector="{{AdminConfigureTaxSection.taxShippingClassSystem}}" stepKey="checkSystemDefaultValue"/> + <click selector="{{AdminConfigureTaxSection.taxClasses}}" stepKey="closeTaxClassSection"/> + + <!-- Save the settings --> + <scrollToTopOfPage stepKey="scrollToTop"/> + <click stepKey="saveTaxOptions" selector="{{AdminCategoryMainActionsSection.SaveButton}}"/> + <waitForPageLoad stepKey="waitForTaxSaved"/> + <see stepKey="seeSuccess" selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the configuration."/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/Data/TaxCodeData.xml b/app/code/Magento/Tax/Test/Mftf/Data/TaxCodeData.xml index 27c89162b5cea..4b8d79117eb24 100644 --- a/app/code/Magento/Tax/Test/Mftf/Data/TaxCodeData.xml +++ b/app/code/Magento/Tax/Test/Mftf/Data/TaxCodeData.xml @@ -26,6 +26,12 @@ <data key="zip">*</data> <data key="rate">8.375</data> </entity> + <entity name="SimpleTaxNYRate" type="tax"> + <data key="state">New York</data> + <data key="country">United States</data> + <data key="zip">*</data> + <data key="rate">20.00</data> + </entity> <entity name="SimpleTaxCA" type="tax"> <data key="state">California</data> <data key="country">United States</data> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxReportsSection.xml b/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxReportsSection.xml index 80101687e173e..71bc4cbceff83 100644 --- a/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxReportsSection.xml +++ b/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxReportsSection.xml @@ -7,7 +7,7 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminTaxReportsSection"> <element name="refreshStatistics" type="button" selector="//a[contains(text(),'here')]"/> <element name="fromDate" type="input" selector="#sales_report_from"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxCalcWithApplyTaxOnSettingTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxCalcWithApplyTaxOnSettingTest.xml index d249af983f715..fbf7873d37438 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxCalcWithApplyTaxOnSettingTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxCalcWithApplyTaxOnSettingTest.xml @@ -28,8 +28,8 @@ <requiredEntity createDataKey="createCategory"/> </createData> <actionGroup ref="LoginActionGroup" stepKey="loginAsAdmin"/> - <actionGroup ref="SetTaxClassForShipping" stepKey="setTaxClass"/> - <actionGroup ref="SetTaxApplyOnSetting" stepKey="setApplyTaxOnSetting"> + <actionGroup ref="SetTaxClassForShippingActionGroup" stepKey="setTaxClass"/> + <actionGroup ref="SetTaxApplyOnSettingActionGroup" stepKey="setApplyTaxOnSetting"> <argument name="userInput" value="Original price only"/> </actionGroup> <amOnPage url="{{AdminEditTaxRulePage.url($$createTaxRule.id$$)}}" stepKey="navigateToEditTaxRulePage"/> @@ -45,19 +45,19 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> <deleteData stepKey="deleteTaxRate" createDataKey="initialTaxRate" /> - <actionGroup ref="DisableTaxApplyOnOriginalPrice" stepKey="setApplyTaxOffSetting"> + <actionGroup ref="DisableTaxApplyOnOriginalPriceActionGroup" stepKey="setApplyTaxOffSetting"> <argument name="userInput" value="Custom price if available"/> </actionGroup> - <actionGroup ref="ResetTaxClassForShipping" stepKey="resetTaxClassForShipping"/> + <actionGroup ref="ResetTaxClassForShippingActionGroup" stepKey="resetTaxClassForShipping"/> <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="navigateToNewOrderPageNewCustomerSingleStore" stepKey="gotoNewOrderCreationPage"/> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToOrder"> + <actionGroup ref="NavigateToNewOrderPageNewCustomerSingleStoreActionGroup" stepKey="gotoNewOrderCreationPage"/> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSimpleProductToOrder"> <argument name="product" value="$$createProduct$$"></argument> </actionGroup> <fillField selector="{{AdminOrderFormAccountSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillEmailField"/> - <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerAddress"> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerAddress"> <argument name="customer" value="Simple_US_Customer"/> <argument name="address" value="US_Address_CA"/> </actionGroup> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml index 628d189823a52..e90602441261d 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml @@ -25,11 +25,11 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <!-- Go to tax rule page --> - <actionGroup ref="addNewTaxRuleActionGroup" stepKey="addFirstTaxRuleActionGroup"/> + <actionGroup ref="AddNewTaxRuleActionGroup" stepKey="addFirstTaxRuleActionGroup"/> <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="TaxRule1"/> <!-- Add NY and CA tax rules --> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addNYTaxRate"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> <argument name="taxCode" value="SimpleTaxWithZipCode"/> </actionGroup> @@ -43,10 +43,10 @@ <waitForPageLoad stepKey="waitForNewTaxRuleCreated"/> <!-- Go to tax rule page to create second Tax Rule--> - <actionGroup ref="addNewTaxRuleActionGroup" stepKey="addSecondTaxRuleActionGroup"/> + <actionGroup ref="AddNewTaxRuleActionGroup" stepKey="addSecondTaxRuleActionGroup"/> <fillField stepKey="fillSecondRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="TaxRule2"/> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addCATaxRate"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> <argument name="taxCode" value="SimpleSecondTaxWithZipCode"/> </actionGroup> @@ -72,11 +72,11 @@ <!--Open Created products. In Tax Class select new created Product Tax classes.--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductIndex"/> <waitForPageLoad stepKey="wait1"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetProductGrid"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGrid"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProductGridBySku"> <argument name="product" value="$$firstProduct$$"/> </actionGroup> - <actionGroup ref="openProducForEditByClickingRowXColumnYInProductGrid" stepKey="openFirstProductForEdit"/> + <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openFirstProductForEdit"/> <selectOption selector="{{AdminProductFormSection.productTaxClass}}" stepKey="selectTexClassForFirstProduct" userInput="TaxClasses1"/> <!-- Save the second product --> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveFirstProduct"/> @@ -84,11 +84,11 @@ <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="againGoToProductIndex"/> <waitForPageLoad stepKey="wait2"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetSecondProductGrid"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterSecondProductGridBySku"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetSecondProductGrid"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterSecondProductGridBySku"> <argument name="product" value="$$secondProduct$$"/> </actionGroup> - <actionGroup ref="openProducForEditByClickingRowXColumnYInProductGrid" stepKey="openSecondProductForEdit"/> + <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openSecondProductForEdit"/> <selectOption selector="{{AdminProductFormSection.productTaxClass}}" stepKey="selectTexClassForSecondProduct" userInput="TaxClasses2"/> <!-- Save the second product --> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveSecondProduct"/> @@ -104,10 +104,10 @@ <waitForPageLoad stepKey="waitForPage" time="60"/> <!--Check if order can be submitted without the required fields including email address--> <scrollToTopOfPage stepKey="scrollToTopOfOrderFormPage" after="seeNewOrderPageTitle"/> - <actionGroup ref="addSimpleProductToOrder" stepKey="addFirstProductToOrder" after="scrollToTopOfOrderFormPage"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addFirstProductToOrder" after="scrollToTopOfOrderFormPage"> <argument name="product" value="$$firstProduct$$"/> </actionGroup> - <actionGroup ref="addSimpleProductToOrder" stepKey="addSecondProductToOrder" after="addFirstProductToOrder"> + <actionGroup ref="AddSimpleProductToOrderActionGroup" stepKey="addSecondProductToOrder" after="addFirstProductToOrder"> <argument name="product" value="$$secondProduct$$"/> </actionGroup> @@ -116,15 +116,15 @@ <fillField selector="{{AdminOrderFormAccountSection.email}}" userInput="{{Simple_US_Customer.email}}" stepKey="fillCustomerEmail" after="selectCustomerGroup"/> <!--Fill customer address information--> - <actionGroup ref="fillOrderCustomerInformation" stepKey="fillCustomerAddress" after="fillCustomerEmail"> + <actionGroup ref="FillOrderCustomerInformationActionGroup" stepKey="fillCustomerAddress" after="fillCustomerEmail"> <argument name="customer" value="Simple_US_Customer"/> <argument name="address" value="US_Address_TX"/> </actionGroup> <!-- Select shipping --> - <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRateShipping" after="fillCustomerAddress"/> + <actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="selectFlatRateShipping" after="fillCustomerAddress"/> <!-- Checkout select Check/Money Order payment --> - <actionGroup ref="SelectCheckMoneyPaymentMethod" after="selectFlatRateShipping" stepKey="selectCheckMoneyPayment"/> + <actionGroup ref="SelectCheckMoneyPaymentMethodActionGroup" after="selectFlatRateShipping" stepKey="selectCheckMoneyPayment"/> <!--Submit Order and verify information--> <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" after="selectCheckMoneyPayment" stepKey="clickSubmitOrder"/> <seeInCurrentUrl url="{{AdminOrderDetailsPage.url}}" stepKey="seeViewOrderPage" after="clickSubmitOrder"/> @@ -211,11 +211,11 @@ <deleteData createDataKey="secondProduct" stepKey="deleteSecondProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="deleteProductTaxClass" stepKey="deleteFirstProductTaxClass"> + <actionGroup ref="DeleteProductTaxClassActionGroup" stepKey="deleteFirstProductTaxClass"> <argument name="taxClassName" value="TaxClasses1"/> </actionGroup> - <actionGroup ref="deleteProductTaxClass" stepKey="deleteSecondProductTaxClass"> + <actionGroup ref="DeleteProductTaxClassActionGroup" stepKey="deleteSecondProductTaxClass"> <argument name="taxClassName" value="TaxClasses2"/> </actionGroup> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/CheckCreditMemoTotalsTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/CheckCreditMemoTotalsTest.xml index 38cc687ff53a4..e7964a2dd29eb 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/CheckCreditMemoTotalsTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/CheckCreditMemoTotalsTest.xml @@ -95,14 +95,14 @@ <argument name="name" value="{{SimpleTaxNY.state}}"/> <argument name="searchInput" value="{{AdminSecondaryGridSection.taxIdentifierSearch}}"/> </actionGroup> - <actionGroup ref="deleteProductTaxClass" stepKey="deleteFirstProductTaxClass"> + <actionGroup ref="DeleteProductTaxClassActionGroup" stepKey="deleteFirstProductTaxClass"> <argument name="taxClassName" value="NewTaxClass"/> </actionGroup> <!--logout--> <actionGroup ref="logout" stepKey="logout"/> </after> <!--Create new order--> - <actionGroup stepKey="CreateNewOrder" ref="navigateToNewOrderPageExistingCustomer"> + <actionGroup stepKey="CreateNewOrder" ref="NavigateToNewOrderPageExistingCustomerActionGroup"> <argument name="customer" value="Simple_US_Customer_NY"/> </actionGroup> <!--Add product to order--> @@ -112,7 +112,7 @@ <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="clickAddSelectedProducts"/> <waitForPageLoad stepKey="waitForPageLoad3"/> <!--Set shipping method--> - <actionGroup stepKey="orderSelectFlatRateShipping" ref="orderSelectFlatRateShipping"/> + <actionGroup stepKey="OrderSelectFlatRateShippingActionGroup" ref="OrderSelectFlatRateShippingActionGroup"/> <!--Submit order--> <click stepKey="SubmitOrder" selector="{{AdminOrderFormActionSection.SubmitOrder}}"/> <waitForPageLoad stepKey="waitForPageLoad4"/> @@ -120,7 +120,7 @@ <!--Open new created order--> <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> <waitForPageLoad stepKey="waitForPageLoad5"/> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <actionGroup ref="FilterOrderGridByIdActionGroup" stepKey="filterOrderGridById"> <argument name="orderId" value="$getOrderId"/> </actionGroup> <!--Create order invoice--> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml index aa44593400a89..d81ea25de87a4 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml @@ -57,10 +57,10 @@ <argument name="stateForFPT" value="New York"/> <argument name="valueForFPT" value="20"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetGridToDefaultKeywordSearch"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> </before> <after> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> @@ -86,7 +86,7 @@ <argument name="productCount" value="1"/> </actionGroup> <!-- Step 3: Go to Shopping Cart --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <!-- Step 4: Open Estimate Shipping and Tax section --> <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> <seeOptionIsSelected selector="{{CheckoutCartSummarySection.country}}" userInput="{{US_Address_CA.country}}" stepKey="checkCustomerCountry" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml index ac090fd4fe9c0..c62fc59a52e68 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml @@ -58,7 +58,7 @@ <argument name="productCount" value="1"/> </actionGroup> <!-- Step 3: Go to Shopping Cart --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <!-- Step 4: Open Estimate Shipping and Tax section --> <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> <seeOptionIsSelected selector="{{CheckoutCartSummarySection.country}}" userInput="{{US_Address_NY.country}}" stepKey="checkCustomerCountry" /> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml index bce9d895e311e..e8c2f1a66f0c3 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml @@ -55,10 +55,10 @@ <argument name="stateForFPT" value="New York"/> <argument name="valueForFPT" value="20"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetGridToDefaultKeywordSearch"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> </before> <after> <deleteData createDataKey="createTaxRule" stepKey="deleteTaxRule"/> @@ -81,7 +81,7 @@ <argument name="productCount" value="1"/> </actionGroup> <!-- Step 3: Go to Shopping Cart --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <!-- Step 4: Open Estimate Shipping and Tax section --> <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUSCountry"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml index 74233bbff4a64..4b911cd6db32f 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml @@ -53,7 +53,7 @@ <argument name="productCount" value="1"/> </actionGroup> <!-- Step 3: Go to Shopping Cart --> - <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCartFromMinicart"/> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/> <!-- Step 4: Open Estimate Shipping and Tax section --> <conditionalClick selector="{{CheckoutCartSummarySection.estimateShippingAndTax}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="expandEstimateShippingandTax" /> <selectOption selector="{{CheckoutCartSummarySection.country}}" userInput="United States" stepKey="selectUSCountry"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml index 05ced7e61b3b7..f0fac38a6c05c 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml @@ -23,7 +23,7 @@ <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="editTaxConfigurationByUI" stepKey="fillDefaultTaxForms"/> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> <!-- Go to tax rule page --> <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> @@ -32,11 +32,11 @@ <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> <!-- Add NY and CA tax rules --> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addNYTaxRate"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> <argument name="taxCode" value="SimpleTaxNY"/> </actionGroup> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addCATaxRate"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> <argument name="taxCode" value="SimpleTaxCA"/> </actionGroup> @@ -76,7 +76,7 @@ </actionGroup> <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="changeToDefaultTaxConfigurationUI" stepKey="changeToDefaultTaxConfiguration"/> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> @@ -103,7 +103,7 @@ <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> <!-- Change the address --> - <actionGroup ref="changeSummaryQuoteAddress" stepKey="changeAddress"> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress"> <argument name="taxCode" value="SimpleTaxSwiss"/> </actionGroup> @@ -113,7 +113,7 @@ <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> <!-- Change the address --> - <actionGroup ref="changeSummaryQuoteAddress" stepKey="changeAddress2"> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress2"> <argument name="taxCode" value="SimpleTaxCA"/> </actionGroup> @@ -141,7 +141,7 @@ <createData entity="VirtualProduct" stepKey="virtualProduct1"/> <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="editTaxConfigurationByUI" stepKey="fillDefaultTaxForms"/> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> <!-- Go to tax rule page --> <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> @@ -150,11 +150,11 @@ <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> <!-- Add NY and CA tax rules --> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addNYTaxRate"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> <argument name="taxCode" value="SimpleTaxNY"/> </actionGroup> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addCATaxRate"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> <argument name="taxCode" value="SimpleTaxCA"/> </actionGroup> @@ -194,7 +194,7 @@ </actionGroup> <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="changeToDefaultTaxConfigurationUI" stepKey="changeToDefaultTaxConfiguration"/> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> <deleteData createDataKey="virtualProduct1" stepKey="deleteVirtualProduct1"/> @@ -220,7 +220,7 @@ <see stepKey="seeTotalExcl" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> <!-- Change the address --> - <actionGroup ref="changeSummaryQuoteAddress" stepKey="changeAddress"> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress"> <argument name="taxCode" value="SimpleTaxSwiss"/> </actionGroup> @@ -230,7 +230,7 @@ <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$$virtualProduct1.price$$"/> <!-- Change the address --> - <actionGroup ref="changeSummaryQuoteAddress" stepKey="changeAddress2"> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress2"> <argument name="taxCode" value="SimpleTaxCA"/> </actionGroup> @@ -257,7 +257,7 @@ <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="editTaxConfigurationByUI" stepKey="fillDefaultTaxForms"/> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> <!-- Go to tax rule page --> <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> @@ -266,11 +266,11 @@ <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> <!-- Add NY and CA tax rules --> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addNYTaxRate"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> <argument name="taxCode" value="SimpleTaxNY"/> </actionGroup> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addCATaxRate"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> <argument name="taxCode" value="SimpleTaxCA"/> </actionGroup> @@ -301,7 +301,7 @@ </actionGroup> <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="changeToDefaultTaxConfigurationUI" stepKey="changeToDefaultTaxConfiguration"/> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> @@ -330,7 +330,7 @@ <see stepKey="seeTotalExcl3" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> <!-- Change the address --> - <actionGroup ref="changeSummaryQuoteAddress" stepKey="changeAddress"> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress"> <argument name="taxCode" value="SimpleTaxSwiss"/> </actionGroup> @@ -340,7 +340,7 @@ <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$128.00"/> <!-- Change the address --> - <actionGroup ref="changeSummaryQuoteAddress" stepKey="changeAddress2"> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress2"> <argument name="taxCode" value="SimpleTaxNY"/> </actionGroup> @@ -367,7 +367,7 @@ <createData entity="VirtualProduct" stepKey="virtualProduct1"/> <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="editTaxConfigurationByUI" stepKey="fillDefaultTaxForms"/> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> <!-- Go to tax rule page --> <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> @@ -376,11 +376,11 @@ <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> <!-- Add NY and CA tax rules --> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addNYTaxRate"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> <argument name="taxCode" value="SimpleTaxNY"/> </actionGroup> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addCATaxRate"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> <argument name="taxCode" value="SimpleTaxCA"/> </actionGroup> @@ -411,7 +411,7 @@ </actionGroup> <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="changeToDefaultTaxConfigurationUI" stepKey="changeToDefaultTaxConfiguration"/> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> <deleteData createDataKey="virtualProduct1" stepKey="deleteVirtualProduct1"/> @@ -441,7 +441,7 @@ <see stepKey="seeTotalExcl3" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> <!-- Change the address --> - <actionGroup ref="changeSummaryQuoteAddress" stepKey="changeAddress"> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress"> <argument name="taxCode" value="SimpleTaxSwiss"/> </actionGroup> @@ -451,7 +451,7 @@ <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$$virtualProduct1.price$$"/> <!-- Change the address --> - <actionGroup ref="changeSummaryQuoteAddress" stepKey="changeAddress2"> + <actionGroup ref="ChangeSummaryQuoteAddressActionGroup" stepKey="changeAddress2"> <argument name="taxCode" value="SimpleTaxNY"/> </actionGroup> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml index e7bf08257ea69..e0e411a04d484 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml @@ -23,7 +23,7 @@ <createData entity="VirtualProduct" stepKey="virtualProduct1"/> <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="editTaxConfigurationByUI" stepKey="fillDefaultTaxForms"/> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> <!-- Go to tax rule page --> <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> @@ -32,11 +32,11 @@ <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> <!-- Add NY and CA tax rules --> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addNYTaxRate"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> <argument name="taxCode" value="SimpleTaxNY"/> </actionGroup> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addCATaxRate"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> <argument name="taxCode" value="SimpleTaxCA"/> </actionGroup> @@ -67,7 +67,7 @@ </actionGroup> <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="changeToDefaultTaxConfigurationUI" stepKey="changeToDefaultTaxConfiguration"/> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> <deleteData createDataKey="virtualProduct1" stepKey="deleteVirtualProduct1"/> @@ -127,7 +127,7 @@ <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="editTaxConfigurationByUI" stepKey="fillDefaultTaxForms"/> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> <!-- Go to tax rule page --> <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> @@ -136,11 +136,11 @@ <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> <!-- Add NY and CA tax rules --> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addNYTaxRate"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> <argument name="taxCode" value="SimpleTaxNY"/> </actionGroup> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addCATaxRate"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> <argument name="taxCode" value="SimpleTaxCA"/> </actionGroup> @@ -180,7 +180,7 @@ </actionGroup> <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="changeToDefaultTaxConfigurationUI" stepKey="changeToDefaultTaxConfiguration"/> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> @@ -247,7 +247,7 @@ <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="editTaxConfigurationByUI" stepKey="fillDefaultTaxForms"/> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> <!-- Go to tax rule page --> <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> @@ -256,11 +256,11 @@ <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> <!-- Add NY and CA tax rules --> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addNYTaxRate"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> <argument name="taxCode" value="SimpleTaxNY"/> </actionGroup> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addCATaxRate"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> <argument name="taxCode" value="SimpleTaxCA"/> </actionGroup> @@ -291,7 +291,7 @@ </actionGroup> <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="changeToDefaultTaxConfigurationUI" stepKey="changeToDefaultTaxConfiguration"/> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> @@ -362,7 +362,7 @@ <createData entity="VirtualProduct" stepKey="virtualProduct1"/> <!-- Fill in rules to display tax in the cart --> - <actionGroup ref="editTaxConfigurationByUI" stepKey="fillDefaultTaxForms"/> + <actionGroup ref="EditTaxConfigurationByUIActionGroup" stepKey="fillDefaultTaxForms"/> <!-- Go to tax rule page --> <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> @@ -371,11 +371,11 @@ <fillField stepKey="fillRuleName" selector="{{AdminTaxRulesSection.ruleName}}" userInput="SampleRule"/> <!-- Add NY and CA tax rules --> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addNYTaxRate"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addNYTaxRate"> <argument name="taxCode" value="SimpleTaxNY"/> </actionGroup> - <actionGroup ref="addNewTaxRateNoZip" stepKey="addCATaxRate"> + <actionGroup ref="AddNewTaxRateNoZipActionGroup" stepKey="addCATaxRate"> <argument name="taxCode" value="SimpleTaxCA"/> </actionGroup> @@ -415,7 +415,7 @@ </actionGroup> <!-- Ensure tax won't be shown in the cart --> - <actionGroup ref="changeToDefaultTaxConfigurationUI" stepKey="changeToDefaultTaxConfiguration"/> + <actionGroup ref="ChangeToDefaultTaxConfigurationUIActionGroup" stepKey="changeToDefaultTaxConfiguration"/> <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> <deleteData createDataKey="virtualProduct1" stepKey="deleteVirtualProduct1"/> diff --git a/app/code/Magento/Tax/Test/Unit/CustomerData/CheckoutTotalsJsLayoutDataProviderTest.php b/app/code/Magento/Tax/Test/Unit/CustomerData/CheckoutTotalsJsLayoutDataProviderTest.php new file mode 100644 index 0000000000000..d624a42c1e134 --- /dev/null +++ b/app/code/Magento/Tax/Test/Unit/CustomerData/CheckoutTotalsJsLayoutDataProviderTest.php @@ -0,0 +1,129 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Tax\Test\Unit\CustomerData; + +use PHPUnit\Framework\TestCase; +use Magento\Tax\CustomerData\CheckoutTotalsJsLayoutDataProvider; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Tax\Model\Config as TaxConfig; + +/** + * Test class to cover CheckoutTotalsJsLayoutDataProvider + * + * Class \Magento\Tax\Test\Unit\CustomerData\CheckoutTotalsJsLayoutDataProviderTest + */ +class CheckoutTotalsJsLayoutDataProviderTest extends TestCase +{ + /** + * @var CheckoutTotalsJsLayoutDataProvider + */ + private $dataProvider; + + /** + * @var TaxConfig|PHPUnit_Framework_MockObject_MockObject + */ + private $taxConfigMock; + + /** + * Setup environment for test + */ + protected function setUp() + { + $this->taxConfigMock = $this->createMock(TaxConfig::class); + $objectManager = new ObjectManagerHelper($this); + + $this->dataProvider = $objectManager->getObject( + CheckoutTotalsJsLayoutDataProvider::class, + [ + 'taxConfig' => $this->taxConfigMock + ] + ); + } + + /** + * Test getData() with dataset getDataDataProvider + * + * @param int $displayCartSubtotalInclTax + * @param int $displayCartSubtotalExclTax + * @param array $expected + * @return void + * @dataProvider getDataDataProvider + */ + public function testGetData($displayCartSubtotalInclTax, $displayCartSubtotalExclTax, $expected) + { + $this->taxConfigMock->expects($this->any())->method('displayCartSubtotalInclTax') + ->willReturn($displayCartSubtotalInclTax); + $this->taxConfigMock->expects($this->any())->method('displayCartSubtotalExclTax') + ->willReturn($displayCartSubtotalExclTax); + + $this->assertEquals($expected, $this->dataProvider->getData()); + } + + /** + * Dataset for test getData() + * + * @return array + */ + public function getDataDataProvider() + { + return [ + 'Test with settings display cart incl and excl is Yes' => [ + '1' , + '1', + [ + 'components' => [ + 'minicart_content' => [ + 'children' => [ + 'subtotal.container' => [ + 'children' => [ + 'subtotal' => [ + 'children' => [ + 'subtotal.totals' => [ + 'config' => [ + 'display_cart_subtotal_incl_tax' => 1, + 'display_cart_subtotal_excl_tax' => 1 + ] + ], + ], + ], + ], + ], + ], + ], + ] + ] + ], + 'Test with settings display cart incl and excl is No' => [ + '0' , + '0', + [ + 'components' => [ + 'minicart_content' => [ + 'children' => [ + 'subtotal.container' => [ + 'children' => [ + 'subtotal' => [ + 'children' => [ + 'subtotal.totals' => [ + 'config' => [ + 'display_cart_subtotal_incl_tax' => 0, + 'display_cart_subtotal_excl_tax' => 0 + ] + ], + ], + ], + ], + ], + ], + ], + ] + ] + ] + ]; + } +} diff --git a/app/code/Magento/Tax/etc/di.xml b/app/code/Magento/Tax/etc/di.xml index a0b43df226f22..50683b1b879e6 100644 --- a/app/code/Magento/Tax/etc/di.xml +++ b/app/code/Magento/Tax/etc/di.xml @@ -183,4 +183,13 @@ <type name="Magento\Catalog\Ui\DataProvider\Product\Listing\DataProvider"> <plugin name="taxSettingsProvider" type="Magento\Tax\Plugin\Ui\DataProvider\TaxSettings"/> </type> + <type name="Magento\Eav\Model\Config"> + <arguments> + <argument name="attributesForPreload" xsi:type="array"> + <item name="catalog_product" xsi:type="array"> + <item name="tax_class_id" xsi:type="string">catalog_product</item> + </item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Tax/registration.php b/app/code/Magento/Tax/registration.php index 3568ba311e9f2..2d36e0cc4c992 100644 --- a/app/code/Magento/Tax/registration.php +++ b/app/code/Magento/Tax/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Tax', __DIR__); diff --git a/app/code/Magento/Tax/view/frontend/web/template/checkout/cart/totals/shipping.html b/app/code/Magento/Tax/view/frontend/web/template/checkout/cart/totals/shipping.html index 9f8574a9438d1..269b447cfe66e 100644 --- a/app/code/Magento/Tax/view/frontend/web/template/checkout/cart/totals/shipping.html +++ b/app/code/Magento/Tax/view/frontend/web/template/checkout/cart/totals/shipping.html @@ -9,6 +9,9 @@ <tr class="totals shipping excl"> <th class="mark" scope="row"> <span class="label" data-bind="text: title + ' ' + excludingTaxMessage"></span> + <!-- ko if: haveToShowCoupon() --> + <span class="label description" data-bind="text: getCouponDescription()"></span> + <!-- /ko --> <span class="value" data-bind="text: getShippingMethodTitle()"></span> </th> <td class="amount"> @@ -19,6 +22,9 @@ <tr class="totals shipping incl"> <th class="mark" scope="row"> <span class="label" data-bind="text: title + ' ' + includingTaxMessage"></span> + <!-- ko if: haveToShowCoupon() --> + <span class="label description" data-bind="text: getCouponDescription()"></span> + <!-- /ko --> <span class="value" data-bind="text: getShippingMethodTitle()"></span> </th> <td class="amount"> @@ -31,6 +37,9 @@ <tr class="totals shipping incl"> <th class="mark" scope="row"> <span class="label" data-bind="i18n: title"></span> + <!-- ko if: haveToShowCoupon() --> + <span class="label description" data-bind="text: getCouponDescription()"></span> + <!-- /ko --> <span class="value" data-bind="text: getShippingMethodTitle()"></span> </th> <td class="amount"> @@ -43,6 +52,9 @@ <tr class="totals shipping excl"> <th class="mark" scope="row"> <span class="label" data-bind="i18n: title"></span> + <!-- ko if: haveToShowCoupon() --> + <span class="label description" data-bind="text: getCouponDescription()"></span> + <!-- /ko --> <span class="value" data-bind="text: getShippingMethodTitle()"></span> </th> <td class="amount"> diff --git a/app/code/Magento/Tax/view/frontend/web/template/checkout/summary/shipping.html b/app/code/Magento/Tax/view/frontend/web/template/checkout/summary/shipping.html index 007e7ded68210..82ab993bb63eb 100644 --- a/app/code/Magento/Tax/view/frontend/web/template/checkout/summary/shipping.html +++ b/app/code/Magento/Tax/view/frontend/web/template/checkout/summary/shipping.html @@ -9,6 +9,9 @@ <tr class="totals shipping excl"> <th class="mark" scope="row"> <span class="label" data-bind="text: title+ ' ' + excludingTaxMessage"></span> + <!-- ko if: haveToShowCoupon() --> + <span class="label description" data-bind="text: getCouponDescription()"></span> + <!-- /ko --> <span class="value" data-bind="text: getShippingMethodTitle()"></span> </th> <td class="amount"> @@ -25,6 +28,9 @@ <tr class="totals shipping incl"> <th class="mark" scope="row"> <span class="label" data-bind="text: title + ' ' + includingTaxMessage"></span> + <!-- ko if: haveToShowCoupon() --> + <span class="label description" data-bind="text: getCouponDescription()"></span> + <!-- /ko --> <span class="value" data-bind="text: getShippingMethodTitle()"></span> </th> <td class="amount"> @@ -43,6 +49,9 @@ <tr class="totals shipping incl"> <th class="mark" scope="row"> <span class="label" data-bind="i18n: title"></span> + <!-- ko if: haveToShowCoupon() --> + <span class="label description" data-bind="text: getCouponDescription()"></span> + <!-- /ko --> <span class="value" data-bind="text: getShippingMethodTitle()"></span> </th> <td class="amount"> @@ -61,6 +70,9 @@ <tr class="totals shipping excl"> <th class="mark" scope="row"> <span class="label" data-bind="i18n: title"></span> + <!-- ko if: haveToShowCoupon() --> + <span class="label description" data-bind="text: getCouponDescription()"></span> + <!-- /ko --> <span class="value" data-bind="text: getShippingMethodTitle()"></span> </th> <td class="amount"> diff --git a/app/code/Magento/TaxImportExport/registration.php b/app/code/Magento/TaxImportExport/registration.php index 63093dc25aa6d..67b67b55cbeee 100644 --- a/app/code/Magento/TaxImportExport/registration.php +++ b/app/code/Magento/TaxImportExport/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_TaxImportExport', __DIR__); diff --git a/app/code/Magento/Theme/Block/Html/Header/Logo.php b/app/code/Magento/Theme/Block/Html/Header/Logo.php index b51f624c20339..626a771b4e309 100644 --- a/app/code/Magento/Theme/Block/Html/Header/Logo.php +++ b/app/code/Magento/Theme/Block/Html/Header/Logo.php @@ -98,7 +98,7 @@ public function getLogoWidth() \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); } - return (int)$this->_data['logo_width'] ? : (int)$this->getLogoImgWidth(); + return (int)$this->_data['logo_width']; } /** @@ -114,7 +114,7 @@ public function getLogoHeight() \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); } - return (int)$this->_data['logo_height'] ? : (int)$this->getLogoImgHeight(); + return (int)$this->_data['logo_height']; } /** diff --git a/app/code/Magento/Theme/Block/Html/Pager.php b/app/code/Magento/Theme/Block/Html/Pager.php index ad3f4aad676eb..5798b94e31a70 100644 --- a/app/code/Magento/Theme/Block/Html/Pager.php +++ b/app/code/Magento/Theme/Block/Html/Pager.php @@ -236,7 +236,7 @@ public function setShowPerPage($varName) */ public function isShowPerPage() { - if (sizeof($this->getAvailableLimit()) <= 1) { + if (count($this->getAvailableLimit()) <= 1) { return false; } return $this->_showPerPage; @@ -450,7 +450,11 @@ public function getLastPageUrl() */ public function getPageUrl($page) { - return $this->getPagerUrl([$this->getPageVarName() => $page]); + return $this->getPagerUrl( + [ + $this->getPageVarName() => $page > 1 ? $page : null, + ] + ); } /** diff --git a/app/code/Magento/Theme/Block/Html/Topmenu.php b/app/code/Magento/Theme/Block/Html/Topmenu.php index 77b9144069502..fd8aaa7708cf3 100644 --- a/app/code/Magento/Theme/Block/Html/Topmenu.php +++ b/app/code/Magento/Theme/Block/Html/Topmenu.php @@ -133,7 +133,7 @@ protected function _countItems($items) * * @param Menu $items * @param int $limit - * @return array + * @return array|void * * @todo: Add Depth Level limit, and better logic for columns */ @@ -141,7 +141,7 @@ protected function _columnBrake($items, $limit) { $total = $this->_countItems($items); if ($total <= $limit) { - return []; + return; } $result[] = ['total' => $total, 'max' => (int)ceil($total / ceil($total / $limit))]; diff --git a/app/code/Magento/Theme/Controller/Result/JsFooterPlugin.php b/app/code/Magento/Theme/Controller/Result/JsFooterPlugin.php index 317ab39d307cd..6a80dec460660 100644 --- a/app/code/Magento/Theme/Controller/Result/JsFooterPlugin.php +++ b/app/code/Magento/Theme/Controller/Result/JsFooterPlugin.php @@ -41,7 +41,7 @@ public function beforeSendResponse(Http $subject) { $content = $subject->getContent(); $script = []; - if (strpos($content, '</body') !== false) { + if (is_string($content) && strpos($content, '</body') !== false) { if ($this->scopeConfig->isSetFlag( self::XML_PATH_DEV_MOVE_JS_TO_BOTTOM, ScopeInterface::SCOPE_STORE diff --git a/app/code/Magento/Theme/Model/Theme/ThemeProvider.php b/app/code/Magento/Theme/Model/Theme/ThemeProvider.php index cd3f7f8a2dc0e..04e4c131dbcd3 100644 --- a/app/code/Magento/Theme/Model/Theme/ThemeProvider.php +++ b/app/code/Magento/Theme/Model/Theme/ThemeProvider.php @@ -93,8 +93,8 @@ public function getThemeByFullPath($fullPath) if ($theme->getId()) { $this->saveThemeToCache($theme, 'theme' . $fullPath); $this->saveThemeToCache($theme, 'theme-by-id-' . $theme->getId()); - $this->themes[$fullPath] = $theme; } + $this->themes[$fullPath] = $theme; return $theme; } @@ -167,6 +167,8 @@ private function saveThemeToCache(\Magento\Theme\Model\Theme $theme, $cacheId) } /** + * Get theme list + * * @deprecated 100.1.3 * @return ListInterface */ @@ -179,6 +181,8 @@ private function getThemeList() } /** + * Get deployment config + * * @deprecated 100.1.3 * @return DeploymentConfig */ diff --git a/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml b/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml index 069068163ccaf..762537ba426f2 100644 --- a/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml +++ b/app/code/Magento/Theme/Test/Mftf/Section/AdminDesignConfigSection.xml @@ -31,6 +31,7 @@ <element name="storesArrow" type="button" selector="#ZmF2aWNvbi9zdG9yZXM- > .jstree-icon" /> <element name="checkIfStoresArrowExpand" type="button" selector="//li[@id='ZmF2aWNvbi9zdG9yZXM-' and contains(@class,'jstree-closed')]" /> <element name="storeLink" type="button" selector="#ZmF2aWNvbi9zdG9yZXMvMQ-- > a"/> + <element name="imageWatermarkType" type="text" selector="//div[contains(@class, 'fieldset-wrapper-title')]//span[contains(text(), '{{watermarkType}}')]" parameterized="true"/> <element name="appliedTheme" type="select" selector="select[name='theme_theme_id']"/> <element name="scopeEditLinkByName" type="button" selector="//tr//td[count(//div[@data-role='grid-wrapper']//tr//th[normalize-space(.)= '{{scope}}']/preceding-sibling::th)+1][contains(.,'{{scopeName}}')]/..//a[contains(@class, 'action-menu-item')]" timeout="30" parameterized="true"/> </section> diff --git a/app/code/Magento/Theme/Test/Mftf/Section/AdminThemeSection.xml b/app/code/Magento/Theme/Test/Mftf/Section/AdminThemeSection.xml index 219ca7420361c..1a0bb738bc9ca 100644 --- a/app/code/Magento/Theme/Test/Mftf/Section/AdminThemeSection.xml +++ b/app/code/Magento/Theme/Test/Mftf/Section/AdminThemeSection.xml @@ -15,7 +15,8 @@ <element name="rowsInThemeTitleColumn" type="text" selector="//tbody/tr/td[contains(@class, 'parent_theme')]/preceding-sibling::td"/> <element name="rowsInColumn" type="text" selector="//tbody/tr/td[contains(@class, '{{column}}')]" parameterized="true"/> <!--Specific cell e.g. {{Section.gridCell('Name')}}--> - <element name="gridCell" type="text" selector="//table[@id='theme_grid_table']//td[contains(text(), '{{gridCellText}}')]" parameterized="true"/> + <element name="gridCell" type="text" selector="//table//div[contains(text(), '{{gridCellText}}')]" parameterized="true"/> + <element name="gridCellUpdated" type="text" selector="//tbody//tr//div[contains(text(), '{{gridCellText}}')]" parameterized="true"/> <element name="columnHeader" type="text" selector="//thead/tr/th[contains(@class, 'data-grid-th')]/span[text() = '{{label}}']" parameterized="true" timeout="30"/> </section> -</sections> \ No newline at end of file +</sections> diff --git a/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml new file mode 100644 index 0000000000000..9facab57e9a09 --- /dev/null +++ b/app/code/Magento/Theme/Test/Mftf/Test/AdminContentThemeSortTest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminContentThemesSortTest"> + <annotations> + <features value="Theme"/> + <stories value="Menu Navigation"/> + <title value="Admin content themes sort themes test"/> + <description value="Admin should be able to sort Themes"/> + <severity value="CRITICAL"/> + <testCaseId value="https://github.com/magento/magento2/pull/25926"/> + <group value="menu"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToContentThemesPage"> + <argument name="menuUiId" value="{{AdminMenuContent.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuContentDesignThemes.dataUiId}}"/> + </actionGroup> + <actionGroup ref="AdminAssertPageTitleActionGroup" stepKey="seePageTitle"> + <argument name="title" value="{{AdminMenuContentDesignThemes.pageTitle}}"/> + </actionGroup> + <click selector="{{AdminThemeSection.columnHeader('Theme Title')}}" stepKey="clickSortByTitle"/> + <click selector="{{AdminThemeSection.columnHeader('Theme Title')}}" stepKey="clickSortByTitleSecondTime"/> + <seeNumberOfElements selector="{{AdminThemeSection.rowsInColumn('theme_path')}}" userInput="2" stepKey="see2RowsOnTheGrid"/> + <seeNumberOfElements selector="{{AdminThemeSection.gridCellUpdated('Magento Luma')}}" userInput="1" stepKey="seeLumaThemeInTitleColumn"/> + </test> +</tests> diff --git a/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml index f46328ac151b1..8291ddc12b3b1 100644 --- a/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml +++ b/app/code/Magento/Theme/Test/Mftf/Test/AdminDesignConfigMediaGalleryImageUploadTest.xml @@ -35,14 +35,14 @@ <!--Upload Image--> <comment userInput="Upload Image" stepKey="uploadImageComment"/> <click selector="{{AdminDesignConfigSection.selectFromGalleryByFieldsetName('Head')}}" stepKey="openMediaGallery"/> - <actionGroup ref="VerifyMediaGalleryStorageActions" stepKey="verifyMediaGalleryStorageBtn"/> + <actionGroup ref="VerifyMediaGalleryStorageActionsActionGroup" stepKey="verifyMediaGalleryStorageBtn"/> <actionGroup ref="NavigateToMediaFolderActionGroup" stepKey="navigateToFolder"> <argument name="FolderName" value="Storage Root"/> </actionGroup> - <actionGroup ref="attachImage" stepKey="selectImageFromMediaStorage"> + <actionGroup ref="AttachImageActionGroup" stepKey="selectImageFromMediaStorage"> <argument name="Image" value="ImageUpload3"/> </actionGroup> - <actionGroup ref="saveImage" stepKey="insertImage"/> + <actionGroup ref="SaveImageActionGroup" stepKey="insertImage"/> <click selector="{{AdminDesignConfigSection.saveConfiguration}}" stepKey="saveConfiguration"/> <waitForElementVisible selector="{{AdminDesignConfigSection.successNotification}}" stepKey="waitForSuccessNotification"/> <waitForPageLoad stepKey="waitForPageloadSuccess"/> @@ -61,7 +61,7 @@ <waitForPageLoad stepKey="waitForPageloadSuccess2"/> <!--Delete Image: will be in both root and favicon--> <comment userInput="Delete Image" stepKey="deleteImageComment"/> - <actionGroup ref="navigateToMediaGallery" stepKey="navigateToMediaGallery"/> + <actionGroup ref="NavigateToMediaGalleryActionGroup" stepKey="navigateToMediaGallery"/> <actionGroup ref="NavigateToMediaFolderActionGroup" stepKey="navigateToFolder2"> <argument name="FolderName" value="Storage Root"/> </actionGroup> diff --git a/app/code/Magento/Theme/Test/Unit/Block/Html/Header/LogoTest.php b/app/code/Magento/Theme/Test/Unit/Block/Html/Header/LogoTest.php index 91af1a9bf6078..077f12e578dca 100644 --- a/app/code/Magento/Theme/Test/Unit/Block/Html/Header/LogoTest.php +++ b/app/code/Magento/Theme/Test/Unit/Block/Html/Header/LogoTest.php @@ -44,4 +44,38 @@ public function testGetLogoSrc() $this->assertEquals('http://localhost/pub/media/logo/default/image.gif', $block->getLogoSrc()); } + + /** + * cover \Magento\Theme\Block\Html\Header\Logo::getLogoHeight + */ + public function testGetLogoHeight() + { + $scopeConfig = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); + $scopeConfig->expects($this->once())->method('getValue')->willReturn(null); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $arguments = [ + 'scopeConfig' => $scopeConfig, + ]; + $block = $objectManager->getObject(\Magento\Theme\Block\Html\Header\Logo::class, $arguments); + + $this->assertEquals(null, $block->getLogoHeight()); + } + + /** + * @covers \Magento\Theme\Block\Html\Header\Logo::getLogoWidth + */ + public function testGetLogoWidth() + { + $scopeConfig = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class); + $scopeConfig->expects($this->once())->method('getValue')->willReturn('170'); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $arguments = [ + 'scopeConfig' => $scopeConfig, + ]; + $block = $objectManager->getObject(\Magento\Theme\Block\Html\Header\Logo::class, $arguments); + + $this->assertEquals('170', $block->getLogoHeight()); + } } diff --git a/app/code/Magento/Theme/Test/Unit/Block/Html/PagerTest.php b/app/code/Magento/Theme/Test/Unit/Block/Html/PagerTest.php new file mode 100644 index 0000000000000..2fa1c637f1838 --- /dev/null +++ b/app/code/Magento/Theme/Test/Unit/Block/Html/PagerTest.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Theme\Test\Unit\Block\Html; + +use Magento\Framework\App\Config; +use Magento\Framework\Data\Collection; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\UrlInterface; +use Magento\Framework\View\Element\Template\Context; +use Magento\Theme\Block\Html\Pager; +use PHPUnit\Framework\TestCase; + +/** + * Test For Page class + */ +class PagerTest extends TestCase +{ + + /** + * @var Pager $pager + */ + private $pager; + + /** + * @var Context $context + */ + private $context; + + /** + * @var Config $scopeConfig + */ + private $scopeConfig; + + /** + * @var UrlInterface $urlBuilderMock + */ + private $urlBuilderMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->context = $this->createMock(Context::class); + $this->urlBuilderMock = $this->createMock(UrlInterface::class); + $this->context->expects($this->any()) + ->method('getUrlBuilder') + ->willReturn($this->urlBuilderMock); + $this->scopeConfig = $this->createMock(Config::class); + $this->pager = (new ObjectManager($this))->getObject( + Pager::class, + ['context' => $this->context] + ); + } + + /** + * Verify current page Url + * + * @return void + */ + public function testGetPageUrl(): void + { + $expectedPageUrl = 'page-url'; + $this->urlBuilderMock->expects($this->once()) + ->method('getUrl') + ->willReturn($expectedPageUrl); + $this->assertEquals($expectedPageUrl, $this->pager->getPageUrl(0)); + } + + /** + * Verify get pages method. + * + * @return void + */ + public function testGetPages(): void + { + $expectedPages = range(1, 5); + $collectionMock = $this->createMock(Collection::class); + $collectionMock->expects($this->exactly(2)) + ->method('getCurPage') + ->willReturn(2); + $collectionMock->expects($this->any()) + ->method('getLastPageNumber') + ->willReturn(10); + $this->setCollectionProperty($collectionMock); + $this->assertEquals($expectedPages, $this->pager->getPages()); + } + + /** + * Set Collection + * + * @return void + */ + private function setCollectionProperty($collection): void + { + $reflection = new \ReflectionClass($this->pager); + $reflection_property = $reflection->getProperty('_collection'); + $reflection_property->setAccessible(true); + $reflection_property->setValue($this->pager, $collection); + } +} diff --git a/app/code/Magento/Theme/registration.php b/app/code/Magento/Theme/registration.php index 8f2b360da9d33..c00f6e4ba2136 100644 --- a/app/code/Magento/Theme/registration.php +++ b/app/code/Magento/Theme/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Theme', __DIR__); diff --git a/app/code/Magento/Theme/view/adminhtml/ui_component/design_theme_listing.xml b/app/code/Magento/Theme/view/adminhtml/ui_component/design_theme_listing.xml index 14aea72d87357..d2e5fa7ae1ca9 100644 --- a/app/code/Magento/Theme/view/adminhtml/ui_component/design_theme_listing.xml +++ b/app/code/Magento/Theme/view/adminhtml/ui_component/design_theme_listing.xml @@ -20,6 +20,9 @@ <dataSource name="design_theme_listing_data_source" component="Magento_Ui/js/grid/provider"> <settings> <updateUrl path="mui/index/render"/> + <storageConfig> + <param name="indexField" xsi:type="string">theme_id</param> + </storageConfig> </settings> <aclResource>Magento_Theme::theme</aclResource> <dataProvider class="Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider" name="design_theme_listing_data_source"> diff --git a/app/code/Magento/Theme/view/frontend/layout/default.xml b/app/code/Magento/Theme/view/frontend/layout/default.xml index 81cffe8c040b3..8eaac4aa3e794 100644 --- a/app/code/Magento/Theme/view/frontend/layout/default.xml +++ b/app/code/Magento/Theme/view/frontend/layout/default.xml @@ -50,12 +50,7 @@ </container> </container> <container name="header-wrapper" label="Page Header" as="header-wrapper" htmlTag="div" htmlClass="header content"> - <block class="Magento\Theme\Block\Html\Header\Logo" name="logo"> - <arguments> - <argument name="logo_img_width" xsi:type="number">189</argument> - <argument name="logo_img_height" xsi:type="number">64</argument> - </arguments> - </block> + <block class="Magento\Theme\Block\Html\Header\Logo" name="logo"/> </container> </referenceContainer> <referenceContainer name="page.top"> diff --git a/app/code/Magento/Theme/view/frontend/templates/html/pager.phtml b/app/code/Magento/Theme/view/frontend/templates/html/pager.phtml index bd50fa39d4099..6b28dbd4521a0 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/pager.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/pager.phtml @@ -10,31 +10,33 @@ * @see \Magento\Theme\Block\Html\Pager */ ?> -<?php if ($block->getCollection()->getSize()) : ?> +<?php if ($block->getCollection()->getSize()): ?> - <?php if ($block->getUseContainer()) : ?> + <?php if ($block->getUseContainer()): ?> <div class="pager"> <?php endif ?> - <?php if ($block->getShowAmounts()) : ?> + <?php if ($block->getShowAmounts()): ?> <p class="toolbar-amount"> <span class="toolbar-number"> - <?php if ($block->getLastPageNum()>1) : ?> - <?= $block->escapeHtml(__('Items %1 to %2 of %3 total', $block->getFirstNum(), $block->getLastNum(), $block->getTotalNum())) ?> - <?php elseif ($block->getTotalNum() == 1) : ?> + <?php if ($block->getLastPageNum()>1): ?> + <?= $block->escapeHtml( + __('Items %1 to %2 of %3 total', $block->getFirstNum(), $block->getLastNum(), $block->getTotalNum()) + ) ?> + <?php elseif ($block->getTotalNum() == 1): ?> <?= $block->escapeHtml(__('%1 Item', $block->getTotalNum())) ?> - <?php else : ?> + <?php else: ?> <?= $block->escapeHtml(__('%1 Item(s)', $block->getTotalNum())) ?> <?php endif; ?> </span> </p> <?php endif ?> - <?php if ($block->getLastPageNum()>1) : ?> + <?php if ($block->getLastPageNum()>1): ?> <div class="pages"> <strong class="label pages-label" id="paging-label"><?= $block->escapeHtml(__('Page')) ?></strong> <ul class="items pages-items" aria-labelledby="paging-label"> - <?php if (!$block->isFirstPage()) : ?> + <?php if (!$block->isFirstPage()): ?> <li class="item pages-item-previous"> <?php $text = $block->getAnchorTextForPrevious() ? $block->getAnchorTextForPrevious() : '';?> <a class="<?= $block->escapeHtmlAttr($text ? 'link ' : 'action ') ?> previous" @@ -46,7 +48,7 @@ </li> <?php endif;?> - <?php if ($block->canShowFirst()) : ?> + <?php if ($block->canShowFirst()): ?> <li class="item"> <a class="page first" href="<?= $block->escapeUrl($block->getFirstPageUrl()) ?>"> <span class="label"><?= $block->escapeHtml(__('Page')) ?></span> @@ -55,7 +57,7 @@ </li> <?php endif;?> - <?php if ($block->canShowPreviousJump()) : ?> + <?php if ($block->canShowPreviousJump()): ?> <li class="item"> <a class="page previous jump" title="" @@ -65,15 +67,15 @@ </li> <?php endif;?> - <?php foreach ($block->getFramePages() as $_page) : ?> - <?php if ($block->isPageCurrent($_page)) : ?> + <?php foreach ($block->getFramePages() as $_page): ?> + <?php if ($block->isPageCurrent($_page)): ?> <li class="item current"> <strong class="page"> <span class="label"><?= $block->escapeHtml(__('You\'re currently reading page')) ?></span> <span><?= $block->escapeHtml($_page) ?></span> </strong> </li> - <?php else : ?> + <?php else: ?> <li class="item"> <a href="<?= $block->escapeUrl($block->getPageUrl($_page)) ?>" class="page"> <span class="label"><?= $block->escapeHtml(__('Page')) ?></span> @@ -83,7 +85,7 @@ <?php endif;?> <?php endforeach;?> - <?php if ($block->canShowNextJump()) : ?> + <?php if ($block->canShowNextJump()): ?> <li class="item"> <a class="page next jump" title="" href="<?= $block->escapeUrl($block->getNextJumpUrl()) ?>"> <span>...</span> @@ -91,7 +93,7 @@ </li> <?php endif;?> - <?php if ($block->canShowLast()) : ?> + <?php if ($block->canShowLast()): ?> <li class="item"> <a class="page last" href="<?= $block->escapeUrl($block->getLastPageUrl()) ?>"> <span class="label"><?= $block->escapeHtml(__('Page')) ?></span> @@ -100,7 +102,7 @@ </li> <?php endif;?> - <?php if (!$block->isLastPage()) : ?> + <?php if (!$block->isLastPage()): ?> <li class="item pages-item-next"> <?php $text = $block->getAnchorTextForNext() ? $block->getAnchorTextForNext() : '';?> <a class="<?= /* @noEscape */ $text ? 'link ' : 'action ' ?> next" @@ -115,13 +117,13 @@ </div> <?php endif; ?> - <?php if ($block->isShowPerPage()) : ?> + <?php if ($block->isShowPerPage()): ?> <div class="limiter"> <strong class="limiter-label"><?= $block->escapeHtml(__('Show')) ?></strong> <select id="limiter" data-mage-init='{"redirectUrl": {"event":"change"}}' class="limiter-options"> - <?php foreach ($block->getAvailableLimit() as $_key => $_limit) : ?> - <option value="<?= $block->escapeHtmlAttr($block->getLimitUrl($_key)) ?>" - <?php if ($block->isLimitCurrent($_key)) : ?> + <?php foreach ($block->getAvailableLimit() as $_key => $_limit): ?> + <option value="<?= $block->escapeUrl($block->getLimitUrl($_key)) ?>" + <?php if ($block->isLimitCurrent($_key)): ?> selected="selected"<?php endif ?>> <?= $block->escapeHtml($_limit) ?> </option> @@ -131,7 +133,7 @@ </div> <?php endif ?> - <?php if ($block->getUseContainer()) : ?> + <?php if ($block->getUseContainer()): ?> </div> <?php endif ?> diff --git a/app/code/Magento/Tinymce3/Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml b/app/code/Magento/Tinymce3/Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml index e15886e026c4c..deb94a7f30d34 100644 --- a/app/code/Magento/Tinymce3/Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml +++ b/app/code/Magento/Tinymce3/Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml @@ -20,7 +20,7 @@ </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="EnabledWYSIWYG" stepKey="enableWYSIWYG"/> + <actionGroup ref="EnabledWYSIWYGActionGroup" stepKey="enableWYSIWYG"/> </before> <amOnPage url="{{ConfigurationStoresPage.url}}" stepKey="navigateToWYSIWYGConfigPage1"/> <waitForPageLoad stepKey="wait1"/> @@ -80,7 +80,7 @@ <see userInput="Hello TinyMCE3!" stepKey="seeContent2"/> <actionGroup ref="SwitchToVersion4ActionGroup" stepKey="switchToTinyMCE4" /> <after> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Tinymce3/registration.php b/app/code/Magento/Tinymce3/registration.php index fd57434a6280f..1e561dd1ae36a 100644 --- a/app/code/Magento/Tinymce3/registration.php +++ b/app/code/Magento/Tinymce3/registration.php @@ -3,6 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Tinymce3', __DIR__); diff --git a/app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js b/app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js index bb3300baf988a..15bc5465e5d04 100644 --- a/app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js +++ b/app/code/Magento/Tinymce3/view/base/web/tinymce3Adapter.js @@ -78,6 +78,8 @@ define([ } tinyMCE3.init(this.getSettings(mode)); + varienGlobalEvents.clearEventHandlers('open_browser_callback'); + varienGlobalEvents.attachEventHandler('open_browser_callback', tinyMceEditors.get(this.id).openFileBrowser); }, /** diff --git a/app/code/Magento/Translation/Console/Command/UninstallLanguageCommand.php b/app/code/Magento/Translation/Console/Command/UninstallLanguageCommand.php index a1092b231479e..4f7a1133ab208 100644 --- a/app/code/Magento/Translation/Console/Command/UninstallLanguageCommand.php +++ b/app/code/Magento/Translation/Console/Command/UninstallLanguageCommand.php @@ -85,31 +85,33 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ protected function configure() { $this->setName('i18n:uninstall') ->setDescription('Uninstalls language packages') - ->setDefinition([ - new InputArgument( - self::PACKAGE_ARGUMENT, - InputArgument::IS_ARRAY | InputArgument::REQUIRED, - 'Language package name' - ), - new InputOption( - self::BACKUP_CODE_OPTION, - '-b', - InputOption::VALUE_NONE, - 'Take code and configuration files backup (excluding temporary files)' - ), - ]); + ->setDefinition( + [ + new InputArgument( + self::PACKAGE_ARGUMENT, + InputArgument::IS_ARRAY | InputArgument::REQUIRED, + 'Language package name' + ), + new InputOption( + self::BACKUP_CODE_OPTION, + '-b', + InputOption::VALUE_NONE, + 'Take code and configuration files backup (excluding temporary files)' + ) + ] + ); parent::configure(); } /** - * {@inheritdoc} + * @inheritdoc */ protected function execute(InputInterface $input, OutputInterface $output) { @@ -121,7 +123,7 @@ protected function execute(InputInterface $input, OutputInterface $output) if (!$this->validate($package)) { $output->writeln("<info>Package $package is not a Magento language and will be skipped.</info>"); } else { - if (sizeof($dependencies[$package]) > 0) { + if (count($dependencies[$package]) > 0) { $output->writeln("<info>Package $package has dependencies and will be skipped.</info>"); } else { $packagesToRemove[] = $package; diff --git a/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml b/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml index 2b86a8ac07e77..08448f7735f7c 100644 --- a/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml +++ b/app/code/Magento/Translation/Test/Mftf/Test/AdminInlineTranslationOnCheckoutTest.xml @@ -56,7 +56,7 @@ <argument name="Customer" value="$$createCustomer$$"/> </actionGroup> - <actionGroup ref="AddSimpleProductToCart" stepKey="addProduct"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProduct"> <argument name="product" value="$$createProduct$$"/> </actionGroup> @@ -333,7 +333,7 @@ <argument name="Customer" value="$$createCustomer2$$"/> </actionGroup> - <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart"> <argument name="product" value="$$createProduct$$"/> </actionGroup> <click selector="{{StorefrontMiniCartSection.show}}" stepKey="showCart"/> @@ -427,7 +427,7 @@ <argument name="Customer" value="$$createCustomer3$$"/> </actionGroup> - <actionGroup ref="AddSimpleProductToCart" stepKey="addOneProductToCart"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addOneProductToCart"> <argument name="product" value="$$createProduct$$"/> </actionGroup> diff --git a/app/code/Magento/Translation/Test/Mftf/Test/StorefrontButtonsInlineTranslationTest.xml b/app/code/Magento/Translation/Test/Mftf/Test/StorefrontButtonsInlineTranslationTest.xml index 155e174310ea9..d87d3635fa07c 100644 --- a/app/code/Magento/Translation/Test/Mftf/Test/StorefrontButtonsInlineTranslationTest.xml +++ b/app/code/Magento/Translation/Test/Mftf/Test/StorefrontButtonsInlineTranslationTest.xml @@ -45,7 +45,7 @@ </after> <!-- Add product to cart on storefront --> - <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart"> + <actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart"> <argument name="product" value="$$createProduct$$"/> </actionGroup> diff --git a/app/code/Magento/Translation/registration.php b/app/code/Magento/Translation/registration.php index 2e3adaf378181..7c6266026c5b0 100644 --- a/app/code/Magento/Translation/registration.php +++ b/app/code/Magento/Translation/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Translation', __DIR__); diff --git a/app/code/Magento/Ui/Component/Filters.php b/app/code/Magento/Ui/Component/Filters.php index fe02c23af9c8a..5bf89ae7936e9 100644 --- a/app/code/Magento/Ui/Component/Filters.php +++ b/app/code/Magento/Ui/Component/Filters.php @@ -12,6 +12,8 @@ use Magento\Ui\Component\Listing\Columns\ColumnInterface; /** + * Grid filters UI component + * * @api * @since 100.0.2 */ @@ -36,6 +38,7 @@ class Filters extends AbstractComponent implements ObserverInterface 'textRange' => 'filterRange', 'select' => 'filterSelect', 'dateRange' => 'filterDate', + 'datetimeRange' => 'filterDate', ]; /** diff --git a/app/code/Magento/Ui/Component/Filters/Type/Date.php b/app/code/Magento/Ui/Component/Filters/Type/Date.php index e854b888c45e6..28ad8568ebe31 100644 --- a/app/code/Magento/Ui/Component/Filters/Type/Date.php +++ b/app/code/Magento/Ui/Component/Filters/Type/Date.php @@ -9,6 +9,8 @@ use Magento\Ui\Component\Form\Element\DataType\Date as DataTypeDate; /** + * Date grid filter UI Component + * * @api * @since 100.0.2 */ @@ -84,30 +86,18 @@ protected function applyFilter() if (isset($value['from'])) { $this->applyFilterByType( 'gteq', - $this->wrappedComponent->convertDate( - $value['from'], - 0, - 0, - 0, - !$this->getData('config/skipTimeZoneConversion') - ) + $this->convertDatetime((string)$value['from']) ); } if (isset($value['to'])) { $this->applyFilterByType( 'lteq', - $this->wrappedComponent->convertDate( - $value['to'], - 23, - 59, - 59, - !$this->getData('config/skipTimeZoneConversion') - ) + $this->convertDatetime((string)$value['to'], 23, 59, 59) ); } } else { - $this->applyFilterByType('eq', $this->wrappedComponent->convertDate($value)); + $this->applyFilterByType('eq', $this->convertDatetime((string)$value)); } } } @@ -130,4 +120,31 @@ protected function applyFilterByType($type, $value) $this->getContext()->getDataProvider()->addFilter($filter); } } + + /** + * Convert given date to default (UTC) timezone + * + * @param string $value + * @param int $hour + * @param int $minute + * @param int $second + * @return \DateTime + */ + private function convertDatetime(string $value, int $hour = 0, int $minute = 0, int $second = 0): ?\DateTime + { + $value = $this->getData('config/options/showsTime') + ? $this->wrappedComponent->convertDatetime( + $value, + !$this->getData('config/skipTimeZoneConversion') + ) + : $this->wrappedComponent->convertDate( + $value, + $hour, + $minute, + $second, + !$this->getData('config/skipTimeZoneConversion') + ); + + return $value; + } } diff --git a/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php b/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php index 31d2fe786cfd8..ef2df77e7daff 100644 --- a/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php +++ b/app/code/Magento/Ui/Component/Form/Element/DataType/Date.php @@ -11,7 +11,7 @@ use Magento\Framework\View\Element\UiComponent\ContextInterface; /** - * Class Date + * UI component date type */ class Date extends AbstractDataType { @@ -111,7 +111,7 @@ public function getComponentName() public function convertDate($date, $hour = 0, $minute = 0, $second = 0, $setUtcTimeZone = true) { try { - $dateObj = $this->localeDate->date($date, $this->getLocale(), true); + $dateObj = $this->localeDate->date($date, $this->getLocale(), false); $dateObj->setTime($hour, $minute, $second); //convert store date to default date in UTC timezone without DST if ($setUtcTimeZone) { @@ -122,4 +122,26 @@ public function convertDate($date, $hour = 0, $minute = 0, $second = 0, $setUtcT return null; } } + + /** + * Convert given date to default (UTC) timezone + * + * @param string $date + * @param bool $setUtcTimezone + * @return \DateTime|null + */ + public function convertDatetime(string $date, bool $setUtcTimezone = true): ?\DateTime + { + try { + $date = rtrim($date, 'Z'); + $dateObj = new \DateTime($date, new \DateTimeZone($this->localeDate->getConfigTimezone())); + //convert store date to default date in UTC timezone without DST + if ($setUtcTimezone) { + $dateObj->setTimezone(new \DateTimeZone('UTC')); + } + return $dateObj; + } catch (\Throwable $e) { + return null; + } + } } diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridDeleteCustomPerPageActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridDeleteCustomPerPageActionGroup.xml new file mode 100644 index 0000000000000..275aaac39a757 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridDeleteCustomPerPageActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminDataGridDeleteCustomPerPageActionGroup"> + <annotations> + <description>Sets the provided custom 'per page' display setting on an Admin Grid page. Deletes the Items listed in a grid. Validates that the 'per page' count in NOT present.</description> + </annotations> + <arguments> + <argument name="perPage"/> + </arguments> + + <click selector="{{AdminDataGridPaginationSection.perPageDropdown}}" stepKey="clickPerPageDropdown1"/> + <click selector="{{AdminDataGridPaginationSection.perPageEditCustomValue(perPage)}}" stepKey="clickToEditCustomPerPage"/> + <waitForElementVisible selector="{{AdminDataGridPaginationSection.perPageDeleteCustomValue(perPage)}}" time="30" stepKey="waitForDeleteButtonVisible"/> + <click selector="{{AdminDataGridPaginationSection.perPageDeleteCustomValue(perPage)}}" stepKey="clickToDeleteCustomPerPage"/> + <waitForLoadingMaskToDisappear stepKey="waitForGridLoad"/> + <click selector="{{AdminDataGridPaginationSection.perPageDropdown}}" stepKey="clickPerPageDropdown"/> + <dontSeeElement selector="{{AdminDataGridPaginationSection.perPageDropDownItem(perPage)}}" stepKey="dontSeeDropDownItem"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml deleted file mode 100644 index 4c7b386a4cd00..0000000000000 --- a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridFilterActionGroup.xml +++ /dev/null @@ -1,44 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Search grid with keyword search--> - <actionGroup name="searchAdminDataGridByKeyword"> - <annotations> - <description>Fills 'Search by keyword' on an Admin Grid page. Clicks on Submit Search.</description> - </annotations> - <arguments> - <argument name="keyword"/> - </arguments> - - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> - <fillField selector="{{AdminDataGridHeaderSection.search}}" userInput="{{keyword}}" stepKey="fillKeywordSearchField"/> - <click selector="{{AdminDataGridHeaderSection.submitSearch}}" stepKey="clickKeywordSearch"/> - </actionGroup> - - <!--Reset data grid to default view--> - <actionGroup name="resetAdminDataGridToDefaultView"> - <annotations> - <description>Resets an Admin Grid page to the 'Default View'.</description> - </annotations> - - <click selector="{{AdminDataGridHeaderSection.bookmarkToggle}}" stepKey="openViewBookmarks"/> - <click selector="{{AdminDataGridHeaderSection.bookmarkOption('Default View')}}" stepKey="selectDefaultGridView"/> - <see selector="{{AdminDataGridHeaderSection.bookmarkToggle}}" userInput="Default View" stepKey="seeDefaultViewSelected"/> - </actionGroup> - - <!--Clear all filters in grid--> - <actionGroup name="clearFiltersAdminDataGrid"> - <annotations> - <description>Clicks on 'Clear Filters' on an Admin Grid page.</description> - </annotations> - - <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilters"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridPaginationActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridPaginationActionGroup.xml deleted file mode 100644 index 11c5ef039f3e4..0000000000000 --- a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridPaginationActionGroup.xml +++ /dev/null @@ -1,57 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="adminDataGridSelectPerPage"> - <annotations> - <description>Sets the provided preset 'per page' display setting on an Admin Grid page.</description> - </annotations> - <arguments> - <argument name="perPage" type="string"/> - </arguments> - - <click selector="{{AdminDataGridPaginationSection.perPageDropdown}}" stepKey="clickPerPageDropdown"/> - <click selector="{{AdminDataGridPaginationSection.perPageOption(perPage)}}" stepKey="selectCustomPerPage"/> - <waitForPageLoad stepKey="waitForGridLoad"/> - </actionGroup> - - <actionGroup name="adminDataGridSelectCustomPerPage"> - <annotations> - <description>Sets the provided custom 'per page' display setting on an Admin Grid page.</description> - </annotations> - <arguments> - <argument name="perPage"/> - </arguments> - - <click selector="{{AdminDataGridPaginationSection.perPageDropdown}}" stepKey="clickPerPageDropdown"/> - <click selector="{{AdminDataGridPaginationSection.perPageOption('Custom')}}" stepKey="selectCustomPerPage"/> - <waitForElementVisible selector="{{AdminDataGridPaginationSection.perPageInput}}" time="30" stepKey="waitForInputVisible"/> - <fillField selector="{{AdminDataGridPaginationSection.perPageInput}}" userInput="{{perPage}}" stepKey="fillCustomPerPage"/> - <click selector="{{AdminDataGridPaginationSection.perPageApplyInput}}" stepKey="applyCustomPerPage"/> - <waitForLoadingMaskToDisappear stepKey="waitForGridLoad"/> - <seeInField selector="{{AdminDataGridPaginationSection.perPageDropDownValue}}" userInput="{{perPage}}" stepKey="seePerPageValueInDropDown"/> - </actionGroup> - - <actionGroup name="adminDataGridDeleteCustomPerPage"> - <annotations> - <description>Sets the provided custom 'per page' display setting on an Admin Grid page. Deletes the Items listed in a grid. Validates that the 'per page' count in NOT present.</description> - </annotations> - <arguments> - <argument name="perPage"/> - </arguments> - - <click selector="{{AdminDataGridPaginationSection.perPageDropdown}}" stepKey="clickPerPageDropdown1"/> - <click selector="{{AdminDataGridPaginationSection.perPageEditCustomValue(perPage)}}" stepKey="clickToEditCustomPerPage"/> - <waitForElementVisible selector="{{AdminDataGridPaginationSection.perPageDeleteCustomValue(perPage)}}" time="30" stepKey="waitForDeleteButtonVisible"/> - <click selector="{{AdminDataGridPaginationSection.perPageDeleteCustomValue(perPage)}}" stepKey="clickToDeleteCustomPerPage"/> - <waitForLoadingMaskToDisappear stepKey="waitForGridLoad"/> - <click selector="{{AdminDataGridPaginationSection.perPageDropdown}}" stepKey="clickPerPageDropdown"/> - <dontSeeElement selector="{{AdminDataGridPaginationSection.perPageDropDownItem(perPage)}}" stepKey="dontSeeDropDownItem"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridSelectCustomPerPageActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridSelectCustomPerPageActionGroup.xml new file mode 100644 index 0000000000000..0d9c80e086352 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridSelectCustomPerPageActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminDataGridSelectCustomPerPageActionGroup"> + <annotations> + <description>Sets the provided custom 'per page' display setting on an Admin Grid page.</description> + </annotations> + <arguments> + <argument name="perPage"/> + </arguments> + + <click selector="{{AdminDataGridPaginationSection.perPageDropdown}}" stepKey="clickPerPageDropdown"/> + <click selector="{{AdminDataGridPaginationSection.perPageOption('Custom')}}" stepKey="selectCustomPerPage"/> + <waitForElementVisible selector="{{AdminDataGridPaginationSection.perPageInput}}" time="30" stepKey="waitForInputVisible"/> + <fillField selector="{{AdminDataGridPaginationSection.perPageInput}}" userInput="{{perPage}}" stepKey="fillCustomPerPage"/> + <click selector="{{AdminDataGridPaginationSection.perPageApplyInput}}" stepKey="applyCustomPerPage"/> + <waitForLoadingMaskToDisappear stepKey="waitForGridLoad"/> + <seeInField selector="{{AdminDataGridPaginationSection.perPageDropDownValue}}" userInput="{{perPage}}" stepKey="seePerPageValueInDropDown"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridSelectPerPageActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridSelectPerPageActionGroup.xml new file mode 100644 index 0000000000000..df342602a62be --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminDataGridSelectPerPageActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminDataGridSelectPerPageActionGroup"> + <annotations> + <description>Sets the provided preset 'per page' display setting on an Admin Grid page.</description> + </annotations> + <arguments> + <argument name="perPage" type="string"/> + </arguments> + + <click selector="{{AdminDataGridPaginationSection.perPageDropdown}}" stepKey="clickPerPageDropdown"/> + <click selector="{{AdminDataGridPaginationSection.perPageOption(perPage)}}" stepKey="selectCustomPerPage"/> + <waitForPageLoad stepKey="waitForGridLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/ClearFiltersAdminDataGridActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/ClearFiltersAdminDataGridActionGroup.xml new file mode 100644 index 0000000000000..74b4a6c75ed2a --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/ClearFiltersAdminDataGridActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ClearFiltersAdminDataGridActionGroup"> + <annotations> + <description>Clicks on 'Clear Filters' on an Admin Grid page.</description> + </annotations> + + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilters"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/ResetAdminDataGridToDefaultViewActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/ResetAdminDataGridToDefaultViewActionGroup.xml new file mode 100644 index 0000000000000..a07e3c80623de --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/ResetAdminDataGridToDefaultViewActionGroup.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="ResetAdminDataGridToDefaultViewActionGroup"> + <annotations> + <description>Resets an Admin Grid page to the 'Default View'.</description> + </annotations> + + <click selector="{{AdminDataGridHeaderSection.bookmarkToggle}}" stepKey="openViewBookmarks"/> + <click selector="{{AdminDataGridHeaderSection.bookmarkOption('Default View')}}" stepKey="selectDefaultGridView"/> + <see selector="{{AdminDataGridHeaderSection.bookmarkToggle}}" userInput="Default View" stepKey="seeDefaultViewSelected"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/SearchAdminDataGridByKeywordActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/SearchAdminDataGridByKeywordActionGroup.xml new file mode 100644 index 0000000000000..4a59b1baa70d2 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/SearchAdminDataGridByKeywordActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SearchAdminDataGridByKeywordActionGroup"> + <annotations> + <description>Fills 'Search by keyword' on an Admin Grid page. Clicks on Submit Search.</description> + </annotations> + <arguments> + <argument name="keyword"/> + </arguments> + + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <fillField selector="{{AdminDataGridHeaderSection.search}}" userInput="{{keyword}}" stepKey="fillKeywordSearchField"/> + <click selector="{{AdminDataGridHeaderSection.submitSearch}}" stepKey="clickKeywordSearch"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminMessagesSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminMessagesSection.xml deleted file mode 100644 index 8dc20142add3f..0000000000000 --- a/app/code/Magento/Ui/Test/Mftf/Section/AdminMessagesSection.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="AdminMessagesSection"> - <element name="successMessage" type="text" selector=".message-success"/> - <element name="errorMessage" type="text" selector=".message.message-error.error"/> - <element name="warningMessage" type="text" selector=".message-warning"/> - <element name="noticeMessage" type="text" selector=".message-notice"/> - <element name="accessDenied" type="text" selector=".access-denied-page"/> - </section> -</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterDeleteAndVerifyErrorMessageTest.xml b/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterDeleteAndVerifyErrorMessageTest.xml index e2c3b157c1059..674d1d2216793 100644 --- a/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterDeleteAndVerifyErrorMessageTest.xml +++ b/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterDeleteAndVerifyErrorMessageTest.xml @@ -32,7 +32,7 @@ <argument name="websiteCode" value="{{customWebsite.code}}"/> </actionGroup> <!-- Create second store --> - <actionGroup ref="CreateCustomStore" stepKey="createCustomStore"> + <actionGroup ref="CreateCustomStoreActionGroup" stepKey="createCustomStore"> <argument name="website" value="{{customWebsite.name}}"/> <argument name="store" value="{{customStoreGroup.name}}"/> <argument name="rootCategory" value="$$rootCategory.name$$"/> @@ -53,7 +53,7 @@ <!--Filter created simple product in grid and add category and website created in create data--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="openProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProduct"> <argument name="product" value="$$createProduct$$"/> </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowOfCreatedSimpleProduct"/> @@ -78,4 +78,4 @@ <argument name="errorMessage" value="Something went wrong with processing the default view and we have restored the filter to its original state."/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php b/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php index 78456968cbef1..20af2627fbb04 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/Filters/Type/DateTest.php @@ -13,34 +13,35 @@ use Magento\Ui\Component\Filters\Type\Date; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Ui\Component\Form\Element\DataType\Date as FormDate; +use PHPUnit\Framework\MockObject\MockObject; /** - * Class DateTest + * Test for Date grid filter functionality */ class DateTest extends \PHPUnit\Framework\TestCase { /** - * @var ContextInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ContextInterface|MockObject */ private $contextMock; /** - * @var UiComponentFactory|\PHPUnit_Framework_MockObject_MockObject + * @var UiComponentFactory|MockObject */ private $uiComponentFactory; /** - * @var FilterBuilder|\PHPUnit_Framework_MockObject_MockObject + * @var FilterBuilder|MockObject */ private $filterBuilderMock; /** - * @var FilterModifier|\PHPUnit_Framework_MockObject_MockObject + * @var FilterModifier|MockObject */ private $filterModifierMock; /** - * @var DataProviderInterface|\PHPUnit_Framework_MockObject_MockObject + * @var DataProviderInterface|MockObject */ private $dataProviderMock; @@ -89,18 +90,19 @@ public function testGetComponentName() * Run test prepare method * * @param string $name + * @param bool $showsTime * @param array $filterData * @param array|null $expectedCondition * @dataProvider getPrepareDataProvider * @return void */ - public function testPrepare($name, $filterData, $expectedCondition) + public function testPrepare(string $name, bool $showsTime, array $filterData, ?array $expectedCondition) { $processor = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\Processor::class) ->disableOriginalConstructor() ->getMock(); $this->contextMock->expects(static::atLeastOnce())->method('getProcessor')->willReturn($processor); - /** @var FormDate $uiComponent */ + /** @var FormDate|MockObject $uiComponent */ $uiComponent = $this->getMockBuilder(FormDate::class) ->disableOriginalConstructor() ->getMock(); @@ -125,7 +127,7 @@ public function testPrepare($name, $filterData, $expectedCondition) ->willReturn($this->dataProviderMock); if ($expectedCondition !== null) { - $this->processFilters($name, $filterData, $expectedCondition, $uiComponent); + $this->processFilters($name, $showsTime, $filterData, $expectedCondition, $uiComponent); } $this->uiComponentFactory->expects($this->any()) @@ -139,7 +141,10 @@ public function testPrepare($name, $filterData, $expectedCondition) $this->filterBuilderMock, $this->filterModifierMock, [], - ['name' => $name] + [ + 'name' => $name, + 'config' => ['options' => ['showsTime' => $showsTime]], + ] ); $date->prepare(); } @@ -152,7 +157,7 @@ public function testPrepare($name, $filterData, $expectedCondition) * @param string $expectedDate * @param int $i * - * @return Filter|\PHPUnit_Framework_MockObject_MockObject + * @return Filter|MockObject */ private function getFilterMock($name, $expectedType, $expectedDate, &$i) { @@ -184,57 +189,92 @@ public function getPrepareDataProvider() { return [ [ - 'test_date', - ['test_date' => ['from' => '11-05-2015', 'to' => null]], - ['date' => '2015-05-11 00:00:00', 'type' => 'gteq'], + 'name' => 'test_date', + 'showsTime' => false, + 'filterData' => ['test_date' => ['from' => '11-05-2015', 'to' => null]], + 'expectedCondition' => ['date' => '2015-05-11 00:00:00', 'type' => 'gteq'], ], [ - 'test_date', - ['test_date' => ['from' => null, 'to' => '11-05-2015']], - ['date' => '2015-05-11 23:59:59', 'type' => 'lteq'], + 'name' => 'test_date', + 'showsTime' => false, + 'filterData' => ['test_date' => ['from' => null, 'to' => '11-05-2015']], + 'expectedCondition' => ['date' => '2015-05-11 23:59:59', 'type' => 'lteq'], ], [ - 'test_date', - ['test_date' => ['from' => '11-05-2015', 'to' => '11-05-2015']], - [ + 'name' => 'test_date', + 'showsTime' => false, + 'filterData' => ['test_date' => ['from' => '11-05-2015', 'to' => '11-05-2015']], + 'expectedCondition' => [ 'date_from' => '2015-05-11 00:00:00', 'type_from' => 'gteq', 'date_to' => '2015-05-11 23:59:59', 'type_to' => 'lteq' ], ], [ - 'test_date', - ['test_date' => '11-05-2015'], - ['date' => '2015-05-11 00:00:00', 'type' => 'eq'], + 'name' => 'test_date', + 'showsTime' => false, + 'filterData' => ['test_date' => '11-05-2015'], + 'expectedCondition' => ['date' => '2015-05-11 00:00:00', 'type' => 'eq'], + ], + [ + 'name' => 'test_date', + 'showsTime' => false, + 'filterData' => ['test_date' => ['from' => '', 'to' => '']], + 'expectedCondition' => null, ], [ - 'test_date', - ['test_date' => ['from' => '', 'to' => '']], - null, + 'name' => 'test_date', + 'showsTime' => true, + 'filterData' => ['test_date' => ['from' => '11-05-2015 10:20:00', 'to' => '11-05-2015 18:25:00']], + 'expectedCondition' => [ + 'date_from' => '2015-05-11 10:20:00', 'type_from' => 'gteq', + 'date_to' => '2015-05-11 18:25:00', 'type_to' => 'lteq' + ], ], ]; } /** - * @param $name - * @param $filterData - * @param $expectedCondition - * @param $uiComponent + * @param string $name + * @param bool $showsTime + * @param array $filterData + * @param array $expectedCondition + * @param MockObject $uiComponent + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - private function processFilters($name, $filterData, $expectedCondition, $uiComponent) - { + private function processFilters( + string $name, + bool $showsTime, + array $filterData, + array $expectedCondition, + FormDate $uiComponent + ) { if (is_string($filterData[$name])) { $uiComponent->expects(static::once()) - ->method('convertDate') + ->method($showsTime ? 'convertDatetime' : 'convertDate') ->with($filterData[$name]) ->willReturn(new \DateTime($filterData[$name])); } else { - $from = new \DateTime($filterData[$name]['from']); - $to = new \DateTime($filterData[$name]['to'] . ' 23:59:59'); - $uiComponent->method('convertDate') - ->willReturnMap([ - [$filterData[$name]['from'], 0, 0, 0, true, $from], - [$filterData[$name]['to'], 23, 59, 59, true, $to], - ]); + if ($showsTime) { + $from = new \DateTime($filterData[$name]['from']); + $to = new \DateTime($filterData[$name]['to']); + $uiComponent->method('convertDatetime') + ->willReturnMap( + [ + [$filterData[$name]['from'], true, $from], + [$filterData[$name]['to'], true, $to], + ] + ); + } else { + $from = new \DateTime($filterData[$name]['from']); + $to = new \DateTime($filterData[$name]['to'] . ' 23:59:59'); + $uiComponent->method('convertDate') + ->willReturnMap( + [ + [$filterData[$name]['from'], 0, 0, 0, true, $from], + [$filterData[$name]['to'], 23, 59, 59, true, $to], + ] + ); + } } $i = 0; diff --git a/app/code/Magento/Ui/Test/Unit/Component/FiltersTest.php b/app/code/Magento/Ui/Test/Unit/Component/FiltersTest.php index 402fd30bf4d5b..d4cf7f1af8d62 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/FiltersTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/FiltersTest.php @@ -8,23 +8,27 @@ namespace Magento\Ui\Test\Unit\Component; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use \Magento\Ui\Component\Filters; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\View\Element\UiComponentFactory; +use Magento\Framework\View\Element\UiComponentInterface; +use Magento\Ui\Component\Filters; +use PHPUnit\Framework\MockObject\MockObject; /** * Unit tests for \Magento\Ui\Component\Filters class */ class FiltersTest extends \PHPUnit\Framework\TestCase { - /** @var Filters|\PHPUnit_Framework_MockObject_MockObject */ + /** @var Filters|MockObject */ private $filters; - /** @var \Magento\Framework\View\Element\UiComponentInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var UiComponentInterface|MockObject */ private $uiComponentInterface; - /** @var \Magento\Framework\View\Element\UiComponentFactory|\PHPUnit_Framework_MockObject_MockObject */ + /** @var UiComponentFactory|MockObject */ private $uiComponentFactory; - /** @var \Magento\Framework\View\Element\UiComponent\ContextInterface|\PHPUnit_Framework_MockObject_MockObject */ + /** @var ContextInterface|MockObject */ private $context; /** @@ -33,13 +37,13 @@ class FiltersTest extends \PHPUnit\Framework\TestCase protected function setUp() { $objectManager = new ObjectManager($this); - $this->uiComponentInterface = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponentInterface::class) + $this->uiComponentInterface = $this->getMockBuilder(UiComponentInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->uiComponentFactory = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponentFactory::class) + $this->uiComponentFactory = $this->getMockBuilder(UiComponentFactory::class) ->disableOriginalConstructor() ->getMock(); - $this->context = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\ContextInterface::class) + $this->context = $this->getMockBuilder(ContextInterface::class) ->disableOriginalConstructor() ->getMock(); $this->filters = $objectManager->getObject( @@ -52,7 +56,14 @@ protected function setUp() ); } - public function testUpdate() + /** + * Test to Update filter component according to $component + * + * @param string $filterType + * @param string $filterName + * @dataProvider updateDataProvider + */ + public function testUpdate(string $filterType, string $filterName) { $componentName = 'component_name'; $componentConfig = [0, 1, 2]; @@ -60,7 +71,10 @@ public function testUpdate() ->disableOriginalConstructor() ->setMethods(['getData', 'getName', 'getConfiguration']) ->getMockForAbstractClass(); - $columnInterface->expects($this->atLeastOnce())->method('getData')->with('config/filter')->willReturn('text'); + $columnInterface->expects($this->atLeastOnce()) + ->method('getData') + ->with('config/filter') + ->willReturn($filterType); $columnInterface->expects($this->atLeastOnce())->method('getName')->willReturn($componentName); $columnInterface->expects($this->once())->method('getConfiguration')->willReturn($componentConfig); $filterComponent = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponentInterface::class) @@ -71,11 +85,22 @@ public function testUpdate() ->willReturnSelf(); $filterComponent->expects($this->once())->method('prepare')->willReturnSelf(); $this->uiComponentFactory->expects($this->once())->method('create') - ->with($componentName, 'filterInput', ['context' => $this->context]) + ->with($componentName, $filterName, ['context' => $this->context]) ->willReturn($filterComponent); $this->filters->update($columnInterface); /** Verify that filter is already set and it wouldn't be set again */ $this->filters->update($columnInterface); } + + /** + * @return array + */ + public function updateDataProvider(): array + { + return [ + ['text', 'filterInput'], + ['datetimeRange', 'filterDate'], + ]; + } } diff --git a/app/code/Magento/Ui/Test/Unit/Component/Form/Element/DataType/DateTest.php b/app/code/Magento/Ui/Test/Unit/Component/Form/Element/DataType/DateTest.php index cdb11f05daa8c..015c025e7c102 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/Form/Element/DataType/DateTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/Form/Element/DataType/DateTest.php @@ -11,27 +11,35 @@ use Magento\Framework\View\Element\UiComponent\Context; use Magento\Framework\View\Element\UiComponent\Processor; use Magento\Ui\Component\Form\Element\DataType\Date; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class DateTest extends \PHPUnit\Framework\TestCase +/** + * Class to test Date form element + */ +class DateTest extends TestCase { - /** @var \PHPUnit_Framework_MockObject_MockObject */ + /** @var Context|MockObject */ private $contextMock; - /** @var \PHPUnit_Framework_MockObject_MockObject */ + /** @var TimezoneInterface|MockObject */ private $localeDateMock; - /** @var \PHPUnit_Framework_MockObject_MockObject */ + /** @var ResolverInterface|MockObject */ private $localeResolverMock; - /** @var \Magento\Ui\Component\Form\Element\DataType\Date */ + /** @var Date */ private $date; - /** @var \PHPUnit_Framework_MockObject_MockObject */ + /** @var Processor|MockObject */ private $processorMock; - /** @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */ + /** @var ObjectManager */ private $objectManagerHelper; + /** + * @inheritdoc + */ public function setUp() { $this->contextMock = $this->createMock(Context::class); @@ -39,9 +47,12 @@ public function setUp() $this->localeResolverMock = $this->createMock(ResolverInterface::class); $this->objectManagerHelper = new ObjectManager($this); $this->processorMock = $this->createMock(Processor::class); - $this->contextMock->expects($this->atLeastOnce())->method('getProcessor')->willReturn($this->processorMock); + $this->contextMock->method('getProcessor')->willReturn($this->processorMock); } + /** + * Test to Prepare component configuration with Time offset + */ public function testPrepareWithTimeOffset() { $this->date = new Date( @@ -72,6 +83,9 @@ public function testPrepareWithTimeOffset() $this->assertEquals($localeDateFormat, $config['options']['dateFormat']); } + /** + * Test to Prepare component configuration without Time offset + */ public function testPrepareWithoutTimeOffset() { $defaultDateFormat = 'MM/dd/y'; @@ -130,4 +144,43 @@ public function testPrepare() $this->assertEquals('America/Chicago', $configArray['storeTimeZone']); $this->assertEquals('de-DE', $configArray['options']['storeLocale']); } + + /** + * Test to Convert given date to default (UTC) timezone + * + * @param string $dateStr + * @param bool $setUtcTimeZone + * @param string $convertedDate + * @dataProvider convertDatetimeDataProvider + */ + public function testConvertDatetime(string $dateStr, bool $setUtcTimeZone, string $convertedDate) + { + $this->localeDateMock->method('getConfigTimezone') + ->willReturn('America/Los_Angeles'); + + $this->date = $this->objectManagerHelper->getObject( + Date::class, + [ + 'localeDate' => $this->localeDateMock, + ] + ); + + $this->assertEquals( + $convertedDate, + $this->date->convertDatetime($dateStr, $setUtcTimeZone)->format('Y-m-d H:i:s'), + "The date value wasn't converted" + ); + } + + /** + * @return array + */ + public function convertDatetimeDataProvider(): array + { + return [ + ['2019-09-30T12:32:00.000Z', false, '2019-09-30 12:32:00'], + ['2019-09-30T12:32:00.000', false, '2019-09-30 12:32:00'], + ['2019-09-30T12:32:00.000Z', true, '2019-09-30 19:32:00'], + ]; + } } diff --git a/app/code/Magento/Ui/registration.php b/app/code/Magento/Ui/registration.php index 28d4d44e82d9b..d6d80766703fe 100644 --- a/app/code/Magento/Ui/registration.php +++ b/app/code/Magento/Ui/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Ui', __DIR__); diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/date.js b/app/code/Magento/Ui/view/base/web/js/form/element/date.js index ac28271e90a3b..1432372dd75a9 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/date.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/date.js @@ -107,6 +107,13 @@ define([ return this._super().observe(['shiftedValue']); }, + /** + * @inheritdoc + */ + getPreview: function () { + return this.shiftedValue(); + }, + /** * Prepares and sets date/time value that will be displayed * in the input field. diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js b/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js index 0eaacdc32567b..72c352f353239 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/post-code.js @@ -8,14 +8,14 @@ */ define([ 'underscore', - 'uiRegistry', './abstract' -], function (_, registry, Abstract) { +], function (_, Abstract) { 'use strict'; return Abstract.extend({ defaults: { imports: { + countryOptions: '${ $.parentName }.country_id:indexedOptions', update: '${ $.parentName }.country_id:value' } }, @@ -41,31 +41,32 @@ define([ }, /** - * @param {String} value + * Method called every time country selector's value gets changed. + * Updates all validations and requirements for certain country. + * @param {String} value - Selected country ID. */ update: function (value) { - var country = registry.get(this.parentName + '.' + 'country_id'), - options = country.indexedOptions, - option = null; + var isZipCodeOptional, + option; if (!value) { return; } - option = options[value]; + option = _.isObject(this.countryOptions) && this.countryOptions[value]; if (!option) { return; } - if (option['is_zipcode_optional']) { + isZipCodeOptional = !!option['is_zipcode_optional']; + + if (isZipCodeOptional) { this.error(false); - this.validation = _.omit(this.validation, 'required-entry'); - } else { - this.validation['required-entry'] = true; } - this.required(!option['is_zipcode_optional']); + this.validation['required-entry'] = !isZipCodeOptional; + this.required(!isZipCodeOptional); } }); }); diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/region.js b/app/code/Magento/Ui/view/base/web/js/form/element/region.js index f6eafcf49284d..cd9c2aee85dc6 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/region.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/region.js @@ -18,81 +18,54 @@ define([ defaults: { skipValidation: false, imports: { + countryOptions: '${ $.parentName }.country_id:indexedOptions', update: '${ $.parentName }.country_id:value' } }, /** - * @param {String} value + * Method called every time country selector's value gets changed. + * Updates all validations and requirements for certain country. + * @param {String} value - Selected country ID. */ update: function (value) { - var country = registry.get(this.parentName + '.' + 'country_id'), - options = country.indexedOptions, - isRegionRequired, + var isRegionRequired, option; if (!value) { return; } - option = options[value]; - if (typeof option === 'undefined') { + option = _.isObject(this.countryOptions) && this.countryOptions[value]; + + if (!option) { return; } defaultPostCodeResolver.setUseDefaultPostCode(!option['is_zipcode_optional']); - if (this.skipValidation) { - this.validation['required-entry'] = false; - this.required(false); - } else { - if (option && !option['is_region_required']) { - this.error(false); - this.validation = _.omit(this.validation, 'required-entry'); - registry.get(this.customName, function (input) { - input.validation['required-entry'] = false; - input.required(false); - }); - } else { - this.validation['required-entry'] = true; - } + if (option['is_region_visible'] === false) { + // Hide select and corresponding text input field if region must not be shown for selected country. + this.setVisible(false); - if (option && !this.options().length) { - registry.get(this.customName, function (input) { - isRegionRequired = !!option['is_region_required']; - input.validation['required-entry'] = isRegionRequired; - input.validation['validate-not-number-first'] = true; - input.required(isRegionRequired); - }); + if (this.customEntry) { // eslint-disable-line max-depth + this.toggleInput(false); } - - this.required(!!option['is_region_required']); } - }, - /** - * Filters 'initialOptions' property by 'field' and 'value' passed, - * calls 'setOptions' passing the result to it - * - * @param {*} value - * @param {String} field - */ - filter: function (value, field) { - var superFn = this._super; - - registry.get(this.parentName + '.' + 'country_id', function (country) { - var option = country.indexedOptions[value]; + isRegionRequired = !this.skipValidation && !!option['is_region_required']; - superFn.call(this, value, field); + if (!isRegionRequired) { + this.error(false); + } - if (option && option['is_region_visible'] === false) { - // hide select and corresponding text input field if region must not be shown for selected country - this.setVisible(false); + this.required(isRegionRequired); + this.validation['required-entry'] = isRegionRequired; - if (this.customEntry) {// eslint-disable-line max-depth - this.toggleInput(false); - } - } + registry.get(this.customName, function (input) { + input.required(isRegionRequired); + input.validation['required-entry'] = isRegionRequired; + input.validation['validate-not-number-first'] = !this.options().length; }.bind(this)); } }); diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/date.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/date.js index 3f9c5b20d6215..88959cda7499d 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/date.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/date.js @@ -9,8 +9,10 @@ define([ 'mageUtils', 'moment', - './column' -], function (utils, moment, Column) { + './column', + 'underscore', + 'moment-timezone-with-data' +], function (utils, moment, Column, _) { 'use strict'; return Column.extend({ @@ -20,9 +22,9 @@ define([ }, /** - * Overrides base method to normalize date format. + * Overrides base method to normalize date format * - * @returns {DateColumn} Chainable. + * @returns {DateColumn} Chainable */ initConfig: function () { this._super(); @@ -43,7 +45,12 @@ define([ if (this.storeLocale !== undefined) { moment.locale(this.storeLocale, utils.extend({}, this.calendarConfig)); } - date = moment(this._super()); + + date = moment.utc(this._super()); + + if (!_.isUndefined(this.timezone)) { + date = date.tz(this.timezone); + } date = date.isValid() && value[this.index] ? date.format(format || this.dateFormat) : diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js new file mode 100644 index 0000000000000..2549fa93a834f --- /dev/null +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js @@ -0,0 +1,251 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +define([ + 'jquery', + 'Magento_Ui/js/grid/columns/column', + 'Magento_Ui/js/lib/key-codes' +], function ($, Column, keyCodes) { + 'use strict'; + + return Column.extend({ + defaults: { + bodyTmpl: 'ui/grid/columns/image-preview', + previewImageSelector: '[data-image-preview]', + visibleRecord: null, + height: 0, + displayedRecord: {}, + lastOpenedImage: null, + fields: { + previewUrl: 'preview_url', + title: 'title' + }, + modules: { + masonry: '${ $.parentName }', + thumbnailComponent: '${ $.parentName }.thumbnail_url' + }, + statefull: { + sorting: true, + lastOpenedImage: true + }, + listens: { + '${ $.provider }:params.filters': 'hide', + '${ $.provider }:params.search': 'hide', + '${ $.provider }:params.paging': 'hide' + }, + exports: { + height: '${ $.parentName }.thumbnail_url:previewHeight' + } + }, + + /** + * Initialize image preview component + * + * @returns {Object} + */ + initialize: function () { + this._super(); + $(document).on('keydown', this.handleKeyDown.bind(this)); + + return this; + }, + + /** + * Init observable variables + * @return {Object} + */ + initObservable: function () { + this._super() + .observe([ + 'visibleRecord', + 'height', + 'displayedRecord', + 'lastOpenedImage' + ]); + + return this; + }, + + /** + * Next image preview + * + * @param {Object} record + */ + next: function (record) { + var recordToShow; + + if (record._rowIndex + 1 === this.masonry().rows().length) { + return; + } + + recordToShow = this.getRecord(record._rowIndex + 1); + recordToShow.rowNumber = record.lastInRow ? record.rowNumber + 1 : record.rowNumber; + this.show(recordToShow); + }, + + /** + * Previous image preview + * + * @param {Object} record + */ + prev: function (record) { + var recordToShow; + + if (record._rowIndex === 0) { + return; + } + recordToShow = this.getRecord(record._rowIndex - 1); + + recordToShow.rowNumber = record.firstInRow ? record.rowNumber - 1 : record.rowNumber; + this.show(recordToShow); + }, + + /** + * Get record + * + * @param {Integer} recordIndex + * + * @return {Object} + */ + getRecord: function (recordIndex) { + return this.masonry().rows()[recordIndex]; + }, + + /** + * Set selected row id + * + * @param {Number} rowId + * @private + */ + _selectRow: function (rowId) { + this.thumbnailComponent().previewRowId(rowId); + }, + + /** + * Show image preview + * + * @param {Object} record + */ + show: function (record) { + var img; + + if (record._rowIndex === this.visibleRecord()) { + this.hide(); + + return; + } + + this.hide(); + this.displayedRecord(record); + this._selectRow(record.rowNumber || null); + this.visibleRecord(record._rowIndex); + + img = $(this.previewImageSelector + ' img'); + + if (img.get(0).complete) { + this.updateHeight(); + this.scrollToPreview(); + } else { + img.load(function () { + this.updateHeight(); + this.scrollToPreview(); + }.bind(this)); + } + + this.lastOpenedImage(record._rowIndex); + }, + + /** + * Update image preview section height + */ + updateHeight: function () { + this.height($(this.previewImageSelector).height() + 'px'); + }, + + /** + * Close image preview + */ + hide: function () { + this.lastOpenedImage(null); + this.visibleRecord(null); + this.height(0); + this._selectRow(null); + }, + + /** + * Returns visibility for given record. + * + * @param {Object} record + * @return {*|bool} + */ + isVisible: function (record) { + if (this.lastOpenedImage() === record._rowIndex && + this.visibleRecord() === null + ) { + this.show(record); + } + + return this.visibleRecord() === record._rowIndex || false; + }, + + /** + * Returns preview image url for a given record. + * + * @param {Object} record + * @return {String} + */ + getUrl: function (record) { + return record[this.fields.previewUrl]; + }, + + /** + * Returns image title for a given record. + * + * @param {Object} record + * @return {String} + */ + getTitle: function (record) { + return record[this.fields.title]; + }, + + /** + * Get styles for preview + * + * @returns {Object} + */ + getStyles: function () { + return { + 'margin-top': '-' + this.height() + }; + }, + + /** + * Scroll to preview window + */ + scrollToPreview: function () { + $(this.previewImageSelector).get(0).scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'nearest' + }); + }, + + /** + * Handle keyboard navigation for image preview + * + * @param {Object} e + */ + handleKeyDown: function (e) { + var key = keyCodes[e.keyCode]; + + if (this.visibleRecord() !== null) { + if (key === 'pageLeftKey') { + this.prev(this.displayedRecord()); + } else if (key === 'pageRightKey') { + this.next(this.displayedRecord()); + } + } + } + }); +}); diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/image.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/image.js new file mode 100644 index 0000000000000..e8e1cf3246c76 --- /dev/null +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/image.js @@ -0,0 +1,101 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +define([ + 'Magento_Ui/js/grid/columns/column' +], function (Column) { + 'use strict'; + + return Column.extend({ + defaults: { + bodyTmpl: 'ui/grid/columns/image', + modules: { + previewComponent: '${ $.parentName }.preview' + }, + previewRowId: null, + previewHeight: 0, + fields: { + id: 'id', + url: 'url' + } + }, + + /** + * Init observable variables + * @return {Object} + */ + initObservable: function () { + this._super() + .observe([ + 'previewRowId', + 'previewHeight' + ]); + + return this; + }, + + /** + * Returns url to given record. + * + * @param {Object} record - Data to be preprocessed. + * @returns {String} + */ + getUrl: function (record) { + return record[this.fields.url]; + }, + + /** + * Returns id to given record. + * + * @param {Object} record - Data to be preprocessed. + * @returns {Number} + */ + getId: function (record) { + return record[this.fields.id]; + }, + + /** + * Returns container styles to given record. + * + * @param {Object} record - Data to be preprocessed. + * @returns {Object} + */ + getStyles: function (record) { + var styles = record.styles(); + + styles['margin-bottom'] = this.previewRowId() === record.rowNumber ? this.previewHeight : 0; + record.styles(styles); + + return record.styles; + }, + + /** + * Returns class list to given record. + * + * @param {Object} record - Data to be preprocessed. + * @returns {Object} + */ + getClasses: function (record) { + return record.css || {}; + }, + + /** + * Get is active record + * + * @param {Object} record - Data to be preprocessed. + * + * @returns {Boolean} + */ + getIsActive: function (record) { + return this.previewComponent().visibleRecord() === record._rowIndex || false; + }, + + /** + * Expand image preview + */ + expandPreview: function (record) { + this.previewComponent().show(record); + } + }); +}); diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/overlay.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/overlay.js new file mode 100644 index 0000000000000..420b318e0b440 --- /dev/null +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/overlay.js @@ -0,0 +1,35 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +define([ + 'Magento_Ui/js/grid/columns/column' +], function (Column) { + 'use strict'; + + return Column.extend({ + defaults: { + bodyTmpl: 'ui/grid/columns/overlay' + }, + + /** + * If overlay should be visible + * + * @param {Object} row + * @returns {Boolean} + */ + isVisible: function (row) { + return !!row[this.index]; + }, + + /** + * Get overlay label + * + * @param {Object} row + * @returns {String} + */ + getLabel: function (row) { + return row[this.index]; + } + }); +}); diff --git a/app/code/Magento/Ui/view/base/web/js/grid/editing/editor.js b/app/code/Magento/Ui/view/base/web/js/grid/editing/editor.js index ece49cc8fe27c..ad70b200e4420 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/editing/editor.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/editing/editor.js @@ -337,7 +337,18 @@ define([ * @returns {Object} Collection of records data. */ getData: function () { - var data = this.activeRecords.map('getData'); + var data = this.activeRecords.map(function (record) { + var elemKey, + recordData = record.getData(); + + for (elemKey in recordData) { + if (_.isUndefined(recordData[elemKey])) { + recordData[elemKey] = null; + } + } + + return recordData; + }); return _.indexBy(data, this.indexField); }, diff --git a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js index 78016ee489a11..fe33389eabad4 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js @@ -83,6 +83,10 @@ define([ component: 'Magento_Ui/js/grid/filters/range', rangeType: 'date' }, + datetimeRange: { + component: 'Magento_Ui/js/grid/filters/range', + rangeType: 'datetime' + }, textRange: { component: 'Magento_Ui/js/grid/filters/range', rangeType: 'text' diff --git a/app/code/Magento/Ui/view/base/web/js/grid/filters/range.js b/app/code/Magento/Ui/view/base/web/js/grid/filters/range.js index ccfba8e98b6f4..1949234c89324 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/filters/range.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/filters/range.js @@ -30,6 +30,14 @@ define([ dateFormat: 'MM/dd/YYYY', shiftedValue: 'filter' }, + datetime: { + component: 'Magento_Ui/js/form/element/date', + dateFormat: 'MM/dd/YYYY', + shiftedValue: 'filter', + options: { + showsTime: true + } + }, text: { component: 'Magento_Ui/js/form/element/abstract' }, diff --git a/app/code/Magento/Ui/view/base/web/js/grid/masonry.js b/app/code/Magento/Ui/view/base/web/js/grid/masonry.js new file mode 100644 index 0000000000000..e4c72ee950c26 --- /dev/null +++ b/app/code/Magento/Ui/view/base/web/js/grid/masonry.js @@ -0,0 +1,276 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +define([ + 'Magento_Ui/js/grid/listing', + 'Magento_Ui/js/lib/view/utils/raf', + 'jquery', + 'ko', + 'underscore' +], function (Listing, raf, $, ko, _) { + 'use strict'; + + return Listing.extend({ + defaults: { + template: 'ui/grid/masonry', + imports: { + rows: '${ $.provider }:data.items', + errorMessage: '${ $.provider }:data.errorMessage' + }, + listens: { + rows: 'initComponent' + }, + + /** + * Images container id + * @param string + */ + containerId: null, + + /** + * Minimum aspect ratio for each image + * @param int + */ + minRatio: null, + + /** + * Container width + * @param int + */ + containerWidth: window.innerWidth, + + /** + * Margin between images + * @param int + */ + imageMargin: 20, + + /** + * Maximum image height value + * @param int + */ + maxImageHeight: 240, + + /** + * The value is minimum image width to height ratio when container width is less than the key + * @param {Object} + */ + containerWidthToMinRatio: { + 640: 3, + 1280: 5, + 1920: 8 + }, + + /** + * Default minimal image width to height ratio. + * Applied when container width is greater than max width in the containerWidthToMinRatio matrix. + * @param int + */ + defaultMinRatio: 10, + + /** + * Layout update FPS during window resizing + */ + refreshFPS: 60 + }, + + /** + * Init observable variables + * @return {Object} + */ + initObservable: function () { + this._super() + .observe([ + 'rows', + 'errorMessage' + ]); + + return this; + }, + + /** + * Init component handler + * @param {Object} rows + * @return {Object} + */ + initComponent: function (rows) { + if (!rows.length) { + return; + } + this.imageMargin = parseInt(this.imageMargin, 10); + this.container = $('[data-id="' + this.containerId + '"]')[0]; + + this.setLayoutStyles(); + this.setEventListener(); + + return this; + }, + + /** + * Set event listener to track resize event + */ + setEventListener: function () { + window.addEventListener('resize', function () { + raf(function () { + this.containerWidth = window.innerWidth; + this.setLayoutStyles(); + }.bind(this), this.refreshFPS); + }.bind(this)); + }, + + /** + * Set layout styles inside the container + */ + setLayoutStyles: function () { + var containerWidth = parseInt(this.container.clientWidth, 10), + rowImages = [], + ratio = 0, + rowHeight = 0, + calcHeight = 0, + isLastRow = false, + rowNumber = 1; + + this.setMinRatio(); + + this.rows().forEach(function (image, index) { + ratio += parseFloat((image.width / image.height).toFixed(2)); + rowImages.push(image); + + if (ratio < this.minRatio && index + 1 !== this.rows().length) { + // Row has more space for images and the image is not the last one - proceed to the next iteration + return; + } + + ratio = Math.max(ratio, this.minRatio); + calcHeight = (containerWidth - this.imageMargin * rowImages.length) / ratio; + rowHeight = calcHeight < this.maxImageHeight ? calcHeight : this.maxImageHeight; + isLastRow = index + 1 === this.rows().length; + + this.assignImagesToRow(rowImages, rowNumber, rowHeight, isLastRow); + + rowImages = []; + ratio = 0; + rowNumber++; + + }.bind(this)); + }, + + /** + * Apply styles, css classes and add properties for images in the row + * + * @param {Object[]} images + * @param {Number} rowNumber + * @param {Number} rowHeight + * @param {Boolean} isLastRow + */ + assignImagesToRow: function (images, rowNumber, rowHeight, isLastRow) { + var imageWidth; + + images.forEach(function (img) { + imageWidth = rowHeight * (img.width / img.height).toFixed(2); + this.setImageStyles(img, imageWidth, rowHeight); + this.setImageClass(img, { + bottom: isLastRow + }); + img.rowNumber = rowNumber; + }.bind(this)); + + images[0].firstInRow = true; + images[images.length - 1].lastInRow = true; + }, + + /** + * Wait for container to initialize + */ + waitForContainer: function (callback) { + if (typeof this.container === 'undefined') { + setTimeout(function () { + this.waitForContainer(callback); + }.bind(this), 500); + } else { + setTimeout(callback, 0); + } + }, + + /** + * Set layout styles when container element is loaded. + */ + setLayoutStylesWhenLoaded: function () { + this.waitForContainer(function () { + this.setLayoutStyles(); + }.bind(this)); + }, + + /** + * Set styles for every image in layout + * + * @param {Object} img + * @param {Number} width + * @param {Number} height + */ + setImageStyles: function (img, width, height) { + if (!img.styles) { + img.styles = ko.observable(); + } + img.styles({ + width: parseInt(width, 10) + 'px', + height: parseInt(height, 10) + 'px' + }); + }, + + /** + * Set css classes to and an image + * + * @param {Object} image + * @param {Object} classes + */ + setImageClass: function (image, classes) { + if (!image.css) { + image.css = ko.observable(classes); + } + image.css(classes); + }, + + /** + * Set min ratio for images in layout + */ + setMinRatio: function () { + var minRatio = _.find( + this.containerWidthToMinRatio, + + /** + * Find the minimal ratio for container width in the matrix + * + * @param {Number} ratio + * @param {Number} width + * @returns {Boolean} + */ + function (ratio, width) { + return this.containerWidth <= width; + }, + this + ); + + this.minRatio = minRatio ? minRatio : this.defaultMinRatio; + }, + + /** + * Checks if grid has data. + * + * @returns {Boolean} + */ + hasData: function () { + return !!this.rows() && !!this.rows().length; + }, + + /** + * Returns error message returned by the data provider + * + * @returns {String|null} + */ + getErrorMessageUnsanitizedHtml: function () { + return this.errorMessage(); + } + }); +}); diff --git a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/color-picker.js b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/color-picker.js index c678b85276093..1e8e89894d22f 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/color-picker.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/color-picker.js @@ -71,6 +71,11 @@ define([ update: function (element, valueAccessor, allBindings, viewModel) { var config = valueAccessor(); + /** Initialise value as empty if it is undefined when color picker input is reset **/ + if (config.value() === undefined) { + config.value(''); + } + if (tinycolor(config.value()).isValid() || config.value() === '') { $(element).spectrum('set', config.value()); diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index 08f67955976c4..825b7f4a0546e 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js @@ -830,7 +830,7 @@ define([ ], 'validate-state': [ function (value) { - return value !== 0 || value === ''; + return value !== 0; }, $.mage.__('Please select State/Province.') ], diff --git a/app/code/Magento/Ui/view/base/web/js/modal/modal.js b/app/code/Magento/Ui/view/base/web/js/modal/modal.js index a8a76206bcd2b..bcbb2f3c31dbd 100644 --- a/app/code/Magento/Ui/view/base/web/js/modal/modal.js +++ b/app/code/Magento/Ui/view/base/web/js/modal/modal.js @@ -15,6 +15,7 @@ define([ 'text!ui/template/modal/modal-custom.html', 'Magento_Ui/js/lib/key-codes', 'jquery-ui-modules/widget', + 'jquery-ui-modules/core', 'mage/translate' ], function ($, _, template, popupTpl, slideTpl, customTpl, keyCodes) { 'use strict'; @@ -131,7 +132,10 @@ define([ this._createWrapper(); this._renderModal(); this._createButtons(); - $(this.options.trigger).on('click', _.bind(this.toggleModal, this)); + + if (this.options.trigger) { + $(document).on('click', this.options.trigger, _.bind(this.toggleModal, this)); + } this._on(this.modal.find(this.options.modalCloseBtn), { 'click': this.options.modalCloseBtnHandler ? this.options.modalCloseBtnHandler : this.closeModal }); diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/columns/image-preview.html b/app/code/Magento/Ui/view/base/web/templates/grid/columns/image-preview.html new file mode 100644 index 0000000000000..3b430cf2dcbdc --- /dev/null +++ b/app/code/Magento/Ui/view/base/web/templates/grid/columns/image-preview.html @@ -0,0 +1,22 @@ +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<div class="masonry-image-preview" if="$col.isVisible($row())" data-image-preview ko-style="$col.getStyles($row())"> + <div class="container"> + <div class="action-buttons"> + <button class="action-previous" type="button" click="$col.prev.bind($col, $row())"> + <span translate="'Previous'"/> + </button> + <button class="action-next" type="button" click="$col.next.bind($col, $row())"> + <span translate="'Next'"/> + </button> + <button class="action-close" type="button" click="$col.hide.bind($col)"> + <span translate="'Close'"/> + </button> + </div> + <img class="preview" attr="src: $col.getUrl($row()), alt: $col.getTitle($row())"> + </div> +</div> diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/columns/image.html b/app/code/Magento/Ui/view/base/web/templates/grid/columns/image.html new file mode 100644 index 0000000000000..fa0074ad72283 --- /dev/null +++ b/app/code/Magento/Ui/view/base/web/templates/grid/columns/image.html @@ -0,0 +1,9 @@ +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<div class="masonry-image-block" ko-style="$col.getStyles($row())" css="{'active': $col.getIsActive($row())}" attr="'data-id': $col.getId($row())"> + <img attr="src: $col.getUrl($row())" css="$col.getClasses($row())" click="function(){ expandPreview($row()) }" data-role="thumbnail"/> +</div> diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/columns/overlay.html b/app/code/Magento/Ui/view/base/web/templates/grid/columns/overlay.html new file mode 100644 index 0000000000000..3cdc78c0683cb --- /dev/null +++ b/app/code/Magento/Ui/view/base/web/templates/grid/columns/overlay.html @@ -0,0 +1,9 @@ +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<div if="$col.isVisible($row())" class="masonry-image-overlay"> + <span text="$col.getLabel($row())"/> +</div> diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/bookmarks.html b/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/bookmarks.html index 36a3232c3e61a..6d50ed7e5bd03 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/bookmarks.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/bookmarks.html @@ -9,9 +9,9 @@ <span class="admin__action-dropdown-text" translate="activeView.label"/> </button> <ul class="admin__action-dropdown-menu"> - <repeat args="foreach: viewsArray, item: '$view'"> - <li css="_edit: isEditing($view().index)" outerClick="endEdit.bind($data, $view().index)" template="viewTmpl"/> - </repeat> + <!-- ko foreach: { data: viewsArray, as: '$view'} --> + <li css="_edit: $parent.isEditing($view.index)" outerClick="$parent.endEdit.bind($parent, $view.index)" template="$parent.viewTmpl"/> + <!-- /ko --> <li visible="hasChanges" outerClick="hideCustom.bind($data)" css=" _edit: customVisible, diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/view.html b/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/view.html index 521ce9fc806ac..1262fce544599 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/view.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/controls/bookmarks/view.html @@ -4,36 +4,36 @@ * See COPYING.txt for license details. */ --> -<div class="action-dropdown-menu-item-edit" if="$view().editable"> +<div class="action-dropdown-menu-item-edit" if="$view.editable"> <input class="admin__control-text" data-bind=" - value: $view().value, - hasFocus: isEditing($view().index), + value: $view.value, + hasFocus: $parent.isEditing($view.index), autoselect, attr: { - placeholder: $view().label + placeholder: $view.label }, keyboard: { - 13: updateAndSave.bind($data, $view().index), - 27: endEdit.bind($data, $view().index) + 13: $parent.updateAndSave.bind($parent, $view.index), + 27: $parent.endEdit.bind($parent, $view.index) }" type="text"> - <button class="action-submit" type="button" attr="title: $t('Save all changes')" click="updateAndSave.bind($data, $view().index)"> + <button class="action-submit" type="button" attr="title: $t('Save all changes')" click="$parent.updateAndSave.bind($parent, $view.index)"> <span translate="'Submit'"/> </button> <div class="action-dropdown-menu-item-actions"> - <button class="action-delete" type="button" attr="title: $t('Delete bookmark')" click="removeView.bind($data, $view().index)"> + <button class="action-delete" type="button" attr="title: $t('Delete bookmark')" click="$parent.removeView.bind($parent, $view.index)"> <span translate="'Delete'"/> </button> </div> </div> <div class="action-dropdown-menu-item"> - <a href="" class="action-dropdown-menu-link" translate="$view().label" click="applyView.bind($data, $view().index)" closeCollapsible/> + <a href="" class="action-dropdown-menu-link" translate="$view.label" click="$parent.applyView.bind($parent, $view.index)" closeCollapsible/> - <div class="action-dropdown-menu-item-actions" if="$view().editable"> - <button class="action-edit" type="button" attr="title: $t('Edit bookmark')" click="editView.bind($data, $view().index)"> + <div class="action-dropdown-menu-item-actions" if="$view.editable"> + <button class="action-edit" type="button" attr="title: $t('Edit bookmark')" click="$parent.editView.bind($parent, $view.index)"> <span translate="'Edit'"/> </button> </div> diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/masonry.html b/app/code/Magento/Ui/view/base/web/templates/grid/masonry.html new file mode 100644 index 0000000000000..089ee21bec15c --- /dev/null +++ b/app/code/Magento/Ui/view/base/web/templates/grid/masonry.html @@ -0,0 +1,17 @@ +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<div data-role="grid-wrapper" class="masonry-image-grid" attr="'data-id': containerId"> + <div class="masonry-image-column" repeat="foreach: rows, item: '$row'"> + <div outerfasteach="data: getVisible(), as: '$col'" template="getBody()"/> + </div> + <div if="!hasData() && !getErrorMessageUnsanitizedHtml()" class="no-data-message-container"> + <span translate="'We couldn\'t find any records.'"/> + </div> + <div if="getErrorMessageUnsanitizedHtml()" class="error-message-container"> + <span html="getErrorMessageUnsanitizedHtml()"/> + </div> +</div> diff --git a/app/code/Magento/Ui/view/frontend/web/js/view/messages.js b/app/code/Magento/Ui/view/frontend/web/js/view/messages.js index b2fb3f216199b..b34eea5aa226d 100644 --- a/app/code/Magento/Ui/view/frontend/web/js/view/messages.js +++ b/app/code/Magento/Ui/view/frontend/web/js/view/messages.js @@ -20,6 +20,8 @@ define([ template: 'Magento_Ui/messages', selector: '[data-role=checkout-messages]', isHidden: false, + hideTimeout: 5000, + hideSpeed: 500, listens: { isHidden: 'onHiddenChange' } @@ -63,13 +65,11 @@ define([ * @param {Boolean} isHidden */ onHiddenChange: function (isHidden) { - var self = this; - // Hide message block if needed if (isHidden) { setTimeout(function () { - $(self.selector).hide('blind', {}, 500); - }, 5000); + $(this.selector).hide('blind', {}, this.hideSpeed); + }.bind(this), this.hideTimeout); } } }); diff --git a/app/code/Magento/Ups/Model/Carrier.php b/app/code/Magento/Ups/Model/Carrier.php index 72b68c476d88a..103ba9d3fb4b7 100644 --- a/app/code/Magento/Ups/Model/Carrier.php +++ b/app/code/Magento/Ups/Model/Carrier.php @@ -7,6 +7,12 @@ namespace Magento\Ups\Model; +use Magento\CatalogInventory\Api\StockRegistryInterface; +use Magento\Directory\Helper\Data; +use Magento\Directory\Model\CountryFactory; +use Magento\Directory\Model\CurrencyFactory; +use Magento\Directory\Model\RegionFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ObjectManager; use Magento\Framework\Async\CallbackDeferred; use Magento\Framework\DataObject; @@ -15,16 +21,30 @@ use Magento\Framework\HTTP\AsyncClient\Request; use Magento\Framework\HTTP\AsyncClientInterface; use Magento\Framework\HTTP\ClientFactory; +use Magento\Framework\Locale\FormatInterface; use Magento\Framework\Xml\Security; use Magento\Quote\Model\Quote\Address\RateRequest; use Magento\Quote\Model\Quote\Address\RateResult\Error; +use Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory as RateErrorFactory; +use Magento\Quote\Model\Quote\Address\RateResult\MethodFactory as RateMethodFactory; +use Magento\Sales\Model\Order\Shipment as OrderShipment; use Magento\Shipping\Model\Carrier\AbstractCarrierOnline; use Magento\Shipping\Model\Carrier\CarrierInterface; use Magento\Shipping\Model\Rate\Result; use Magento\Shipping\Model\Rate\Result\ProxyDeferredFactory; +use Magento\Shipping\Model\Rate\ResultFactory as RateFactory; use Magento\Shipping\Model\Simplexml\Element; +use Magento\Shipping\Model\Simplexml\ElementFactory; +use Magento\Shipping\Model\Tracking\Result\ErrorFactory as TrackErrorFactory; +use Magento\Shipping\Model\Tracking\Result\StatusFactory as TrackStatusFactory; +use Magento\Shipping\Model\Tracking\ResultFactory as TrackFactory; +use Magento\Store\Model\ScopeInterface; use Magento\Ups\Helper\Config; use Magento\Shipping\Model\Shipment\Request as Shipment; +use Psr\Log\LoggerInterface; +use RuntimeException; +use Throwable; +use Zend_Http_Client; /** * UPS shipping implementation @@ -117,12 +137,12 @@ class Carrier extends AbstractCarrierOnline implements CarrierInterface protected $_customizableContainerTypes = ['CP', 'CSP']; /** - * @var \Magento\Framework\Locale\FormatInterface + * @var FormatInterface */ protected $_localeFormat; /** - * @var \Psr\Log\LoggerInterface + * @var LoggerInterface */ protected $_logger; @@ -149,22 +169,22 @@ class Carrier extends AbstractCarrierOnline implements CarrierInterface private $deferredProxyFactory; /** - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory - * @param \Psr\Log\LoggerInterface $logger + * @param ScopeConfigInterface $scopeConfig + * @param RateErrorFactory $rateErrorFactory + * @param LoggerInterface $logger * @param Security $xmlSecurity - * @param \Magento\Shipping\Model\Simplexml\ElementFactory $xmlElFactory - * @param \Magento\Shipping\Model\Rate\ResultFactory $rateFactory - * @param \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory - * @param \Magento\Shipping\Model\Tracking\ResultFactory $trackFactory - * @param \Magento\Shipping\Model\Tracking\Result\ErrorFactory $trackErrorFactory - * @param \Magento\Shipping\Model\Tracking\Result\StatusFactory $trackStatusFactory - * @param \Magento\Directory\Model\RegionFactory $regionFactory - * @param \Magento\Directory\Model\CountryFactory $countryFactory - * @param \Magento\Directory\Model\CurrencyFactory $currencyFactory - * @param \Magento\Directory\Helper\Data $directoryData - * @param \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry - * @param \Magento\Framework\Locale\FormatInterface $localeFormat + * @param ElementFactory $xmlElFactory + * @param RateFactory $rateFactory + * @param RateMethodFactory $rateMethodFactory + * @param TrackFactory $trackFactory + * @param TrackErrorFactory $trackErrorFactory + * @param TrackStatusFactory $trackStatusFactory + * @param RegionFactory $regionFactory + * @param CountryFactory $countryFactory + * @param CurrencyFactory $currencyFactory + * @param Data $directoryData + * @param StockRegistryInterface $stockRegistry + * @param FormatInterface $localeFormat * @param Config $configHelper * @param ClientFactory $httpClientFactory * @param array $data @@ -175,27 +195,27 @@ class Carrier extends AbstractCarrierOnline implements CarrierInterface * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory, - \Psr\Log\LoggerInterface $logger, + ScopeConfigInterface $scopeConfig, + RateErrorFactory $rateErrorFactory, + LoggerInterface $logger, Security $xmlSecurity, - \Magento\Shipping\Model\Simplexml\ElementFactory $xmlElFactory, - \Magento\Shipping\Model\Rate\ResultFactory $rateFactory, - \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory, - \Magento\Shipping\Model\Tracking\ResultFactory $trackFactory, - \Magento\Shipping\Model\Tracking\Result\ErrorFactory $trackErrorFactory, - \Magento\Shipping\Model\Tracking\Result\StatusFactory $trackStatusFactory, - \Magento\Directory\Model\RegionFactory $regionFactory, - \Magento\Directory\Model\CountryFactory $countryFactory, - \Magento\Directory\Model\CurrencyFactory $currencyFactory, - \Magento\Directory\Helper\Data $directoryData, - \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry, - \Magento\Framework\Locale\FormatInterface $localeFormat, + ElementFactory $xmlElFactory, + RateFactory $rateFactory, + RateMethodFactory $rateMethodFactory, + TrackFactory $trackFactory, + TrackErrorFactory $trackErrorFactory, + TrackStatusFactory $trackStatusFactory, + RegionFactory $regionFactory, + CountryFactory $countryFactory, + CurrencyFactory $currencyFactory, + Data $directoryData, + StockRegistryInterface $stockRegistry, + FormatInterface $localeFormat, Config $configHelper, ClientFactory $httpClientFactory, array $data = [], ?AsyncClientInterface $asyncHttpClient = null, - ?ProxyDeferredFactory $proxyDeferredFactory + ?ProxyDeferredFactory $proxyDeferredFactory = null ) { parent::__construct( $scopeConfig, @@ -265,7 +285,7 @@ public function setRequest(RateRequest $request) { $this->_request = $request; - $rowRequest = new \Magento\Framework\DataObject(); + $rowRequest = new DataObject(); if ($request->getLimitMethod()) { $rowRequest->setAction($this->configHelper->getCode('action', 'single')); @@ -300,8 +320,8 @@ public function setRequest(RateRequest $request) $origCountry = $request->getOrigCountry(); } else { $origCountry = $this->_scopeConfig->getValue( - \Magento\Sales\Model\Order\Shipment::XML_PATH_STORE_COUNTRY_ID, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + OrderShipment::XML_PATH_STORE_COUNTRY_ID, + ScopeInterface::SCOPE_STORE, $request->getStoreId() ); } @@ -312,8 +332,8 @@ public function setRequest(RateRequest $request) $origRegionCode = $request->getOrigRegionCode(); } else { $origRegionCode = $this->_scopeConfig->getValue( - \Magento\Sales\Model\Order\Shipment::XML_PATH_STORE_REGION_ID, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + OrderShipment::XML_PATH_STORE_REGION_ID, + ScopeInterface::SCOPE_STORE, $request->getStoreId() ); } @@ -327,8 +347,8 @@ public function setRequest(RateRequest $request) } else { $rowRequest->setOrigPostal( $this->_scopeConfig->getValue( - \Magento\Sales\Model\Order\Shipment::XML_PATH_STORE_ZIP, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + OrderShipment::XML_PATH_STORE_ZIP, + ScopeInterface::SCOPE_STORE, $request->getStoreId() ) ); @@ -339,8 +359,8 @@ public function setRequest(RateRequest $request) } else { $rowRequest->setOrigCity( $this->_scopeConfig->getValue( - \Magento\Sales\Model\Order\Shipment::XML_PATH_STORE_CITY, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + OrderShipment::XML_PATH_STORE_CITY, + ScopeInterface::SCOPE_STORE, $request->getStoreId() ) ); @@ -516,7 +536,7 @@ protected function _getCgiQuotes() if (!$url) { $url = $this->_defaultCgiGatewayUrl; } - $client = new \Zend_Http_Client(); + $client = new Zend_Http_Client(); $client->setUri($url); $client->setConfig(['maxredirects' => 0, 'timeout' => 30]); $client->setParameterGet($params); @@ -525,7 +545,7 @@ protected function _getCgiQuotes() $debugData['result'] = $responseBody; $this->_setCachedQuotes($params, $responseBody); - } catch (\Throwable $e) { + } catch (Throwable $e) { $debugData['result'] = ['error' => $e->getMessage(), 'code' => $e->getCode()]; $responseBody = ''; } @@ -727,7 +747,7 @@ protected function _getXmlQuotes() <StateProvinceCode>{$shipperStateProvince}</StateProvinceCode> </Address> </Shipper> - + <ShipTo> <Address> <PostalCode>{$params['19_destPostal']}</PostalCode> @@ -743,7 +763,7 @@ protected function _getXmlQuotes() $xmlParams .= <<<XMLRequest </Address> </ShipTo> - + <ShipFrom> <Address> <PostalCode>{$params['15_origPostal']}</PostalCode> @@ -1056,7 +1076,7 @@ protected function setXMLAccessRequest() * Get cgi tracking * * @param string[] $trackings - * @return \Magento\Shipping\Model\Tracking\ResultFactory + * @return TrackFactory */ protected function _getCgiTracking($trackings) { @@ -1101,6 +1121,7 @@ protected function _getXmlTracking($trackings) $xmlRequest = <<<XMLAuth <?xml version="1.0" ?> <TrackRequest xml:lang="en-US"> + <IncludeMailInnovationIndicator/> <Request> <RequestAction>Track</RequestAction> <RequestOption>1</RequestOption> @@ -1320,13 +1341,13 @@ public function getAllowedMethods() /** * Form XML for shipment request * - * @param \Magento\Framework\DataObject $request + * @param DataObject $request * @return string * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - protected function _formShipmentRequest(\Magento\Framework\DataObject $request) + protected function _formShipmentRequest(DataObject $request) { $packageParams = $request->getPackageParams(); $height = $packageParams->getHeight(); @@ -1338,7 +1359,7 @@ protected function _formShipmentRequest(\Magento\Framework\DataObject $request) $itemsDesc = []; $itemsShipment = $request->getPackageItems(); foreach ($itemsShipment as $itemShipment) { - $item = new \Magento\Framework\DataObject(); + $item = new DataObject(); $item->setData($itemShipment); $itemsDesc[] = $item->getName(); } @@ -1532,7 +1553,7 @@ protected function _formShipmentRequest(\Magento\Framework\DataObject $request) * Send and process shipment accept request * * @param Element $shipmentConfirmResponse - * @return \Magento\Framework\DataObject + * @return DataObject * @deprecated New asynchronous methods introduced. * @see requestToShipment */ @@ -1558,18 +1579,18 @@ protected function _sendShipmentAcceptRequest(Element $shipmentConfirmResponse) $xmlResponse = $deferredResponse->get()->getBody(); $debugData['result'] = $xmlResponse; $this->_setCachedQuotes($xmlRequest, $xmlResponse); - } catch (\Throwable $e) { + } catch (Throwable $e) { $debugData['result'] = ['error' => $e->getMessage(), 'code' => $e->getCode()]; $xmlResponse = ''; } try { $response = $this->_xmlElFactory->create(['data' => $xmlResponse]); - } catch (\Throwable $e) { + } catch (Throwable $e) { $debugData['result'] = ['error' => $e->getMessage(), 'code' => $e->getCode()]; } - $result = new \Magento\Framework\DataObject(); + $result = new DataObject(); if (isset($response->Error)) { $result->setErrors((string)$response->Error->ErrorDescription); } else { @@ -1608,7 +1629,7 @@ public function getShipAcceptUrl() * @param DataObject[] $packages * @return string[] Quote IDs. * @throws LocalizedException - * @throws \RuntimeException + * @throws RuntimeException */ private function requestQuotes(array $packages): array { @@ -1639,13 +1660,13 @@ private function requestQuotes(array $packages): array try { /** @var Element $response */ $response = $this->_xmlElFactory->create(['data' => $httpResponse->getBody()]); - } catch (\Throwable $e) { - throw new \RuntimeException($e->getMessage()); + } catch (Throwable $e) { + throw new RuntimeException($e->getMessage()); } if (isset($response->Response->Error) && in_array($response->Response->Error->ErrorSeverity, ['Hard', 'Transient']) ) { - throw new \RuntimeException((string)$response->Response->Error->ErrorDescription); + throw new RuntimeException((string)$response->Response->Error->ErrorDescription); } $ids[] = $response->ShipmentDigest; @@ -1660,7 +1681,7 @@ private function requestQuotes(array $packages): array * @param string[] $quoteIds * @return DataObject[] * @throws LocalizedException - * @throws \RuntimeException + * @throws RuntimeException */ private function requestShipments(array $quoteIds): array { @@ -1696,11 +1717,11 @@ private function requestShipments(array $quoteIds): array try { /** @var Element $response */ $response = $this->_xmlElFactory->create(['data' => $httpResponse->getBody()]); - } catch (\Throwable $e) { - throw new \RuntimeException($e->getMessage()); + } catch (Throwable $e) { + throw new RuntimeException($e->getMessage()); } if (isset($response->Error)) { - throw new \RuntimeException((string)$response->Error->ErrorDescription); + throw new RuntimeException((string)$response->Error->ErrorDescription); } else { $shippingLabelContent = (string)$response->ShipmentResults->PackageResults->LabelImage->GraphicImage; $trackingNumber = (string)$response->ShipmentResults->PackageResults->TrackingNumber; @@ -1725,7 +1746,7 @@ private function requestShipments(array $quoteIds): array protected function _doShipmentRequest(DataObject $request) { $this->_prepareShipmentRequest($request); - $result = new \Magento\Framework\DataObject(); + $result = new DataObject(); $rawXmlRequest = $this->_formShipmentRequest($request); $this->setXMLAccessRequest(); $xmlRequest = $this->_xmlAccessRequest . $rawXmlRequest; @@ -1746,14 +1767,14 @@ protected function _doShipmentRequest(DataObject $request) $xmlResponse = $deferredResponse->get()->getBody(); $debugData['result'] = $xmlResponse; $this->_setCachedQuotes($xmlRequest, $xmlResponse); - } catch (\Throwable $e) { + } catch (Throwable $e) { $debugData['result'] = ['code' => $e->getCode(), 'error' => $e->getMessage()]; } } try { $response = $this->_xmlElFactory->create(['data' => $xmlResponse]); - } catch (\Throwable $e) { + } catch (Throwable $e) { $debugData['result'] = ['error' => $e->getMessage(), 'code' => $e->getCode()]; $result->setErrors($e->getMessage()); } @@ -1826,7 +1847,7 @@ public function requestToShipment($request) $labels = $this->requestShipments($quoteIds); } catch (LocalizedException $exception) { return new DataObject(['errors' => [$exception->getMessage()]]); - } catch (\RuntimeException $exception) { + } catch (RuntimeException $exception) { return new DataObject(['errors' => __('Failed to send items')]); } // phpcs:enable @@ -1847,11 +1868,11 @@ public function returnOfShipment($request) /** * Return container types of carrier * - * @param \Magento\Framework\DataObject|null $params + * @param DataObject|null $params * @return array|bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - public function getContainerTypes(\Magento\Framework\DataObject $params = null) + public function getContainerTypes(DataObject $params = null) { if ($params === null) { return $this->_getAllowedContainers($params); @@ -1931,10 +1952,10 @@ public function getContainerTypesFilter() /** * Return delivery confirmation types of carrier * - * @param \Magento\Framework\DataObject|null $params + * @param DataObject|null $params * @return array|bool */ - public function getDeliveryConfirmationTypes(\Magento\Framework\DataObject $params = null) + public function getDeliveryConfirmationTypes(DataObject $params = null) { $countryRecipient = $params != null ? $params->getCountryRecipient() : null; $deliveryConfirmationTypes = []; diff --git a/app/code/Magento/Ups/etc/adminhtml/system.xml b/app/code/Magento/Ups/etc/adminhtml/system.xml index c069eb48a59c7..c6a2516e96170 100644 --- a/app/code/Magento/Ups/etc/adminhtml/system.xml +++ b/app/code/Magento/Ups/etc/adminhtml/system.xml @@ -134,7 +134,7 @@ <field id="include_taxes" translate="label" type="select" sortOrder="45" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Request Tax-Inclusive Rate</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> - <comment>When applicable, taxes (sales tax, VAT etc.) are included in the rate</comment> + <comment>When applicable, taxes (sales tax, VAT etc.) are included in the rate.</comment> </field> <field id="shipper_number" translate="label comment" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Shipper Number</label> diff --git a/app/code/Magento/Ups/registration.php b/app/code/Magento/Ups/registration.php index 4c87e21b00ac7..c9d7f2b58c86f 100644 --- a/app/code/Magento/Ups/registration.php +++ b/app/code/Magento/Ups/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Ups', __DIR__); diff --git a/app/code/Magento/UrlRewrite/Controller/Router.php b/app/code/Magento/UrlRewrite/Controller/Router.php index 47718ba36316b..0525621b6a20e 100644 --- a/app/code/Magento/UrlRewrite/Controller/Router.php +++ b/app/code/Magento/UrlRewrite/Controller/Router.php @@ -5,15 +5,16 @@ */ namespace Magento\UrlRewrite\Controller; +use Magento\Framework\App\Action\Redirect; +use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\App\RequestInterface; +use Magento\Framework\App\Response\Http as HttpResponse; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\UrlInterface; use Magento\UrlRewrite\Controller\Adminhtml\Url\Rewrite; use Magento\UrlRewrite\Model\UrlFinderInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; -use Magento\Framework\App\Request\Http as HttpRequest; -use Magento\Framework\App\Response\Http as HttpResponse; -use Magento\Framework\UrlInterface; -use Magento\Framework\App\Action\Redirect; -use Magento\Framework\App\ActionInterface; /** * UrlRewrite Controller Router @@ -73,11 +74,12 @@ public function __construct( * * @param RequestInterface|HttpRequest $request * @return ActionInterface|null + * @throws NoSuchEntityException */ public function match(RequestInterface $request) { $rewrite = $this->getRewrite( - $this->getNormalizedPathInfo($request), + $request->getPathInfo(), $this->storeManager->getStore()->getId() ); @@ -116,7 +118,7 @@ protected function processRedirect($request, $rewrite) if ($rewrite->getEntityType() !== Rewrite::ENTITY_TYPE_CUSTOM || ($prefix = substr($target, 0, 6)) !== 'http:/' && $prefix !== 'https:' ) { - $target = $this->url->getUrl('', ['_direct' => $target]); + $target = $this->url->getUrl('', ['_direct' => $target, '_query' => $request->getParams()]); } return $this->redirect($request, $target, $rewrite->getRedirectType()); } @@ -153,30 +155,4 @@ protected function getRewrite($requestPath, $storeId) ] ); } - - /** - * Get normalized request path - * - * @param RequestInterface|HttpRequest $request - * @return string - */ - private function getNormalizedPathInfo(RequestInterface $request): string - { - $path = $request->getPathInfo(); - /** - * If request contains query params then we need to trim a slash in end of the path. - * For example: - * the original request is: http://my-host.com/category-url-key.html/?color=black - * where the original path is: category-url-key.html/ - * and the result path will be: category-url-key.html - * - * It need to except a redirect like this: - * http://my-host.com/category-url-key.html/?color=black => http://my-host.com/category-url-key.html - */ - if (!empty($path) && $request->getQuery()->count()) { - $path = rtrim($path, '/'); - } - - return (string)$path; - } } diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddCustomUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddCustomUrlRewriteActionGroup.xml new file mode 100644 index 0000000000000..d4bcb5bbb414f --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddCustomUrlRewriteActionGroup.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAddCustomUrlRewriteActionGroup"> + <annotations> + <description>Goes to the Admin URL Rewrite edit page. Adds the provided Custom URL Rewrite details. Clicks on Save. Validates that the Success Message is present.</description> + </annotations> + <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> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteActionGroup.xml new file mode 100644 index 0000000000000..55f18ae5a0187 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteActionGroup.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAddUrlRewriteActionGroup"> + <annotations> + <description>Goes to the Admin Add URL Rewrite edit page. Fills in the provided URL details. Clicks on Save. Validates that the Success Message is present.</description> + </annotations> + <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"/> + <waitForElementVisible selector="{{AdminUrlRewriteEditSection.createCustomUrlRewrite}}" stepKey="waitForCreateUrlRewriteVisible"/> + <selectOption selector="{{AdminUrlRewriteEditSection.createCustomUrlRewrite}}" userInput="{{customUrlRewriteValue}}" stepKey="selectUrlRewriteTypeOption"/> + <waitForElementVisible selector="{{AdminUrlRewriteEditSection.categoryInTree(category)}}" stepKey="waitForCategoryInTreeVisible"/> + <click selector="{{AdminUrlRewriteEditSection.categoryInTree(category)}}" stepKey="clickOnCategoryInTree"/> + <waitForElementVisible selector="{{AdminUrlRewriteEditSection.store}}" stepKey="waitForStoreSelectVisible"/> + <selectOption selector="{{AdminUrlRewriteEditSection.store}}" userInput="{{storeValue}}" stepKey="selectStoreOption"/> + <fillField selector="{{AdminUrlRewriteEditSection.requestPath}}" userInput="{{requestPath}}" stepKey="fillRequestPathField"/> + <selectOption selector="{{AdminUrlRewriteEditSection.redirectType}}" userInput="{{redirectTypeValue}}" stepKey="selectRedirectType"/> + <fillField selector="{{AdminUrlRewriteEditSection.description}}" userInput="{{description}}" stepKey="fillDescriptionField"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveButton"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The URL Rewrite has been saved." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteForProductActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteForProductActionGroup.xml new file mode 100644 index 0000000000000..51436fc5de584 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminAddUrlRewriteForProductActionGroup.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAddUrlRewriteForProductActionGroup"> + <annotations> + <description>Adds the provided URL Rewrite details for a Product.</description> + </annotations> + <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> +</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminCreateNewUrlRewriteForCmsPageActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminCreateNewUrlRewriteForCmsPageActionGroup.xml new file mode 100644 index 0000000000000..143198e4a1faa --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminCreateNewUrlRewriteForCmsPageActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateNewUrlRewriteForCmsPageActionGroup"> + <annotations> + <description>Select "For Csm Page" URL Rewrite type</description> + </annotations> + <arguments> + <argument name="customUrlRewriteValue" type="string"/> + </arguments> + + <click selector="{{AdminUrlRewriteEditSection.createCustomUrlRewrite}}" stepKey="clickOnCustomUrlRewrite"/> + <click selector="{{AdminUrlRewriteEditSection.createCustomUrlRewriteValue('customUrlRewriteValue')}}" stepKey="selectForCsmPage"/> + <waitForPageLoad stepKey="waitForCategoryEditSectionToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminDeleteUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminDeleteUrlRewriteActionGroup.xml new file mode 100644 index 0000000000000..bc3953c4dedd4 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminDeleteUrlRewriteActionGroup.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminDeleteUrlRewriteActionGroup"> + <annotations> + <description>Goes to the Admin URL Rewrite grid page. Deletes the provided Request Path. Validates that the Success Message is present and correct.</description> + </annotations> + <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> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminFillNewCmsPageUrlRewriteFormActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminFillNewCmsPageUrlRewriteFormActionGroup.xml new file mode 100644 index 0000000000000..0c540d0698dd8 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminFillNewCmsPageUrlRewriteFormActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminFillNewCmsPageUrlRewriteFormActionGroup"> + <annotations> + <description>Fills in the provided URL details. Clicks on Save.</description> + </annotations> + <arguments> + <argument name="storeValue" type="string"/> + <argument name="requestPath" type="string"/> + <argument name="redirectTypeValue" type="string"/> + <argument name="description" type="string"/> + </arguments> + + <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"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminGoToAddNewUrlRewritePageActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminGoToAddNewUrlRewritePageActionGroup.xml new file mode 100644 index 0000000000000..e0e8df47852d6 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminGoToAddNewUrlRewritePageActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminGoToAddNewUrlRewritePageActionGroup"> + <annotations> + <description>Goes to the Admin Add URL Rewrite edit page</description> + </annotations> + + <amOnPage url="{{AdminUrlRewriteEditPage.url}}" stepKey="openUrlRewriteEditPage"/> + <waitForPageLoad stepKey="waitForUrlRewriteEditPageToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchAndSelectUrlRewriteInGridActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchAndSelectUrlRewriteInGridActionGroup.xml new file mode 100644 index 0000000000000..f2f114f01cc9e --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchAndSelectUrlRewriteInGridActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSearchAndSelectUrlRewriteInGridActionGroup"> + <annotations> + <description>Goes to the Admin URL Rewrite grid page. Searches the grid for the provided Request Path. Clicks on Edit.</description> + </annotations> + <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"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchByRequestPathActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchByRequestPathActionGroup.xml new file mode 100644 index 0000000000000..99ac4ea160f82 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchByRequestPathActionGroup.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="AdminSearchByRequestPathActionGroup"> + <annotations> + <description>Goes to the Admin URL Rewrite grid page. Searches the grid based on the provided Redirect Path. Validates that the provided Redirect Path, Type and Target Path are present and correct in the grid.</description> + </annotations> + <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> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchDeletedUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchDeletedUrlRewriteActionGroup.xml new file mode 100644 index 0000000000000..2d7178f72f407 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchDeletedUrlRewriteActionGroup.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="AdminSearchDeletedUrlRewriteActionGroup"> + <annotations> + <description>Goes to the Admin URL Rewrite grid page. Searches the grid for the provided Request Path. Validates that it does NOT appear in the grid.</description> + </annotations> + <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> +</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchUrlRewriteProductBySkuActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchUrlRewriteProductBySkuActionGroup.xml new file mode 100644 index 0000000000000..d77621ccd768f --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchUrlRewriteProductBySkuActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSearchUrlRewriteProductBySkuActionGroup"> + <annotations> + <description>Goes to the Admin URL Rewrite grid page. Searches the grid based on the provided Product SKU. Clicks on the 1st row in the grid.</description> + </annotations> + <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> +</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSelectCmsPageFromGridForNewUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSelectCmsPageFromGridForNewUrlRewriteActionGroup.xml new file mode 100644 index 0000000000000..2713e3937469b --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSelectCmsPageFromGridForNewUrlRewriteActionGroup.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="AdminSelectCmsPageFromGridForNewUrlRewriteActionGroup"> + <annotations> + <description>Select Cms Page for URL Rewrite from the grid</description> + </annotations> + <arguments> + <argument name="cmsPageUrlKey" type="string"/> + </arguments> + + <click selector="{{AdminUrlRewriteEditSection.cmsPage('cmsPageUrlKey')}}" stepKey="selectCmsPage"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateCustomUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateCustomUrlRewriteActionGroup.xml new file mode 100644 index 0000000000000..0d0c754fe076e --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateCustomUrlRewriteActionGroup.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="AdminUpdateCustomUrlRewriteActionGroup"> + <annotations> + <description>Updates the Custom URL Rewrite fields with the provided details.</description> + </annotations> + <arguments> + <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> + + <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"/> + <selectOption selector="{{AdminUrlRewriteEditSection.redirectType}}" userInput="{{redirectTypeValue}}" stepKey="selectRedirectTypeValue"/> + <fillField selector="{{AdminUrlRewriteEditSection.description}}" userInput="{{description}}" stepKey="fillDescription"/> + <click selector="{{AdminUrlRewriteEditSection.saveButton}}" stepKey="clickOnSaveButton"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.successMessage}}" stepKey="seeSuccessSaveMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateUrlRewriteActionGroup.xml new file mode 100644 index 0000000000000..4b884448a7653 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUpdateUrlRewriteActionGroup.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminUpdateUrlRewriteActionGroup"> + <annotations> + <description>Updates the URL Rewrite fields with the provided details.</description> + </annotations> + <arguments> + <argument name="storeValue" type="string"/> + <argument name="requestPath" type="string"/> + <argument name="redirectTypeValue" type="string"/> + <argument name="description" type="string"/> + </arguments> + + <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="selectRedirectTypeValue"/> + <fillField selector="{{AdminUrlRewriteEditSection.description}}" userInput="{{description}}" stepKey="fillDescription"/> + <click selector="{{AdminUrlRewriteEditSection.saveButton}}" stepKey="clickOnSaveButton"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.successMessage}}" stepKey="seeSuccessSaveMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteActionGroup.xml deleted file mode 100644 index d3e9509f1ef00..0000000000000 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteActionGroup.xml +++ /dev/null @@ -1,135 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminAddUrlRewrite"> - <annotations> - <description>Goes to the Admin Add URL Rewrite edit page. Fills in the provided URL details. Clicks on Save. Validates that the Success Message is present.</description> - </annotations> - <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"> - <annotations> - <description>Adds the provided URL Rewrite details for a Product.</description> - </annotations> - <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"> - <annotations> - <description>Goes to the Admin URL Rewrite edit page. Adds the provided Custom URL Rewrite details. Clicks on Save. Validates that the Success Message is present.</description> - </annotations> - <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> - - <actionGroup name="AdminUpdateUrlRewrite"> - <annotations> - <description>Updates the URL Rewrite fields with the provided details.</description> - </annotations> - <arguments> - <argument name="storeValue" type="string"/> - <argument name="requestPath" type="string"/> - <argument name="redirectTypeValue" type="string"/> - <argument name="description" type="string"/> - </arguments> - - <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="selectRedirectTypeValue"/> - <fillField selector="{{AdminUrlRewriteEditSection.description}}" userInput="{{description}}" stepKey="fillDescription"/> - <click selector="{{AdminUrlRewriteEditSection.saveButton}}" stepKey="clickOnSaveButton"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.successMessage}}" stepKey="seeSuccessSaveMessage"/> - </actionGroup> - - <actionGroup name="AdminUpdateCustomUrlRewrite"> - <annotations> - <description>Updates the Custom URL Rewrite fields with the provided details.</description> - </annotations> - <arguments> - <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> - - <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"/> - <selectOption selector="{{AdminUrlRewriteEditSection.redirectType}}" userInput="{{redirectTypeValue}}" stepKey="selectRedirectTypeValue"/> - <fillField selector="{{AdminUrlRewriteEditSection.description}}" userInput="{{description}}" stepKey="fillDescription"/> - <click selector="{{AdminUrlRewriteEditSection.saveButton}}" stepKey="clickOnSaveButton"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.successMessage}}" stepKey="seeSuccessSaveMessage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteGridActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteGridActionGroup.xml deleted file mode 100644 index 2a3f4446e2b74..0000000000000 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteGridActionGroup.xml +++ /dev/null @@ -1,126 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminSearchByRequestPath"> - <annotations> - <description>Goes to the Admin URL Rewrite grid page. Searches the grid based on the provided Redirect Path. Validates that the provided Redirect Path, Type and Target Path are present and correct in the grid.</description> - </annotations> - <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="AdminSearchUrlRewriteProductBySku"> - <annotations> - <description>Goes to the Admin URL Rewrite grid page. Searches the grid based on the provided Product SKU. Clicks on the 1st row in the grid.</description> - </annotations> - <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"> - <annotations> - <description>Goes to the Admin URL Rewrite grid page. Searches the grid for the provided Request Path. Validates that it does NOT appear in the grid.</description> - </annotations> - <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"> - <annotations> - <description>Goes to the Admin URL Rewrite grid page. Deletes the provided Request Path. Validates that the Success Message is present and correct.</description> - </annotations> - <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> - - <actionGroup name="AssertPageByUrlRewriteIsNotFound"> - <annotations> - <description>Validates that the provided Request Path does NOT exist on the Storefront. Validates that the 'Whoops' message is present and correct.</description> - </annotations> - <arguments> - <argument name="requestPath" type="string"/> - </arguments> - - <amOnPage url="{{requestPath}}" stepKey="amOnPage"/> - <waitForPageLoad stepKey="waitForStorefrontPageLoad"/> - <see userInput="Whoops, our bad..." stepKey="seeWhoops"/> - </actionGroup> - - <actionGroup name="AdminSearchAndSelectUrlRewriteInGrid"> - <annotations> - <description>Goes to the Admin URL Rewrite grid page. Searches the grid for the provided Request Path. Clicks on Edit.</description> - </annotations> - <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"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AssertPageByUrlRewriteIsNotFoundActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AssertPageByUrlRewriteIsNotFoundActionGroup.xml new file mode 100644 index 0000000000000..4188c86a58b81 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AssertPageByUrlRewriteIsNotFoundActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertPageByUrlRewriteIsNotFoundActionGroup"> + <annotations> + <description>Validates that the provided Request Path does NOT exist on the Storefront. Validates that the 'Whoops' message is present and correct.</description> + </annotations> + <arguments> + <argument name="requestPath" type="string"/> + </arguments> + + <amOnPage url="{{requestPath}}" stepKey="amOnPage"/> + <waitForPageLoad stepKey="waitForStorefrontPageLoad"/> + <see userInput="Whoops, our bad..." stepKey="seeWhoops"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AssertStorefrontProductRedirectActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AssertStorefrontProductRedirectActionGroup.xml new file mode 100644 index 0000000000000..8a41dc4e2a9b3 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AssertStorefrontProductRedirectActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontProductRedirectActionGroup"> + <annotations> + <description>Goes to the provided New Product URL. Validates that the redirect works and the provided Product is present and correct.</description> + </annotations> + <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> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AssertStorefrontUrlRewriteRedirectActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AssertStorefrontUrlRewriteRedirectActionGroup.xml new file mode 100644 index 0000000000000..4d95c1a43876b --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AssertStorefrontUrlRewriteRedirectActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontUrlRewriteRedirectActionGroup"> + <annotations> + <description>Goes to the provided New URL. Validates that the redirect works and the provided Category is present.</description> + </annotations> + <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> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/StorefrontUrlRewriteRedirectActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/StorefrontUrlRewriteRedirectActionGroup.xml deleted file mode 100644 index 325045bccc869..0000000000000 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/StorefrontUrlRewriteRedirectActionGroup.xml +++ /dev/null @@ -1,41 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertStorefrontUrlRewriteRedirect"> - <annotations> - <description>Goes to the provided New URL. Validates that the redirect works and the provided Category is present.</description> - </annotations> - <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="AssertStorefrontProductRedirect"> - <annotations> - <description>Goes to the provided New Product URL. Validates that the redirect works and the provided Product is present and correct.</description> - </annotations> - <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> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml index 52939607f5377..5a55562e99334 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml @@ -19,6 +19,7 @@ <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(., '{{categoryName}}')]" parameterized="true"/> + <element name="cmsPage" selector="//td[contains(text(), '{{cmsPageUrlKey}}')]" type="button" 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/Test/AdminAutoUpdateURLRewriteWhenCategoryIsDeletedTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminAutoUpdateURLRewriteWhenCategoryIsDeletedTest.xml index 52d313b21f3e1..022d28ed692c1 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminAutoUpdateURLRewriteWhenCategoryIsDeletedTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminAutoUpdateURLRewriteWhenCategoryIsDeletedTest.xml @@ -30,12 +30,12 @@ </after> <!--Filter and Select the created Product --> - <actionGroup ref="AdminSearchUrlRewriteProductBySku" stepKey="searchProduct"> + <actionGroup ref="AdminSearchUrlRewriteProductBySkuActionGroup" stepKey="searchProduct"> <argument name="productSku" value="$$createSimpleProduct.sku$$"/> </actionGroup> <!-- Update the Store, RequestPath, RedirectType and Description --> - <actionGroup ref="AdminAddUrlRewriteForProduct" stepKey="addUrlRewriteForProduct"> + <actionGroup ref="AdminAddUrlRewriteForProductActionGroup" stepKey="addUrlRewriteForProduct"> <argument name="storeValue" value="Default Store View"/> <argument name="requestPath" value="{{_defaultProduct.urlKey}}.html"/> <argument name="redirectTypeValue" value="Temporary (302)"/> @@ -46,28 +46,28 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!--Filter Product in product page and get the Product ID --> - <actionGroup ref="filterAndSelectProduct" stepKey="filterProduct"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="filterProduct"> <argument name="productSku" value="$$createSimpleProduct.sku$$"/> </actionGroup> <grabFromCurrentUrl stepKey="productId" regex="#\/([0-9]*)?\/$#"/> <!-- Assert Redirect path, Target Path and Redirect type in grid --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath"> <argument name="redirectPath" value="$$createSimpleProduct.name$$.html" /> <argument name="redirectType" value="No" /> <argument name="targetPath" value="catalog/product/view/id/{$productId}"/> </actionGroup> <!-- Assert Redirect path, Target Path and Redirect type in grid --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath1"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath1"> <argument name="redirectPath" value="{{_defaultProduct.urlKey}}.html" /> <argument name="redirectType" value="Temporary (302)"/> <argument name="targetPath" value="$$createSimpleProduct.name$$.html"/> </actionGroup> <!--Assert Category Url Redirect is not present --> - <actionGroup ref="AdminSearchDeletedUrlRewrite" stepKey="searchDeletedCategory"> + <actionGroup ref="AdminSearchDeletedUrlRewriteActionGroup" stepKey="searchDeletedCategory"> <argument name="requestPath" value="$$createCategory.name$$.html"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml index 44fad061d7656..77fb2b5285ac3 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml @@ -18,7 +18,16 @@ <group value="urlRewrite"/> </annotations> <before> + <createData entity="ApiCategory" stepKey="createCategory"> + <field key="name">category-admin</field> + </createData> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminDeleteStoreViewIfExistsActionGroup" stepKey="deleteENStoreViewIfExists"> + <argument name="storeViewName" value="{{customStoreENNotUnique.name}}"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewIfExistsActionGroup" stepKey="deleteNLStoreViewIfExists"> + <argument name="storeViewName" value="{{customStoreNLNotUnique.name}}"/> + </actionGroup> <!-- Create Store View EN --> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewEn"> <argument name="customStore" value="customStoreENNotUnique"/> @@ -27,43 +36,41 @@ <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewNl"> <argument name="customStore" value="customStoreNLNotUnique"/> </actionGroup> - <createData entity="ApiCategory" stepKey="createCategory"> - <field key="name">category-admin</field> - </createData> </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="deleteProductByName" stepKey="deleteImportedProduct"> - <argument name="sku" value="productformagetwo68980"/> - <argument name="name" value="productformagetwo68980"/> + <actionGroup ref="AdminDeleteStoreViewIfExistsActionGroup" stepKey="deleteStoreViewEn"> + <argument name="storeViewName" value="{{customStoreENNotUnique.name}}"/> </actionGroup> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFiltersIfSet"/> - <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewEn"> - <argument name="customStore" value="customStoreENNotUnique"/> + <actionGroup ref="AdminDeleteStoreViewIfExistsActionGroup" stepKey="deleteStoreViewNl"> + <argument name="storeViewName" value="{{customStoreNLNotUnique.name}}"/> </actionGroup> - <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewNl"> - <argument name="customStore" value="customStoreNLNotUnique"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearStoreFilters"/> + <actionGroup ref="DeleteProductByNameActionGroup" stepKey="deleteImportedProduct"> + <argument name="sku" value="productformagetwo68980"/> + <argument name="name" value="productformagetwo68980"/> </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFiltersIfSet"/> <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="switchCategoryStoreView" stepKey="switchToStoreViewEn"> + <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchToStoreViewEn"> <argument name="Store" value="customStoreENNotUnique.name"/> <argument name="CatName" value="$$createCategory.name$$"/> </actionGroup> <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValueENStoreView"/> <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-english" stepKey="changeNameField"/> <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader"/> - <actionGroup ref="ChangeSeoUrlKeyForSubCategory" stepKey="changeSeoUrlKeyENStoreView"> + <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeSeoUrlKeyENStoreView"> <argument name="value" value="category-english"/> </actionGroup> - <actionGroup ref="switchCategoryStoreView" stepKey="switchToStoreViewNl"> + <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchToStoreViewNl"> <argument name="Store" value="customStoreNLNotUnique.name"/> <argument name="CatName" value="$$createCategory.name$$"/> </actionGroup> <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValue1"/> <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-dutch" stepKey="changeNameFieldNLStoreView"/> <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader2"/> - <actionGroup ref="ChangeSeoUrlKeyForSubCategory" stepKey="changeSeoUrlKeyNLStoreView"> + <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeSeoUrlKeyNLStoreView"> <argument name="value" value="category-dutch"/> </actionGroup> <amOnPage url="{{AdminImportIndexPage.url}}" stepKey="navigateToSystemImport"/> @@ -72,11 +79,11 @@ <selectOption selector="{{AdminImportMainSection.importBehavior}}" userInput="Add/Update" stepKey="selectAddUpdateOption"/> <attachFile selector="{{AdminImportMainSection.selectFileToImport}}" userInput="import_updated.csv" stepKey="attachFileForImport"/> <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> - <see selector="{{AdminMessagesSection.notice}}" userInput="Checked rows: 3, checked entities: 1, invalid rows: 0, total errors: 0" stepKey="assertNotice"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="File is valid! To start import process press "Import" button" stepKey="assertSuccessMessage"/> + <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="Checked rows: 3, checked entities: 1, invalid rows: 0, total errors: 0" stepKey="assertNotice"/> + <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="File is valid! To start import process press "Import" button" stepKey="assertSuccessMessage"/> <click selector="{{AdminImportMainSection.importButton}}" stepKey="clickImportButton"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="Import successfully done" stepKey="assertSuccessMessage1"/> - <see selector="{{AdminMessagesSection.notice}}" userInput="Created: 1, Updated: 0, Deleted: 0" stepKey="assertNotice1"/> + <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="Import successfully done" stepKey="assertSuccessMessage1"/> + <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="Created: 1, Updated: 0, Deleted: 0" stepKey="assertNotice1"/> <actionGroup ref="SearchForProductOnBackendByNameActionGroup" stepKey="searchForProductOnBackend"> <argument name="productName" value="productformagetwo68980"/> </actionGroup> @@ -130,8 +137,16 @@ <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> <!--Flush cache--> <magentoCLI command="cache:flush" stepKey="cleanCache1"/> - + <createData entity="ApiCategory" stepKey="createCategory"> + <field key="name">category-admin</field> + </createData> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminDeleteStoreViewIfExistsActionGroup" stepKey="deleteENStoreViewIfExists"> + <argument name="storeViewName" value="{{customStoreENNotUnique.name}}"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewIfExistsActionGroup" stepKey="deleteNLStoreViewIfExists"> + <argument name="storeViewName" value="{{customStoreNLNotUnique.name}}"/> + </actionGroup> <!-- Create Store View EN --> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewEn"> <argument name="customStore" value="customStoreENNotUnique"/> @@ -140,10 +155,6 @@ <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewNl"> <argument name="customStore" value="customStoreNLNotUnique"/> </actionGroup> - <createData entity="ApiCategory" stepKey="createCategory"> - <field key="name">category-admin</field> - </createData> - <!-- Set the configuration for Generate "category/product" URL Rewrites to No--> <comment userInput="Disable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentDisableUrlRewriteConfig" /> <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 0" stepKey="disableGenerateUrlRewrite"/> @@ -152,40 +163,41 @@ </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="deleteProductByName" stepKey="deleteImportedProduct"> - <argument name="sku" value="productformagetwo68980"/> - <argument name="name" value="productformagetwo68980"/> + <actionGroup ref="AdminDeleteStoreViewIfExistsActionGroup" stepKey="deleteStoreViewEn"> + <argument name="storeViewName" value="{{customStoreENNotUnique.name}}"/> </actionGroup> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFiltersIfSet"/> - <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewEn"> - <argument name="customStore" value="customStoreENNotUnique"/> + <actionGroup ref="AdminDeleteStoreViewIfExistsActionGroup" stepKey="deleteStoreViewNl"> + <argument name="storeViewName" value="{{customStoreNLNotUnique.name}}"/> </actionGroup> - <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewNl"> - <argument name="customStore" value="customStoreNLNotUnique"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearStoreGridFilters"/> + <actionGroup ref="DeleteProductByNameActionGroup" stepKey="deleteImportedProduct"> + <argument name="sku" value="productformagetwo68980"/> + <argument name="name" value="productformagetwo68980"/> </actionGroup> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFiltersIfSet"/> <actionGroup ref="logout" stepKey="logout"/> <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="resetConfigurationSetting"/> <!--Flush cache--> <magentoCLI command="cache:flush" stepKey="cleanCache2"/> </after> - <actionGroup ref="switchCategoryStoreView" stepKey="switchToStoreViewEn"> + <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchToStoreViewEn"> <argument name="Store" value="customStoreENNotUnique.name"/> <argument name="CatName" value="$$createCategory.name$$"/> </actionGroup> <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValueENStoreView"/> <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-english" stepKey="changeNameField"/> <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader"/> - <actionGroup ref="ChangeSeoUrlKeyForSubCategory" stepKey="changeSeoUrlKeyENStoreView"> + <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeSeoUrlKeyENStoreView"> <argument name="value" value="category-english"/> </actionGroup> - <actionGroup ref="switchCategoryStoreView" stepKey="switchToStoreViewNl"> + <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchToStoreViewNl"> <argument name="Store" value="customStoreNLNotUnique.name"/> <argument name="CatName" value="$$createCategory.name$$"/> </actionGroup> <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValue1"/> <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-dutch" stepKey="changeNameFieldNLStoreView"/> <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader2"/> - <actionGroup ref="ChangeSeoUrlKeyForSubCategory" stepKey="changeSeoUrlKeyNLStoreView"> + <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeSeoUrlKeyNLStoreView"> <argument name="value" value="category-dutch"/> </actionGroup> <amOnPage url="{{AdminImportIndexPage.url}}" stepKey="navigateToSystemImport"/> @@ -194,11 +206,11 @@ <selectOption selector="{{AdminImportMainSection.importBehavior}}" userInput="Add/Update" stepKey="selectAddUpdateOption"/> <attachFile selector="{{AdminImportMainSection.selectFileToImport}}" userInput="import_updated.csv" stepKey="attachFileForImport"/> <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> - <see selector="{{AdminMessagesSection.notice}}" userInput="Checked rows: 3, checked entities: 1, invalid rows: 0, total errors: 0" stepKey="assertNotice"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="File is valid! To start import process press "Import" button" stepKey="assertSuccessMessage"/> + <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="Checked rows: 3, checked entities: 1, invalid rows: 0, total errors: 0" stepKey="assertNotice"/> + <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="File is valid! To start import process press "Import" button" stepKey="assertSuccessMessage"/> <click selector="{{AdminImportMainSection.importButton}}" stepKey="clickImportButton"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="Import successfully done" stepKey="assertSuccessMessage1"/> - <see selector="{{AdminMessagesSection.notice}}" userInput="Created: 1, Updated: 0, Deleted: 0" stepKey="assertNotice1"/> + <see selector="{{AdminImportValidationMessagesSection.success}}" userInput="Import successfully done" stepKey="assertSuccessMessage1"/> + <see selector="{{AdminImportValidationMessagesSection.notice}}" userInput="Created: 1, Updated: 0, Deleted: 0" stepKey="assertNotice1"/> <actionGroup ref="SearchForProductOnBackendByNameActionGroup" stepKey="searchForProductOnBackend"> <argument name="productName" value="productformagetwo68980"/> </actionGroup> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml index 52dce4d67f698..badda06b827ea 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml @@ -34,6 +34,10 @@ <!--Create additional Store View in Main Website Store --> <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"/> + + <!-- Reindex and flush the cache --> + <magentoCLI command="indexer:reindex" stepKey="runReindex"/> + <magentoCLI command="cache:flush" stepKey="cleanCache"/> </before> <after> <deleteData createDataKey="createFirstCategory" stepKey="deleteFirstCategory"/> @@ -45,18 +49,18 @@ </after> <!-- On the categories editing page change store view to created additional view --> - <actionGroup ref="switchCategoryStoreView" stepKey="switchStoreView"> + <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchStoreView"> <argument name="Store" value="customStore.name"/> <argument name="CatName" value="$$createFirstCategory.name$$"/> </actionGroup> <!-- Change url key for category for first category; save --> - <actionGroup ref="ChangeSeoUrlKeyForSubCategory" stepKey="changeUrlKey"> + <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeUrlKey"> <argument name="value" value="{{SimpleRootSubCategory.url_key}}"/> </actionGroup> <!-- Change store view to "All store views" for first category --> - <actionGroup ref="switchCategoryToAllStoreView" stepKey="switchToAllStoreViewProduct"> + <actionGroup ref="SwitchCategoryToAllStoreViewActionGroup" stepKey="switchToAllStoreViewProduct"> <argument name="CatName" value="$$createFirstCategory.name$$"/> </actionGroup> @@ -76,7 +80,6 @@ <!-- Assert category url with custom store view --> <amOnPage url="{{StorefrontHomePage.url}}$$createSecondCategory.name$$/{{SimpleRootSubCategory.url_key}}.html" stepKey="amOnCategoryPage"/> <waitForPageLoad stepKey="waitForCategoryPageLoad"/> - <dontSee userInput="$$createSecondSimpleProduct.name$$" selector="{{StorefrontCategoryMainSection.productsList}}" stepKey="dontSeeProductInCategory"/> - <see selector="{{StorefrontCategoryMainSection.emptyProductMessage}}" userInput="We can't find products matching the selection." stepKey="seeEmptyProductMessage"/> + <see userInput="$$createFirstSimpleProduct.name$$" selector="{{StorefrontCategoryMainSection.productsList}}" stepKey="seeProductInCategory"/> </test> </tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml index a7a7c0c73d826..732fc22aaf84a 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml @@ -27,9 +27,9 @@ </after> <!--Open Url Rewrite Index Page and update the Custom Url Rewrite, Store, Request Path, Redirect Type and Description --> - <actionGroup ref="AdminAddUrlRewrite" stepKey="addUrlRewrite"> + <actionGroup ref="AdminAddUrlRewriteActionGroup" stepKey="addUrlRewrite"> <argument name="category" value="$$category.name$$"/> - <argument name="customUrlRewriteValue" value="For Category'"/> + <argument name="customUrlRewriteValue" value="For Category"/> <argument name="storeValue" value="Default Store View"/> <argument name="requestPath" value="newrequestpath.html"/> <argument name="redirectTypeValue" value="No"/> @@ -37,16 +37,16 @@ </actionGroup> <!-- Get Category ID --> - <actionGroup ref="OpenCategoryFromCategoryTree" stepKey="getCategoryId"> + <actionGroup ref="OpenCategoryFromCategoryTreeActionGroup" 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"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" 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 +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml index 974550bb92214..867b3ee54161c 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml @@ -27,9 +27,9 @@ </after> <!--Open Url Rewrite Index Page and update the Custom Url Rewrite, Store, Request Path, Redirect Type and Description --> - <actionGroup ref="AdminAddUrlRewrite" stepKey="addUrlRewrite"> + <actionGroup ref="AdminAddUrlRewriteActionGroup" stepKey="addUrlRewrite"> <argument name="category" value="$$category.name$$"/> - <argument name="customUrlRewriteValue" value="For Category'"/> + <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)"/> @@ -37,16 +37,16 @@ </actionGroup> <!-- Assert Redirect path, Target Path and Redirect type in grid --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" 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="AssertStorefrontUrlRewriteRedirect" stepKey="openStorefrontUrlRedirectPath"> + <actionGroup ref="AssertStorefrontUrlRewriteRedirectActionGroup" stepKey="openStorefrontUrlRedirectPath"> <argument name="category" value="$$category.name$$"/> <argument name="newRequestPath" value="newrequestpath.html"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml index c64019ea38acc..ab18add56aeb9 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml @@ -27,9 +27,9 @@ </after> <!--Open Url Rewrite Index Page and update the Custom Url Rewrite, Store, Request Path, Redirect Type and Description --> - <actionGroup ref="AdminAddUrlRewrite" stepKey="addUrlRewrite"> + <actionGroup ref="AdminAddUrlRewriteActionGroup" stepKey="addUrlRewrite"> <argument name="category" value="$$category.name$$"/> - <argument name="customUrlRewriteValue" value="For Category'"/> + <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)"/> @@ -37,16 +37,16 @@ </actionGroup> <!-- Assert Redirect path, Target Path and Redirect type in grid --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" 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="AssertStorefrontUrlRewriteRedirect" stepKey="openStorefrontUrlRedirectPath"> + <actionGroup ref="AssertStorefrontUrlRewriteRedirectActionGroup" stepKey="openStorefrontUrlRedirectPath"> <argument name="category" value="$$category.name$$"/> <argument name="newRequestPath" value="newrequestpath.html"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml index 358aa58aba0f7..bd4f7d7a32165 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml @@ -23,20 +23,20 @@ </before> <after> <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> - <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteCustomUrlRewrite"> + <actionGroup ref="AdminDeleteUrlRewriteActionGroup" 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"> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage"> <argument name="CMSPage" value="$$createCMSPage$$"/> </actionGroup> <grabFromCurrentUrl stepKey="cmsId" regex="#\/([0-9]*)?\/$#"/> <!-- Open UrlRewrite Edit page and update the fields and fill the created CMS Page Target Path --> - <actionGroup ref="AdminAddCustomUrlRewrite" stepKey="addCustomUrlRewrite"> + <actionGroup ref="AdminAddCustomUrlRewriteActionGroup" stepKey="addCustomUrlRewrite"> <argument name="customUrlRewriteValue" value="Custom"/> <argument name="storeValue" value="Default Store View"/> <argument name="requestPath" value="{{defaultCmsPage.title}}"/> @@ -46,38 +46,38 @@ </actionGroup> <!-- Assert updated CMS page Url Rewrite in Grid --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath"> <argument name="redirectPath" value="{{defaultCmsPage.title}}" /> <argument name="redirectType" value="Permanent (301)" /> <argument name="targetPath" value="cms/page/view/page_id/{$cmsId}"/> </actionGroup> <!-- Assert initial CMS page Url Rewrite in Grid--> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath1"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath1"> <argument name="redirectPath" value="$$createCMSPage.identifier$$" /> <argument name="redirectType" value="No"/> <argument name="targetPath" value="cms/page/view/page_id/{$cmsId}"/> </actionGroup> <!-- Assert Updated Request Path redirects to the CMS Page on Store Front --> - <actionGroup ref="navigateToStorefrontForCreatedPage" stepKey="navigateToTheStoreFront"> + <actionGroup ref="NavigateToStorefrontForCreatedPageActionGroup" stepKey="navigateToTheStoreFront"> <argument name="page" value="{{defaultCmsPage.title}}"/> </actionGroup> <!-- Assert updated CMS redirect in Store Front --> - <actionGroup ref="AssertStoreFrontCMSPage" stepKey="assertCMSPage"> + <actionGroup ref="AssertStoreFrontCMSPageActionGroup" stepKey="assertCMSPage"> <argument name="cmsTitle" value="$$createCMSPage.title$$"/> <argument name="cmsContent" value="$$createCMSPage.content$$"/> <argument name="cmsContentHeading" value="$$createCMSPage.content_heading$$"/> </actionGroup> <!-- Assert initial request path directs to the CMS Page on Store Front --> - <actionGroup ref="navigateToStorefrontForCreatedPage" stepKey="navigateToTheStoreFront1"> + <actionGroup ref="NavigateToStorefrontForCreatedPageActionGroup" stepKey="navigateToTheStoreFront1"> <argument name="page" value="$$createCMSPage.identifier$$"/> </actionGroup> <!-- Assert initial CMS redirect in Store Front --> - <actionGroup ref="AssertStoreFrontCMSPage" stepKey="assertCMSPage1"> + <actionGroup ref="AssertStoreFrontCMSPageActionGroup" stepKey="assertCMSPage1"> <argument name="cmsTitle" value="$$createCMSPage.title$$"/> <argument name="cmsContent" value="$$createCMSPage.content$$"/> <argument name="cmsContentHeading" value="$$createCMSPage.content_heading$$"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml index e6ee9b484059d..074140845c8a6 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml @@ -23,20 +23,20 @@ </before> <after> <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> - <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteCustomUrlRewrite"> + <actionGroup ref="AdminDeleteUrlRewriteActionGroup" 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"> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage"> <argument name="CMSPage" value="$$createCMSPage$$"/> </actionGroup> <grabFromCurrentUrl stepKey="cmsId" regex="#\/([0-9]*)?\/$#"/> <!-- Open UrlRewrite Edit page and update the fields and fill the created CMS Page Target Path --> - <actionGroup ref="AdminAddCustomUrlRewrite" stepKey="addCustomUrlRewrite"> + <actionGroup ref="AdminAddCustomUrlRewriteActionGroup" stepKey="addCustomUrlRewrite"> <argument name="customUrlRewriteValue" value="Custom"/> <argument name="storeValue" value="Default Store View"/> <argument name="requestPath" value="{{defaultCmsPage.title}}"/> @@ -46,38 +46,38 @@ </actionGroup> <!-- Assert updated CMS page Url Rewrite in Grid --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath"> <argument name="redirectPath" value="{{defaultCmsPage.title}}" /> <argument name="redirectType" value="Temporary (302)" /> <argument name="targetPath" value="cms/page/view/page_id/{$cmsId}"/> </actionGroup> <!-- Assert initial CMS page Url Rewrite in Grid --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath1"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath1"> <argument name="redirectPath" value="$$createCMSPage.identifier$$" /> <argument name="redirectType" value="No"/> <argument name="targetPath" value="cms/page/view/page_id/{$cmsId}"/> </actionGroup> <!-- Assert Updated Request Path redirects to the CMS Page on Store Front --> - <actionGroup ref="navigateToStorefrontForCreatedPage" stepKey="navigateToTheStoreFront"> + <actionGroup ref="NavigateToStorefrontForCreatedPageActionGroup" stepKey="navigateToTheStoreFront"> <argument name="page" value="{{defaultCmsPage.title}}"/> </actionGroup> <!--Assert updated CMS redirect in Store Front--> - <actionGroup ref="AssertStoreFrontCMSPage" stepKey="assertCMSPage"> + <actionGroup ref="AssertStoreFrontCMSPageActionGroup" stepKey="assertCMSPage"> <argument name="cmsTitle" value="$$createCMSPage.title$$"/> <argument name="cmsContent" value="$$createCMSPage.content$$"/> <argument name="cmsContentHeading" value="$$createCMSPage.content_heading$$"/> </actionGroup> <!-- Assert initial request path directs to the CMS Page on Store Front --> - <actionGroup ref="navigateToStorefrontForCreatedPage" stepKey="navigateToTheStoreFront1"> + <actionGroup ref="NavigateToStorefrontForCreatedPageActionGroup" stepKey="navigateToTheStoreFront1"> <argument name="page" value="$$createCMSPage.identifier$$"/> </actionGroup> <!--Assert initial CMS redirect in Store Front--> - <actionGroup ref="AssertStoreFrontCMSPage" stepKey="assertCMSPage1"> + <actionGroup ref="AssertStoreFrontCMSPageActionGroup" stepKey="assertCMSPage1"> <argument name="cmsTitle" value="$$createCMSPage.title$$"/> <argument name="cmsContent" value="$$createCMSPage.content$$"/> <argument name="cmsContentHeading" value="$$createCMSPage.content_heading$$"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml index b123bc14cb1ed..cdd7c334e35cd 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml @@ -23,20 +23,20 @@ </before> <after> <deleteData createDataKey="category" stepKey="deleteCategory"/> - <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteCustomUrlRewrite"> + <actionGroup ref="AdminDeleteUrlRewriteActionGroup" 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"> + <actionGroup ref="OpenCategoryFromCategoryTreeActionGroup" 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"> + <actionGroup ref="AdminAddCustomUrlRewriteActionGroup" stepKey="addCustomUrlRewrite"> <argument name="customUrlRewriteValue" value="Custom"/> <argument name="storeValue" value="Default Store View"/> <argument name="requestPath" value="{{FirstLevelSubCat.name}}.html"/> @@ -46,27 +46,27 @@ </actionGroup> <!-- Assert updated category Url Rewrite in grid --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByCategoryRequestPath"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" 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"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" 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="AssertStorefrontUrlRewriteRedirect" stepKey="verifyCategoryInStoreFront"> + <actionGroup ref="AssertStorefrontUrlRewriteRedirectActionGroup" 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="AssertStorefrontUrlRewriteRedirect" stepKey="verifyCategoryInStoreFront1"> + <actionGroup ref="AssertStorefrontUrlRewriteRedirectActionGroup" 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 711d5389b013b..593c4282fc516 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml @@ -23,20 +23,20 @@ </before> <after> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteCustomUrlRewrite"> + <actionGroup ref="AdminDeleteUrlRewriteActionGroup" 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"> + <actionGroup ref="FilterAndSelectProductActionGroup" 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"> + <actionGroup ref="AdminAddCustomUrlRewriteActionGroup" stepKey="addCustomUrlRewrite"> <argument name="customUrlRewriteValue" value="Custom"/> <argument name="storeValue" value="Default Store View"/> <argument name="requestPath" value="{{_defaultProduct.name}}.html"/> @@ -46,28 +46,28 @@ </actionGroup> <!--Assert updated product Url Rewrite in Grid --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" 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"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" 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="AssertStorefrontProductRedirect" stepKey="verifyProductInStoreFront"> + <actionGroup ref="AssertStorefrontProductRedirectActionGroup" 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="AssertStorefrontProductRedirect" stepKey="verifyProductInStoreFront1"> + <actionGroup ref="AssertStorefrontProductRedirectActionGroup" stepKey="verifyProductInStoreFront1"> <argument name="productName" value="$$createProduct.name$$"/> <argument name="productSku" value="$$createProduct.sku$$"/> <argument name="productRequestPath" value="$$createProduct.name$$.html"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteAndAddNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteAndAddNoRedirectTest.xml index f8d297c92a176..c51030315b287 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteAndAddNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteAndAddNoRedirectTest.xml @@ -27,12 +27,12 @@ </after> <!--Filter and Select the created Product --> - <actionGroup ref="AdminSearchUrlRewriteProductBySku" stepKey="searchProduct"> + <actionGroup ref="AdminSearchUrlRewriteProductBySkuActionGroup" stepKey="searchProduct"> <argument name="productSku" value="$$createSimpleProduct.sku$$"/> </actionGroup> <!-- Update the Store, RequestPath, RedirectType and Description --> - <actionGroup ref="AdminAddUrlRewriteForProduct" stepKey="addUrlRewrite"> + <actionGroup ref="AdminAddUrlRewriteForProductActionGroup" stepKey="addUrlRewrite"> <argument name="storeValue" value="Default Store View"/> <argument name="requestPath" value="{{_defaultProduct.urlKey}}.html"/> <argument name="redirectTypeValue" value="No"/> @@ -40,23 +40,23 @@ </actionGroup> <!--Filter Product in product page and get the Product ID --> - <actionGroup ref="filterAndSelectProduct" stepKey="filterProduct"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="filterProduct"> <argument name="productSku" value="$$createSimpleProduct.sku$$"/> </actionGroup> <grabFromCurrentUrl stepKey="productId" regex="#\/([0-9]*)?\/$#"/> <!--Assert Product Redirect --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath"> <argument name="redirectPath" value="{{_defaultProduct.urlKey}}.html" /> <argument name="redirectType" value="No" /> <argument name="targetPath" value="catalog/product/view/id/{$productId}"/> </actionGroup> <!-- Assert Redirect path, Target Path and Redirect type in grid --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath1"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath1"> <argument name="redirectPath" value="$$createSimpleProduct.name$$.html" /> <argument name="redirectType" value="No" /> <argument name="targetPath" value="catalog/product/view/id/{$productId}"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteWithCategoryAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteWithCategoryAndAddTemporaryRedirectTest.xml index ae18ab33ba6ce..d433aa7557094 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteWithCategoryAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductURLRewriteWithCategoryAndAddTemporaryRedirectTest.xml @@ -31,12 +31,12 @@ </after> <!--Filter and Select the created Product --> - <actionGroup ref="AdminSearchUrlRewriteProductBySku" stepKey="searchProduct"> + <actionGroup ref="AdminSearchUrlRewriteProductBySkuActionGroup" stepKey="searchProduct"> <argument name="productSku" value="$$createSimpleProduct.sku$$"/> </actionGroup> <!-- Update the Store, RequestPath, RedirectType and Description --> - <actionGroup ref="AdminAddUrlRewriteForProduct" stepKey="addUrlRewrite"> + <actionGroup ref="AdminAddUrlRewriteForProductActionGroup" stepKey="addUrlRewrite"> <argument name="storeValue" value="Default Store View"/> <argument name="requestPath" value="{{FirstLevelSubCat.name_lwr}}/{{_defaultProduct.urlKey}}.html"/> <argument name="redirectTypeValue" value="Temporary (302)"/> @@ -44,36 +44,36 @@ </actionGroup> <!--Assert Product Redirect --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath"> <argument name="redirectPath" value="{{FirstLevelSubCat.name_lwr}}/{{_defaultProduct.urlKey}}.html" /> <argument name="redirectType" value="Temporary (302)" /> <argument name="targetPath" value="$$createSimpleProduct.name$$.html"/> </actionGroup> <!--Filter Product in product page and get the Product ID --> - <actionGroup ref="filterAndSelectProduct" stepKey="filterProduct"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="filterProduct"> <argument name="productSku" value="$$createSimpleProduct.sku$$"/> </actionGroup> <grabFromCurrentUrl stepKey="productId" regex="#\/([0-9]*)?\/$#"/> <!-- Assert Redirect path, Target Path and Redirect type in grid --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath1"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath1"> <argument name="redirectPath" value="$$createSimpleProduct.name$$.html" /> <argument name="redirectType" value="No" /> <argument name="targetPath" value="catalog/product/view/id/{$productId}"/> </actionGroup> <!-- Open Category Page and Get Category ID --> - <actionGroup ref="OpenCategoryFromCategoryTree" stepKey="getCategoryId"> + <actionGroup ref="OpenCategoryFromCategoryTreeActionGroup" stepKey="getCategoryId"> <argument name="category" value="$$createCategory.name$$"/> </actionGroup> <grabFromCurrentUrl stepKey="categoryId" regex="#\/([0-9]*)?\/$#"/> <!-- Assert Redirect path, Target Path and Redirect type in grid --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath2"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath2"> <argument name="redirectPath" value="$$createCategory.name$$.html" /> <argument name="redirectType" value="No" /> <argument name="targetPath" value="catalog/category/view/id/{$categoryId}"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml index 66c586d4fe891..e1ff4f598943e 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddPermanentRedirectTest.xml @@ -26,12 +26,12 @@ </after> <!--Filter and Select the created Product --> - <actionGroup ref="AdminSearchUrlRewriteProductBySku" stepKey="searchProduct"> + <actionGroup ref="AdminSearchUrlRewriteProductBySkuActionGroup" stepKey="searchProduct"> <argument name="productSku" value="$$createSimpleProduct.sku$$"/> </actionGroup> <!-- Update the Store, RequestPath, RedirectType and Description --> - <actionGroup ref="AdminAddUrlRewriteForProduct" stepKey="addUrlRewrite"> + <actionGroup ref="AdminAddUrlRewriteForProductActionGroup" stepKey="addUrlRewrite"> <argument name="storeValue" value="Default Store View"/> <argument name="requestPath" value="{{_defaultProduct.urlKey}}.html"/> <argument name="redirectTypeValue" value="Permanent (301)"/> @@ -39,24 +39,24 @@ </actionGroup> <!--Filter Product in product page and get the Product ID --> - <actionGroup ref="filterAndSelectProduct" stepKey="filterProduct"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="filterProduct"> <argument name="productSku" value="$$createSimpleProduct.sku$$"/> </actionGroup> <grabFromCurrentUrl stepKey="productId" regex="#\/([0-9]*)?\/$#"/> <!--Assert Product Redirect --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath"> <argument name="redirectPath" value="{{_defaultProduct.urlKey}}.html" /> <argument name="redirectType" value="Permanent (301)" /> <argument name="targetPath" value="$$createSimpleProduct.name$$.html"/> </actionGroup> <!-- Assert Redirect Path, Target Path and Redirect type in grid --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath1"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath1"> <argument name="redirectPath" value="$$createSimpleProduct.name$$.html" /> <argument name="redirectType" value="No" /> <argument name="targetPath" value="catalog/product/view/id/{$productId}"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml index 2d797a12bedf5..3fa4e6b7bad90 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductUrLRewriteAndAddTemporaryRedirectTest.xml @@ -27,12 +27,12 @@ </after> <!--Filter and Select the created Product --> - <actionGroup ref="AdminSearchUrlRewriteProductBySku" stepKey="searchProduct"> + <actionGroup ref="AdminSearchUrlRewriteProductBySkuActionGroup" stepKey="searchProduct"> <argument name="productSku" value="$$createSimpleProduct.sku$$"/> </actionGroup> <!-- Update the Store, RequestPath, RedirectType and Description --> - <actionGroup ref="AdminAddUrlRewriteForProduct" stepKey="addUrlRewrite"> + <actionGroup ref="AdminAddUrlRewriteForProductActionGroup" stepKey="addUrlRewrite"> <argument name="storeValue" value="Default Store View"/> <argument name="requestPath" value="{{_defaultProduct.urlKey}}.html"/> <argument name="redirectTypeValue" value="Temporary (302)"/> @@ -40,23 +40,23 @@ </actionGroup> <!--Filter Product in product page and get the Product ID --> - <actionGroup ref="filterAndSelectProduct" stepKey="filterProduct"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="filterProduct"> <argument name="productSku" value="$$createSimpleProduct.sku$$"/> </actionGroup> <grabFromCurrentUrl stepKey="productId" regex="#\/([0-9]*)?\/$#"/> <!--Assert Product Redirect --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath"> <argument name="redirectPath" value="{{_defaultProduct.urlKey}}.html" /> <argument name="redirectType" value="Temporary (302)" /> <argument name="targetPath" value="$$createSimpleProduct.name$$.html"/> </actionGroup> <!-- Assert Redirect Path, Target Path and Redirect type in grid --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath1"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByRequestPath1"> <argument name="redirectPath" value="$$createSimpleProduct.name$$.html" /> <argument name="redirectType" value="No" /> <argument name="targetPath" value="catalog/product/view/id/{$productId}"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml index 4dc5c85830076..38a84f6a75f30 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml @@ -51,7 +51,7 @@ </actionGroup> <!-- Create second store --> - <actionGroup ref="CreateCustomStore" stepKey="createCustomStore"> + <actionGroup ref="CreateCustomStoreActionGroup" stepKey="createCustomStore"> <argument name="website" value="{{_defaultWebsite.name}}"/> <argument name="store" value="{{customStoreGroup.name}}"/> <argument name="rootCategory" value="$$rootCategory.name$$"/> @@ -65,7 +65,7 @@ <!-- Create simple product with categories created in create data --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="openProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProduct"> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProduct"> <argument name="product" value="$$createProduct$$"/> </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowOfCreatedSimpleProduct"/> @@ -85,12 +85,12 @@ <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> <!-- Grab category Id --> - <actionGroup ref="OpenCategoryFromCategoryTree" stepKey="grabCategoryId"> + <actionGroup ref="OpenCategoryFromCategoryTreeActionGroup" stepKey="grabCategoryId"> <argument name="category" value="$$category.name$$"/> </actionGroup> <grabFromCurrentUrl stepKey="categoryId" regex="#\/([0-9]*)?\/$#"/> <!-- Open Url Rewrite page and verify new Redirect Path, RedirectType and Target Path for the grabbed category Id --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchPath"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchPath"> <argument name="redirectPath" value="$$category.name$$.html"/> <argument name="redirectType" value="No"/> <argument name="targetPath" value="catalog/category/view/id/{$categoryId}"/> @@ -99,12 +99,12 @@ <see selector="{{AdminUrlRewriteIndexSection.storeView('1')}}" userInput="{{customStoreEN.name}}" stepKey="seeStoreViewValueForCategoryId"/> <!-- Grab product Id --> - <actionGroup ref="filterAndSelectProduct" stepKey="grabProductId"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="grabProductId"> <argument name="productSku" value="$$createProduct.sku$$"/> </actionGroup> <grabFromCurrentUrl stepKey="productId" regex="#\/([0-9]*)?\/$#"/> <!-- Open Url Rewrite page and verify new Redirect Path, RedirectType and Target Path for the grabbed product Id --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchPath1"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchPath1"> <argument name="redirectPath" value="$$createProduct.name$$.html"/> <argument name="redirectType" value="No"/> <argument name="targetPath" value="catalog/product/view/id/{$productId}"/> @@ -112,4 +112,4 @@ <see selector="{{AdminUrlRewriteIndexSection.storeView('1')}}" userInput="{{customStore.name}}" stepKey="seeStoreValueForProductId"/> <see selector="{{AdminUrlRewriteIndexSection.storeView('1')}}" userInput="{{storeViewData.name}}" stepKey="seeStoreViewValueForProductId"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteHypenAsRequestPathTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteHypenAsRequestPathTest.xml new file mode 100644 index 0000000000000..4b9f37f628f34 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteHypenAsRequestPathTest.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="AdminDeleteCategoryUrlRewriteHypenAsRequestPathTest"> + <annotations> + <features value="UrlRewrite" /> + <stories value="Delete custom URL rewrite"/> + <title value="Delete category URL rewrite, hyphen as request path"/> + <description value="Delete category URL rewrite, hyphen as request path"/> + <testCaseId value="MC-5348" /> + <group value="urlRewrite"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <createData entity="_defaultCategory" stepKey="category"/> + </before> + <after> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Create the Category Url Rewrite--> + <actionGroup ref="AdminAddUrlRewriteActionGroup" stepKey="addUrlRewrite"> + <argument name="category" value="$$category.name$$"/> + <argument name="customUrlRewriteValue" value="For Category"/> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="-"/> + <argument name="redirectTypeValue" value="No"/> + <argument name="description" value="End To End Test"/> + </actionGroup> + + <!--Delete the Category Url Rewrite--> + <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteCustomUrlRewrite"> + <argument name="requestPath" value="-"/> + </actionGroup> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="You deleted the URL rewrite."/> + </actionGroup> + + <!--Verify AssertPageByUrlRewriteIsNotFound--> + <actionGroup ref="AssertPageByUrlRewriteIsNotFoundActionGroup" stepKey="checkUrlOnFrontend"> + <argument name="requestPath" value="-"/> + </actionGroup> + + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteWithRequestPathTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteWithRequestPathTest.xml new file mode 100644 index 0000000000000..7c4023c6d0f75 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCategoryUrlRewriteWithRequestPathTest.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="AdminDeleteCategoryUrlRewriteWithRequestPathTest"> + <annotations> + <features value="UrlRewrite" /> + <stories value="Delete custom URL rewrite"/> + <title value="Delete category URL rewrite, with request path"/> + <description value="Delete category URL rewrite, with request path"/> + <testCaseId value="MC-5349" /> + <group value="urlRewrite"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <createData entity="_defaultCategory" stepKey="category"/> + </before> + <after> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Create the Category Url Rewrite--> + <actionGroup ref="AdminAddUrlRewriteActionGroup" stepKey="addUrlRewriteSecondTime"> + <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> + + <!--Delete the Category Url Rewrite--> + <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteCustomUrlRewriteSecondTime"> + <argument name="requestPath" value="newrequestpath.html"/> + </actionGroup> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessageSecondTime"> + <argument name="message" value="You deleted the URL rewrite."/> + </actionGroup> + + <!--Verify AssertPageByUrlRewriteIsNotFound--> + <actionGroup ref="AssertPageByUrlRewriteIsNotFoundActionGroup" stepKey="checkUrlOnFrontendSecondTime"> + <argument name="requestPath" value="newrequestpath.html"/> + </actionGroup> + + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml new file mode 100644 index 0000000000000..c40dd3256114e --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteCmsPageUrlRewriteWithNoRedirectsTest"> + <annotations> + <stories value="Delete CMS Page URL rewrite with No Redirects"/> + <title value="Delete CMS Page URL rewrite with No Redirects"/> + <description value="Log in to admin and delete CMS Page URL rewrite with No Redirects"/> + <testCaseId value="MC-14648"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="simpleCmsPage" stepKey="createCMSPage"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + </before> + <after> + <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <actionGroup ref="AdminGoToAddNewUrlRewritePageActionGroup" stepKey="openUrlRewriteEditPage"/> + <actionGroup ref="AdminCreateNewUrlRewriteForCmsPageActionGroup" stepKey="selectForCsmPageType"> + <argument name="customUrlRewriteValue" value="For CMS page'"/> + </actionGroup> + <actionGroup ref="AdminSelectCmsPageFromGridForNewUrlRewriteActionGroup" stepKey="selectCmsPge"> + <argument name="cmsPageUrlKey" value="$$createCMSPage.identifier$$"/> + </actionGroup> + <actionGroup ref="AdminFillNewCmsPageUrlRewriteFormActionGroup" stepKey="fillTheForm"> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="newrequestpath"/> + <argument name="redirectTypeValue" value="No"/> + <argument name="description" value="cms_default_no_redirect"/> + </actionGroup> + + <!--Delete the URL Rewrite for CMS Page with No redirects--> + <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteCustomUrlRewrite"> + <argument name="requestPath" value="newrequestpath"/> + </actionGroup> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="You deleted the URL rewrite."/> + </actionGroup> + + <!--Search and verify AssertUrlRewriteNotInGrid--> + <actionGroup ref="AdminSearchDeletedUrlRewriteActionGroup" stepKey="searchDeletedUrlRewriteInGrid"> + <argument name="requestPath" value="newrequestpath"/> + </actionGroup> + + <!--Verify AssertPageByUrlRewriteIsNotFound--> + <actionGroup ref="AssertPageByUrlRewriteIsNotFoundActionGroup" stepKey="assertPageByUrlRewriteIsNotFound"> + <argument name="requestPath" value="newrequestpath"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml new file mode 100644 index 0000000000000..741be6985d517 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteCmsPageUrlRewriteWithPermanentRedirectTest"> + <annotations> + <stories value="Delete CMS Page URL rewrite with Permanent Redirect"/> + <title value="Delete CMS Page URL rewrite with Permanent Redirect"/> + <description value="Log in to admin and delete CMS Page URL rewrite with Permanent Redirect"/> + <testCaseId value="MC-14649"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="simpleCmsPage" stepKey="createCMSPage"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + </before> + <after> + <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <actionGroup ref="AdminGoToAddNewUrlRewritePageActionGroup" stepKey="openUrlRewriteEditPage"/> + <actionGroup ref="AdminCreateNewUrlRewriteForCmsPageActionGroup" stepKey="selectForCsmPageType"> + <argument name="customUrlRewriteValue" value="For CMS page'"/> + </actionGroup> + <actionGroup ref="AdminSelectCmsPageFromGridForNewUrlRewriteActionGroup" stepKey="selectCmsPge"> + <argument name="cmsPageUrlKey" value="$$createCMSPage.identifier$$"/> + </actionGroup> + <actionGroup ref="AdminFillNewCmsPageUrlRewriteFormActionGroup" stepKey="fillTheForm"> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="permanentrequestpath.html"/> + <argument name="redirectTypeValue" value="Permanent (301)"/> + <argument name="description" value="cms_default_permanent_redirect"/> + </actionGroup> + + <!-- Delete the URL Rewrite for CMS Page with permanent redirect--> + <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deletePermanentUrlRewrite"> + <argument name="requestPath" value="permanentrequestpath.html"/> + </actionGroup> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="You deleted the URL rewrite."/> + </actionGroup> + + <!-- Verify AssertPageByUrlRewriteIsNotFound --> + <actionGroup ref="AssertPageByUrlRewriteIsNotFoundActionGroup" stepKey="assertPageByUrlRewriteIsNotFound"> + <argument name="requestPath" value="permanentrequestpath.html"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml new file mode 100644 index 0000000000000..43de4123f35a8 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminDeleteCmsPageUrlRewriteWithTemporaryRedirectTest"> + <annotations> + <stories value="Delete CMS Page URL rewrite with Temporary Redirect"/> + <title value="Delete CMS Page URL rewrite with Temporary Redirect"/> + <description value="Log in to admin and delete CMS Page URL rewrite with Temporary Redirect"/> + <testCaseId value="MC-14650"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="simpleCmsPage" stepKey="createCMSPage"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + </before> + <after> + <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <actionGroup ref="AdminGoToAddNewUrlRewritePageActionGroup" stepKey="openUrlRewriteEditPage"/> + <actionGroup ref="AdminCreateNewUrlRewriteForCmsPageActionGroup" stepKey="selectForCsmPageType"> + <argument name="customUrlRewriteValue" value="For CMS page'"/> + </actionGroup> + <actionGroup ref="AdminSelectCmsPageFromGridForNewUrlRewriteActionGroup" stepKey="selectCmsPge"> + <argument name="cmsPageUrlKey" value="$$createCMSPage.identifier$$"/> + </actionGroup> + <actionGroup ref="AdminFillNewCmsPageUrlRewriteFormActionGroup" stepKey="fillTheForm"> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="temporaryrequestpath.html"/> + <argument name="redirectTypeValue" value="Temporary (302)"/> + <argument name="description" value="cms_default_temporary_redirect"/> + </actionGroup> + + <!-- Delete the URL Rewrite for CMS Page with with temporary redirect--> + <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteTemporaryUrlRewrite"> + <argument name="requestPath" value="temporaryrequestpath.html"/> + </actionGroup> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="You deleted the URL rewrite."/> + </actionGroup> + + <!--Verify AssertPageByUrlRewriteIsNotFound--> + <actionGroup ref="AssertPageByUrlRewriteIsNotFoundActionGroup" stepKey="assertPageByUrlRewriteIsNotFound"> + <argument name="requestPath" value="temporaryrequestpath.html"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCustomUrlRewriteTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCustomUrlRewriteTest.xml index cf45931029778..288c32102c606 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCustomUrlRewriteTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminDeleteCustomUrlRewriteTest.xml @@ -27,18 +27,18 @@ </after> <!--Delete created custom url rewrite and verify AssertUrlRewriteDeletedMessage--> - <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteUrlRewrite"> + <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteUrlRewrite"> <argument name="requestPath" value="$$urlRewrite.request_path$$"/> </actionGroup> <!--Search and verify AssertUrlRewriteNotInGrid--> - <actionGroup ref="AdminSearchDeletedUrlRewrite" stepKey="searchDeletedUrlRewriteInGrid"> + <actionGroup ref="AdminSearchDeletedUrlRewriteActionGroup" stepKey="searchDeletedUrlRewriteInGrid"> <argument name="requestPath" value="$$urlRewrite.request_path$$"/> </actionGroup> <!--Verify AssertPageByUrlRewriteIsNotFound--> - <actionGroup ref="AssertPageByUrlRewriteIsNotFound" stepKey="amOnPage"> + <actionGroup ref="AssertPageByUrlRewriteIsNotFoundActionGroup" stepKey="amOnPage"> <argument name="requestPath" value="$$urlRewrite.request_path$$"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminProductCreateUrlRewriteForCustomStoreViewTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminProductCreateUrlRewriteForCustomStoreViewTest.xml index c6ee1a7da9602..0ad68dae4e4ce 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminProductCreateUrlRewriteForCustomStoreViewTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminProductCreateUrlRewriteForCustomStoreViewTest.xml @@ -44,7 +44,7 @@ <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> <!--Step 1. Navigate as Admin on Product Page for edit product`s Url Key--> - <actionGroup ref="navigateToCreatedProductEditPage" stepKey="goToProductForUrlRewrite"> + <actionGroup ref="NavigateToCreatedProductEditPageActionGroup" stepKey="goToProductForUrlRewrite"> <argument name="product" value="$$createProductForUrlRewrite$$"/> </actionGroup> <!--Step 2. As Admin switch on Custom Store View from Precondition --> @@ -55,19 +55,19 @@ <actionGroup ref="AdminProductFormUpdateUrlKeyActionGroup" stepKey="updateUrlKeyForProduct"> <argument name="newUrlKey" value="U2"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProductWithNewUrl"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductWithNewUrl"/> <!--Step 4. Set URL Key for created category --> - <actionGroup ref="navigateToCreatedCategory" stepKey="navigateToCreatedSubCategory"> + <actionGroup ref="NavigateToCreatedCategoryActionGroup" stepKey="navigateToCreatedSubCategory"> <argument name="Category" value="$$createCategory$$"/> </actionGroup> - <actionGroup ref="ChangeSeoUrlKey" stepKey="updateUrlKeyForCategory"> + <actionGroup ref="ChangeSeoUrlKeyActionGroup" stepKey="updateUrlKeyForCategory"> <argument name="value" value="U1"/> </actionGroup> <!--Step 5. On Storefront Assert what URL Key for Category is changed and is correct as for Default Store View --> <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="onCategoryPage"> <argument name="category" value="$$createCategory$$"/> </actionGroup> - <actionGroup ref="AssertStorefrontUrlRewriteRedirect" stepKey="assertUrlCategoryOnDefaultStore"> + <actionGroup ref="AssertStorefrontUrlRewriteRedirectActionGroup" stepKey="assertUrlCategoryOnDefaultStore"> <argument name="category" value="$$createCategory.name$$"/> <argument name="newRequestPath" value="u1.html"/> </actionGroup> @@ -92,7 +92,7 @@ <argument name="storeView" value="customStore"/> </actionGroup> <!--Step 9. On Storefront Assert what URL Key for Category is changed and is correct for Custom Store View --> - <actionGroup ref="AssertStorefrontUrlRewriteRedirect" stepKey="assertUrlCategoryOnCustomStore"> + <actionGroup ref="AssertStorefrontUrlRewriteRedirectActionGroup" stepKey="assertUrlCategoryOnCustomStore"> <argument name="category" value="$$createCategory.name$$"/> <argument name="newRequestPath" value="u1.html"/> </actionGroup> @@ -105,7 +105,7 @@ <argument name="productUrl" value="$$createProduct.custom_attributes[url_key]$$"/> </actionGroup> <!--Step 11. On Storefront Assert what URL Key for product is changed and is correct for Custom Store View --> - <actionGroup ref="AssertStorefrontProductRedirect" stepKey="assertProductUrlRewriteInStoreFront"> + <actionGroup ref="AssertStorefrontProductRedirectActionGroup" stepKey="assertProductUrlRewriteInStoreFront"> <argument name="productName" value="$$createProductForUrlRewrite.name$$"/> <argument name="productSku" value="$$createProductForUrlRewrite.sku$$"/> <argument name="productRequestPath" value="u2.html"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddAspxRequestPathTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddAspxRequestPathTest.xml index 072753505223d..fb8f200741c8a 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddAspxRequestPathTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddAspxRequestPathTest.xml @@ -27,12 +27,12 @@ </after> <!-- Search and Select Edit option for created category in grid --> - <actionGroup ref="AdminSearchAndSelectUrlRewriteInGrid" stepKey="editUrlRewrite"> + <actionGroup ref="AdminSearchAndSelectUrlRewriteInGridActionGroup" stepKey="editUrlRewrite"> <argument name="requestPath" value="$$category.custom_attributes[url_key]$$.html"/> </actionGroup> <!-- Open UrlRewrite Edit page and update the fields --> - <actionGroup ref="AdminUpdateUrlRewrite" stepKey="updateCategoryUrlRewrite"> + <actionGroup ref="AdminUpdateUrlRewriteActionGroup" stepKey="updateCategoryUrlRewrite"> <argument name="storeValue" value="Default Store View"/> <argument name="requestPath" value="{{defaultUrlRewrite.request_path}}"/> <argument name="redirectTypeValue" value="Temporary (302)"/> @@ -40,20 +40,20 @@ </actionGroup> <!-- Open Category Page and Get Category ID --> - <actionGroup ref="OpenCategoryFromCategoryTree" stepKey="getCategoryId"> + <actionGroup ref="OpenCategoryFromCategoryTreeActionGroup" stepKey="getCategoryId"> <argument name="category" value="$$category.name$$"/> </actionGroup> <grabFromCurrentUrl stepKey="categoryId" regex="#\/([0-9]*)?\/$#"/> <!--Assert category Url Rewrite in grid --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByNewRequestPath"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByNewRequestPath"> <argument name="redirectPath" value="{{defaultUrlRewrite.request_path}}" /> <argument name="redirectType" value="Temporary (302)" /> <argument name="targetPath" value="catalog/category/view/id/{$categoryId}"/> </actionGroup> <!-- Assert category redirect in Store Front --> - <actionGroup ref="AssertStorefrontUrlRewriteRedirect" stepKey="verifyCategoryInStoreFront"> + <actionGroup ref="AssertStorefrontUrlRewriteRedirectActionGroup" stepKey="verifyCategoryInStoreFront"> <argument name="category" value="$$category.name$$"/> <argument name="newRequestPath" value="{{defaultUrlRewrite.request_path}}"/> </actionGroup> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddNoRedirectTest.xml index 80b9dbe41bf59..72180105b38f8 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddNoRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddNoRedirectTest.xml @@ -27,12 +27,12 @@ </after> <!-- Search and Select Edit option for created category in grid --> - <actionGroup ref="AdminSearchAndSelectUrlRewriteInGrid" stepKey="editUrlRewrite"> + <actionGroup ref="AdminSearchAndSelectUrlRewriteInGridActionGroup" stepKey="editUrlRewrite"> <argument name="requestPath" value="$$category.custom_attributes[url_key]$$.html"/> </actionGroup> <!-- Open UrlRewrite Edit page and update the fields --> - <actionGroup ref="AdminUpdateUrlRewrite" stepKey="updateCategoryUrlRewrite"> + <actionGroup ref="AdminUpdateUrlRewriteActionGroup" stepKey="updateCategoryUrlRewrite"> <argument name="storeValue" value="Default Store View"/> <argument name="requestPath" value="{{defaultUrlRewrite.request_path}}"/> <argument name="redirectTypeValue" value="No"/> @@ -40,20 +40,20 @@ </actionGroup> <!-- Open Category Page and Get Category ID --> - <actionGroup ref="OpenCategoryFromCategoryTree" stepKey="getCategoryId"> + <actionGroup ref="OpenCategoryFromCategoryTreeActionGroup" stepKey="getCategoryId"> <argument name="category" value="$$category.name$$"/> </actionGroup> <grabFromCurrentUrl stepKey="categoryId" regex="#\/([0-9]*)?\/$#"/> <!-- Assert category Url Rewrite in grid --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByNewRequestPath"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByNewRequestPath"> <argument name="redirectPath" value="{{defaultUrlRewrite.request_path}}" /> <argument name="redirectType" value="No" /> <argument name="targetPath" value="catalog/category/view/id/{$categoryId}"/> </actionGroup> <!-- Assert Category redirect in Store Front --> - <actionGroup ref="AssertStorefrontUrlRewriteRedirect" stepKey="verifyCategoryInStoreFront"> + <actionGroup ref="AssertStorefrontUrlRewriteRedirectActionGroup" stepKey="verifyCategoryInStoreFront"> <argument name="category" value="$$category.name$$"/> <argument name="newRequestPath" value="{{defaultUrlRewrite.request_path}}"/> </actionGroup> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml index be9fd1d83c8f1..3fb8e5da39caf 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddPermanentRedirectTest.xml @@ -27,12 +27,12 @@ </after> <!-- Search and Select Edit option for created category in grid --> - <actionGroup ref="AdminSearchAndSelectUrlRewriteInGrid" stepKey="editUrlRewrite"> + <actionGroup ref="AdminSearchAndSelectUrlRewriteInGridActionGroup" stepKey="editUrlRewrite"> <argument name="requestPath" value="$$category.custom_attributes[url_key]$$.html"/> </actionGroup> <!-- Open UrlRewrite Edit page and update the fields --> - <actionGroup ref="AdminUpdateUrlRewrite" stepKey="updateCategoryUrlRewrite"> + <actionGroup ref="AdminUpdateUrlRewriteActionGroup" stepKey="updateCategoryUrlRewrite"> <argument name="storeValue" value="Default Store View"/> <argument name="requestPath" value="{{defaultUrlRewrite.request_path}}"/> <argument name="redirectTypeValue" value="Permanent (301)"/> @@ -40,20 +40,20 @@ </actionGroup> <!-- Open Category Page and Get Category ID --> - <actionGroup ref="OpenCategoryFromCategoryTree" stepKey="getCategoryId"> + <actionGroup ref="OpenCategoryFromCategoryTreeActionGroup" stepKey="getCategoryId"> <argument name="category" value="$$category.name$$"/> </actionGroup> <grabFromCurrentUrl stepKey="categoryId" regex="#\/([0-9]*)?\/$#"/> <!--Assert category Url Rewrite in grid --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByNewRequestPath"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByNewRequestPath"> <argument name="redirectPath" value="{{defaultUrlRewrite.request_path}}" /> <argument name="redirectType" value="Permanent (301)" /> <argument name="targetPath" value="catalog/category/view/id/{$categoryId}"/> </actionGroup> <!-- Assert category redirect in Store Front --> - <actionGroup ref="AssertStorefrontUrlRewriteRedirect" stepKey="verifyCategoryInStoreFront"> + <actionGroup ref="AssertStorefrontUrlRewriteRedirectActionGroup" stepKey="verifyCategoryInStoreFront"> <argument name="category" value="$$category.name$$"/> <argument name="newRequestPath" value="{{defaultUrlRewrite.request_path}}"/> </actionGroup> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml index 7e1b9acbc47ab..bea5c44461a70 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml @@ -27,12 +27,12 @@ </after> <!-- Search and Select Edit option for created category in grid --> - <actionGroup ref="AdminSearchAndSelectUrlRewriteInGrid" stepKey="editUrlRewrite"> + <actionGroup ref="AdminSearchAndSelectUrlRewriteInGridActionGroup" stepKey="editUrlRewrite"> <argument name="requestPath" value="$$category.custom_attributes[url_key]$$.html"/> </actionGroup> <!-- Open UrlRewrite Edit page and update the fields --> - <actionGroup ref="AdminUpdateUrlRewrite" stepKey="updateCategoryUrlRewrite"> + <actionGroup ref="AdminUpdateUrlRewriteActionGroup" stepKey="updateCategoryUrlRewrite"> <argument name="storeValue" value="Default Store View"/> <argument name="requestPath" value="{{updateUrlRewrite.request_path}}"/> <argument name="redirectTypeValue" value="Temporary (302)"/> @@ -40,20 +40,20 @@ </actionGroup> <!-- Open Category Page and Get Category ID --> - <actionGroup ref="OpenCategoryFromCategoryTree" stepKey="getCategoryId"> + <actionGroup ref="OpenCategoryFromCategoryTreeActionGroup" stepKey="getCategoryId"> <argument name="category" value="$$category.name$$"/> </actionGroup> <grabFromCurrentUrl stepKey="categoryId" regex="#\/([0-9]*)?\/$#"/> <!--Assert category Url Rewrite in grid --> - <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByNewRequestPath"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="searchByNewRequestPath"> <argument name="redirectPath" value="{{updateUrlRewrite.request_path}}" /> <argument name="redirectType" value="Temporary (302)" /> <argument name="targetPath" value="catalog/category/view/id/{$categoryId}"/> </actionGroup> <!-- Assert category redirect in Store Front --> - <actionGroup ref="AssertStorefrontUrlRewriteRedirect" stepKey="verifyCategoryInStoreFront"> + <actionGroup ref="AssertStorefrontUrlRewriteRedirectActionGroup" stepKey="verifyCategoryInStoreFront"> <argument name="category" value="$$category.name$$"/> <argument name="newRequestPath" value="{{updateUrlRewrite.request_path}}"/> </actionGroup> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithNoRedirectTest.xml new file mode 100644 index 0000000000000..6467a5051631d --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithNoRedirectTest.xml @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUpdateCmsPageRewriteEntityWithNoRedirectTest"> + <annotations> + <stories value="Update CMS Page URL Redirect With No Redirect"/> + <title value="Update CMS Page URL Redirect With No Redirect"/> + <description value="Login as Admin and tried to update the created URL Rewrite for CMS page"/> + <group value="cMSContent"/> + <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="logout" stepKey="logout"/> + </after> + + <!--Create Custom Store View--> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createCustomStoreView"/> + + <!-- Open CMS Edit Page, Get the CMS ID and Modify Store View Option to All Store Views --> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage"> + <argument name="CMSPage" value="$$createCMSPage$$"/> + </actionGroup> + <grabFromCurrentUrl stepKey="cmsId" regex="#\/([0-9]*)?\/$#"/> + <actionGroup ref="AddStoreViewToCmsPageActionGroup" stepKey="updateStoreViewForCmsPage"> + <argument name="CMSPage" value="$$createCMSPage$$"/> + <argument name="storeViewName" value="All Store Views"/> + </actionGroup> + + <!--Create CMS Page URL Redirect--> + <actionGroup ref="AdminAddCustomUrlRewriteActionGroup" stepKey="addCustomUrlRewrite"> + <argument name="customUrlRewriteValue" value="Custom"/> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="created-new-cms-page"/> + <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> + + <!--Search created CMS page url rewrite in grid--> + <actionGroup ref="AdminSearchAndSelectUrlRewriteInGridActionGroup" stepKey="searchUrlRewrite"> + <argument name="requestPath" value="created-new-cms-page"/> + </actionGroup> + + <!-- Update URL Rewrite for CMS Page --> + <actionGroup ref="AdminUpdateUrlRewriteActionGroup" stepKey="updateUrlRewriteFirstAttempt"> + <argument name="storeValue" value="{{customStore.name}}"/> + <argument name="requestPath" value="newrequestpath"/> + <argument name="redirectTypeValue" value="No"/> + <argument name="description" value="test_description_custom_store"/> + </actionGroup> + + <!-- Assert Url Rewrite Save Message --> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="The URL Rewrite has been saved."/> + </actionGroup> + + <!-- Assert Url Rewrite Cms Page Redirect --> + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToHomepage"/> + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="storefrontSwitchToCustomStoreView"> + <argument name="storeView" value="customStore"/> + </actionGroup> + <actionGroup ref="NavigateToStorefrontForCreatedPageActionGroup" stepKey="navigateToTheStoreFront"> + <argument name="page" value="newrequestpath"/> + </actionGroup> + <actionGroup ref="AssertStoreFrontCMSPageActionGroup" stepKey="assertCMSPage"> + <argument name="cmsTitle" value="$$createCMSPage.title$$"/> + <argument name="cmsContent" value="$$createCMSPage.content$$"/> + <argument name="cmsContentHeading" value="$$createCMSPage.content_heading$$"/> + </actionGroup> + + <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteCustomUrlRewrite"> + <argument name="requestPath" value="newrequestpath"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteCustomStoreView"/> + + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithPermanentReirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithPermanentReirectTest.xml new file mode 100644 index 0000000000000..3bf278db8410a --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithPermanentReirectTest.xml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUpdateCmsPageRewriteEntityWithPermanentRedirectTest"> + <annotations> + <stories value="Update CMS Page URL Redirect With Permanent Redirect"/> + <title value="Update CMS Page URL Redirect With Permanent Redirect"/> + <description value="Login as Admin and tried to update the created URL Rewrite for CMS page"/> + <group value="cMSContent"/> + <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="logout" stepKey="logout"/> + </after> + + <!--Create Custom Store View--> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createCustomStoreView"/> + + <!-- Open CMS Edit Page, Get the CMS ID and Modify Store View Option to All Store Views --> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage"> + <argument name="CMSPage" value="$$createCMSPage$$"/> + </actionGroup> + <grabFromCurrentUrl stepKey="cmsId" regex="#\/([0-9]*)?\/$#"/> + <actionGroup ref="AddStoreViewToCmsPageActionGroup" stepKey="updateStoreViewForCmsPage"> + <argument name="CMSPage" value="$$createCMSPage$$"/> + <argument name="storeViewName" value="All Store Views"/> + </actionGroup> + + <!--Create CMS Page URL Redirect--> + <actionGroup ref="AdminAddCustomUrlRewriteActionGroup" stepKey="addCustomUrlRewrite"> + <argument name="customUrlRewriteValue" value="Custom"/> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="created-new-cms-page"/> + <argument name="redirectTypeValue" value="No"/> + <argument name="targetPath" value="cms/page/view/page_id/{$cmsId}"/> + <argument name="description" value="Created New CMS Page"/> + </actionGroup> + + <!--Search created CMS page url rewrite in grid--> + <actionGroup ref="AdminSearchAndSelectUrlRewriteInGridActionGroup" stepKey="searchUrlRewrite"> + <argument name="requestPath" value="created-new-cms-page"/> + </actionGroup> + + <!-- Update URL Rewrite for CMS Page --> + <actionGroup ref="AdminUpdateUrlRewriteActionGroup" stepKey="updateUrlRewrite"> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="permanentrequestpath.htm"/> + <argument name="redirectTypeValue" value="Permanent (301)"/> + <argument name="description" value="test_description_301"/> + </actionGroup> + + <!-- Assert Url Rewrite Save Message --> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="The URL Rewrite has been saved."/> + </actionGroup> + + <!-- Assert Url Rewrite Cms Page Redirect --> + <actionGroup ref="NavigateToStorefrontForCreatedPageActionGroup" stepKey="navigateToTheStoreFront"> + <argument name="page" value="permanentrequestpath.htm"/> + </actionGroup> + <actionGroup ref="AssertStoreFrontCMSPageActionGroup" stepKey="assertCMSPage"> + <argument name="cmsTitle" value="$$createCMSPage.title$$"/> + <argument name="cmsContent" value="$$createCMSPage.content$$"/> + <argument name="cmsContentHeading" value="$$createCMSPage.content_heading$$"/> + </actionGroup> + + <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteCustomUrlRewrite"> + <argument name="requestPath" value="permanentrequestpath.htm"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteCustomStoreView"/> + + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithTemporaryRedirectTest.xml new file mode 100644 index 0000000000000..a7cadcdf753c3 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCmsPageRewriteEntityWithTemporaryRedirectTest.xml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUpdateCmsPageRewriteEntityWithTemporaryRedirectTest"> + <annotations> + <stories value="Update CMS Page URL Redirect With Temporary Redirect"/> + <title value="Update CMS Page URL Redirect With Temporary Redirect"/> + <description value="Login as Admin and tried to update the created URL Rewrite for CMS page"/> + <group value="cMSContent"/> + <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="logout" stepKey="logout"/> + </after> + + <!--Create Custom Store View--> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createCustomStoreView"/> + + <!-- Open CMS Edit Page, Get the CMS ID and Modify Store View Option to All Store Views --> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage"> + <argument name="CMSPage" value="$$createCMSPage$$"/> + </actionGroup> + <grabFromCurrentUrl stepKey="cmsId" regex="#\/([0-9]*)?\/$#"/> + <actionGroup ref="AddStoreViewToCmsPageActionGroup" stepKey="updateStoreViewForCmsPage"> + <argument name="CMSPage" value="$$createCMSPage$$"/> + <argument name="storeViewName" value="All Store Views"/> + </actionGroup> + + <!--Create CMS Page URL Redirect--> + <actionGroup ref="AdminAddCustomUrlRewriteActionGroup" stepKey="addCustomUrlRewrite"> + <argument name="customUrlRewriteValue" value="Custom"/> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="created-new-cms-page"/> + <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> + + <!--Search created CMS page url rewrite in grid--> + <actionGroup ref="AdminSearchAndSelectUrlRewriteInGridActionGroup" stepKey="searchUrlRewrite"> + <argument name="requestPath" value="created-new-cms-page"/> + </actionGroup> + + <!-- Update URL Rewrite for CMS Page --> + <actionGroup ref="AdminUpdateUrlRewriteActionGroup" stepKey="updateUrlRewrite"> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="temporaryrequestpath.html"/> + <argument name="redirectTypeValue" value="Temporary (302)"/> + <argument name="description" value="test description_302"/> + </actionGroup> + + <!-- Assert Url Rewrite Save Message --> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="The URL Rewrite has been saved."/> + </actionGroup> + + <!-- Assert Url Rewrite Cms Page Redirect --> + <actionGroup ref="NavigateToStorefrontForCreatedPageActionGroup" stepKey="navigateToTheStoreFront"> + <argument name="page" value="temporaryrequestpath.html"/> + </actionGroup> + <actionGroup ref="AssertStoreFrontCMSPageActionGroup" stepKey="assertCMSPage"> + <argument name="cmsTitle" value="$$createCMSPage.title$$"/> + <argument name="cmsContent" value="$$createCMSPage.content$$"/> + <argument name="cmsContentHeading" value="$$createCMSPage.content_heading$$"/> + </actionGroup> + + <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteCustomUrlRewrite"> + <argument name="requestPath" value="temporaryrequestpath.html"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteCustomStoreView"/> + + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesPermanentTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesPermanentTest.xml index 8339eb63abef1..a07f7b8c0fe60 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesPermanentTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesPermanentTest.xml @@ -23,19 +23,19 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> </before> <after> - <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteCustomUrlRewrite"> + <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteCustomUrlRewrite"> <argument name="requestPath" value="{{customPermanentUrlRewrite.request_path}}"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> </after> <!--Search default custom url rewrite in grid--> - <actionGroup ref="AdminSearchAndSelectUrlRewriteInGrid" stepKey="searchUrlRewrite"> + <actionGroup ref="AdminSearchAndSelectUrlRewriteInGridActionGroup" stepKey="searchUrlRewrite"> <argument name="requestPath" value="$$urlRewrite.request_path$$"/> </actionGroup> <!--Update default custom url rewrite as per requirement and verify AssertUrlRewriteSaveMessage--> - <actionGroup ref="AdminUpdateCustomUrlRewrite" stepKey="updateUrlRewrite"> + <actionGroup ref="AdminUpdateCustomUrlRewriteActionGroup" stepKey="updateUrlRewrite"> <argument name="storeValue" value="Default Store View"/> <argument name="requestPath" value="{{customPermanentUrlRewrite.request_path}}"/> <argument name="targetPath" value="{{customPermanentUrlRewrite.target_path}}"/> @@ -44,7 +44,7 @@ </actionGroup> <!--Search and verify updated AssertUrlRewriteInGrid--> - <actionGroup ref="AdminSearchByRequestPath" stepKey="verifyUpdatedUrlRewriteInGrid"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="verifyUpdatedUrlRewriteInGrid"> <argument name="redirectPath" value="{{customPermanentUrlRewrite.request_path}}"/> <argument name="redirectType" value="{{customPermanentUrlRewrite.redirect_type_label}}"/> <argument name="targetPath" value="{{customPermanentUrlRewrite.target_path}}"/> @@ -55,4 +55,4 @@ <waitForPageLoad stepKey="waitForPageLoad"/> <seeInCurrentUrl url="{{customPermanentUrlRewrite.target_path}}" stepKey="seeAssertUrlRewrite"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesTemporaryTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesTemporaryTest.xml index 07d578cbbeca4..dcf923a34081b 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesTemporaryTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesTemporaryTest.xml @@ -25,25 +25,25 @@ </before> <after> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> - <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteCustomUrlRewrite"> + <actionGroup ref="AdminDeleteUrlRewriteActionGroup" stepKey="deleteCustomUrlRewrite"> <argument name="requestPath" value="{{customTemporaryUrlRewrite.request_path}}"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> </after> <!--Filter Product in product page and get the Product ID --> - <actionGroup ref="filterAndSelectProduct" stepKey="filterProduct"> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="filterProduct"> <argument name="productSku" value="$$createProduct.sku$$"/> </actionGroup> <grabFromCurrentUrl stepKey="productId" regex="#\/([0-9]*)?\/$#"/> <!--Search default custom url rewrite in grid--> - <actionGroup ref="AdminSearchAndSelectUrlRewriteInGrid" stepKey="searchUrlRewrite"> + <actionGroup ref="AdminSearchAndSelectUrlRewriteInGridActionGroup" stepKey="searchUrlRewrite"> <argument name="requestPath" value="$$urlRewrite.request_path$$"/> </actionGroup> <!--Update default custom url rewrite as per requirement and verify AssertUrlRewriteSaveMessage--> - <actionGroup ref="AdminUpdateCustomUrlRewrite" stepKey="updateUrlRewrite"> + <actionGroup ref="AdminUpdateCustomUrlRewriteActionGroup" stepKey="updateUrlRewrite"> <argument name="storeValue" value="Default Store View"/> <argument name="requestPath" value="{{customTemporaryUrlRewrite.request_path}}"/> <argument name="targetPath" value="catalog/product/view/id/{$productId}"/> @@ -52,17 +52,17 @@ </actionGroup> <!--Search and verify AssertUrlRewriteInGrid--> - <actionGroup ref="AdminSearchByRequestPath" stepKey="verifyUpdatedUrlRewriteInGrid"> + <actionGroup ref="AdminSearchByRequestPathActionGroup" stepKey="verifyUpdatedUrlRewriteInGrid"> <argument name="redirectPath" value="{{customTemporaryUrlRewrite.request_path}}"/> <argument name="redirectType" value="{{customTemporaryUrlRewrite.redirect_type_label}}"/> <argument name="targetPath" value="catalog/product/view/id/{$productId}"/> </actionGroup> <!-- AssertUrlRewriteCustomSearchRedirect--> - <actionGroup ref="AssertStorefrontProductRedirect" stepKey="verifyProductInStoreFrontPage"> + <actionGroup ref="AssertStorefrontProductRedirectActionGroup" stepKey="verifyProductInStoreFrontPage"> <argument name="productName" value="$$createProduct.name$$"/> <argument name="productSku" value="$$createProduct.sku$$"/> <argument name="productRequestPath" value="$$createProduct.name$$.html"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml index ea370d8419583..74f3a60f35cea 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateProductUrlRewriteAndAddTemporaryRedirectTest.xml @@ -27,12 +27,12 @@ </after> <!-- Search and Select Edit option for created product in grid --> - <actionGroup ref="AdminSearchAndSelectUrlRewriteInGrid" stepKey="editUrlRewrite"> + <actionGroup ref="AdminSearchAndSelectUrlRewriteInGridActionGroup" stepKey="editUrlRewrite"> <argument name="requestPath" value="$$createProduct.name$$"/> </actionGroup> <!-- Open UrlRewrite Edit page and update the fields --> - <actionGroup ref="AdminUpdateUrlRewrite" stepKey="updateCategoryUrlRewrite"> + <actionGroup ref="AdminUpdateUrlRewriteActionGroup" stepKey="updateCategoryUrlRewrite"> <argument name="storeValue" value="{{updateUrlRewrite.store}}"/> <argument name="requestPath" value="{{updateUrlRewrite.request_path}}"/> <argument name="redirectTypeValue" value="{{updateUrlRewrite.redirect_type_label}}"/> @@ -40,7 +40,7 @@ </actionGroup> <!-- Assert product Url Rewrite in StoreFront --> - <actionGroup ref="AssertStorefrontProductRedirect" stepKey="assertProductUrlRewriteInStoreFront"> + <actionGroup ref="AssertStorefrontProductRedirectActionGroup" stepKey="assertProductUrlRewriteInStoreFront"> <argument name="productName" value="$$createProduct.name$$"/> <argument name="productSku" value="$$createProduct.sku$$"/> <argument name="productRequestPath" value="{{updateUrlRewrite.request_path}}"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml index b708b63599173..dc82a3e4ab24f 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml @@ -54,7 +54,7 @@ <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue4"/> <!-- 3. Edit Category 1 for DEFAULT Store View: --> - <actionGroup ref="switchCategoryStoreView" stepKey="switchStoreView"> + <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchStoreView"> <argument name="Store" value="_defaultStore.name"/> <argument name="CatName" value="$$simpleSubCategory1.name$$"/> </actionGroup> @@ -137,7 +137,7 @@ <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue4"/> <!-- 3. Edit Category 1 for DEFAULT Store View: --> - <actionGroup ref="switchCategoryStoreView" stepKey="switchStoreView"> + <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchStoreView"> <argument name="Store" value="_defaultStore.name"/> <argument name="CatName" value="$$simpleSubCategory1.name$$"/> </actionGroup> @@ -200,7 +200,7 @@ </before> <remove keyForRemoval="switchStoreView"/> <!-- 3. Edit Category 1 for All store view: --> - <actionGroup ref="navigateToCreatedCategory" stepKey="goToCategoryPage" after="seeValue4"> + <actionGroup ref="NavigateToCreatedCategoryActionGroup" stepKey="goToCategoryPage" after="seeValue4"> <argument name="Category" value="$$simpleSubCategory1$$"/> </actionGroup> <remove keyForRemoval="uncheckRedirect2"/> @@ -227,7 +227,7 @@ </before> <remove keyForRemoval="switchStoreView"/> <!-- 3. Edit Category 1 for All store view: --> - <actionGroup ref="navigateToCreatedCategory" stepKey="goToCategoryPage" after="seeValue4"> + <actionGroup ref="NavigateToCreatedCategoryActionGroup" stepKey="goToCategoryPage" after="seeValue4"> <argument name="Category" value="$$simpleSubCategory1$$"/> </actionGroup> <remove keyForRemoval="uncheckRedirect2"/> diff --git a/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php b/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php index 6eea8b962bf9f..c67f3f400b007 100644 --- a/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php +++ b/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php @@ -260,6 +260,7 @@ public function testNoRewriteAfterStoreSwitcherWhenOldRewriteEqualsToNewOne() */ public function testMatchWithRedirect() { + $queryParams = []; $this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($this->store)); $urlRewrite = $this->getMockBuilder(UrlRewrite::class) ->disableOriginalConstructor()->getMock(); @@ -268,7 +269,11 @@ public function testMatchWithRedirect() $this->urlFinder->expects($this->any())->method('findOneByData')->will($this->returnValue($urlRewrite)); $this->response->expects($this->once())->method('setRedirect') ->with('new-target-path', 'redirect-code'); - $this->url->expects($this->once())->method('getUrl')->with('', ['_direct' => 'target-path']) + $this->request->expects($this->once())->method('getParams')->willReturn($queryParams); + $this->url->expects($this->once())->method('getUrl')->with( + '', + ['_direct' => 'target-path', '_query' => $queryParams] + ) ->will($this->returnValue('new-target-path')); $this->request->expects($this->once())->method('setDispatched')->with(true); $this->actionFactory->expects($this->once())->method('create') @@ -282,6 +287,7 @@ public function testMatchWithRedirect() */ public function testMatchWithCustomInternalRedirect() { + $queryParams = []; $this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($this->store)); $urlRewrite = $this->getMockBuilder(UrlRewrite::class) ->disableOriginalConstructor()->getMock(); @@ -289,8 +295,12 @@ public function testMatchWithCustomInternalRedirect() $urlRewrite->expects($this->any())->method('getRedirectType')->will($this->returnValue('redirect-code')); $urlRewrite->expects($this->any())->method('getTargetPath')->will($this->returnValue('target-path')); $this->urlFinder->expects($this->any())->method('findOneByData')->will($this->returnValue($urlRewrite)); + $this->request->expects($this->any())->method('getParams')->willReturn($queryParams); $this->response->expects($this->once())->method('setRedirect')->with('a', 'redirect-code'); - $this->url->expects($this->once())->method('getUrl')->with('', ['_direct' => 'target-path'])->willReturn('a'); + $this->url->expects($this->once())->method('getUrl')->with( + '', + ['_direct' => 'target-path', '_query' => $queryParams] + )->willReturn('a'); $this->request->expects($this->once())->method('setDispatched')->with(true); $this->actionFactory->expects($this->once())->method('create') ->with(\Magento\Framework\App\Action\Redirect::class); @@ -312,6 +322,7 @@ public function testMatchWithCustomExternalRedirect($targetPath) $urlRewrite->expects($this->any())->method('getTargetPath')->will($this->returnValue($targetPath)); $this->urlFinder->expects($this->any())->method('findOneByData')->will($this->returnValue($urlRewrite)); $this->response->expects($this->once())->method('setRedirect')->with($targetPath, 'redirect-code'); + $this->request->expects($this->never())->method('getParams'); $this->url->expects($this->never())->method('getUrl'); $this->request->expects($this->once())->method('setDispatched')->with(true); $this->actionFactory->expects($this->once())->method('create') @@ -351,54 +362,4 @@ public function testMatch() $this->router->match($this->request); } - - /** - * Test to match corresponding URL Rewrite on request with query params - * - * @param string $originalRequestPath - * @param string $requestPath - * @param int $countOfQueryParams - * @dataProvider matchWithQueryParamsDataProvider - */ - public function testMatchWithQueryParams(string $originalRequestPath, string $requestPath, int $countOfQueryParams) - { - $targetPath = 'target-path'; - - $this->storeManager->method('getStore')->willReturn($this->store); - $urlRewrite = $this->createMock(UrlRewrite::class); - $urlRewrite->method('getRedirectType')->willReturn(0); - $urlRewrite->method('getTargetPath')->willReturn($targetPath); - $urlRewrite->method('getRequestPath')->willReturn($requestPath); - $this->urlFinder->method('findOneByData') - ->with([UrlRewrite::REQUEST_PATH => $requestPath, UrlRewrite::STORE_ID => $this->store->getId()]) - ->willReturn($urlRewrite); - - $this->requestQuery->method('count')->willReturn($countOfQueryParams); - $this->request->method('getPathInfo') - ->willReturn($originalRequestPath); - $this->request->expects($this->once()) - ->method('setPathInfo') - ->with('/' . $targetPath); - $this->request->expects($this->once()) - ->method('setAlias') - ->with(UrlInterface::REWRITE_REQUEST_PATH_ALIAS, $requestPath); - $this->actionFactory->expects($this->once()) - ->method('create') - ->with(Forward::class); - - $this->router->match($this->request); - } - - /** - * Data provider for Test to match corresponding URL Rewrite on request with query params - * - * @return array - */ - public function matchWithQueryParamsDataProvider(): array - { - return [ - ['/category.html/', 'category.html/', 0], - ['/category.html/', 'category.html', 1], - ]; - } } diff --git a/app/code/Magento/UrlRewrite/etc/db_schema.xml b/app/code/Magento/UrlRewrite/etc/db_schema.xml index 06d4949e63d9a..93e84d8e02a0f 100644 --- a/app/code/Magento/UrlRewrite/etc/db_schema.xml +++ b/app/code/Magento/UrlRewrite/etc/db_schema.xml @@ -37,5 +37,8 @@ <column name="store_id"/> <column name="entity_id"/> </index> + <index referenceId="URL_REWRITE_ENTITY_ID" indexType="btree"> + <column name="entity_id"/> + </index> </table> </schema> diff --git a/app/code/Magento/UrlRewrite/etc/db_schema_whitelist.json b/app/code/Magento/UrlRewrite/etc/db_schema_whitelist.json index bdaed647587a6..658673959a734 100644 --- a/app/code/Magento/UrlRewrite/etc/db_schema_whitelist.json +++ b/app/code/Magento/UrlRewrite/etc/db_schema_whitelist.json @@ -14,11 +14,12 @@ }, "index": { "URL_REWRITE_TARGET_PATH": true, - "URL_REWRITE_STORE_ID_ENTITY_ID": true + "URL_REWRITE_STORE_ID_ENTITY_ID": true, + "URL_REWRITE_ENTITY_ID": true }, "constraint": { "PRIMARY": true, "URL_REWRITE_REQUEST_PATH_STORE_ID": true } } -} \ No newline at end of file +} diff --git a/app/code/Magento/UrlRewrite/registration.php b/app/code/Magento/UrlRewrite/registration.php index 225a51f517569..87a28d518fab3 100644 --- a/app/code/Magento/UrlRewrite/registration.php +++ b/app/code/Magento/UrlRewrite/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_UrlRewrite', __DIR__); diff --git a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php index fb7bbd634d11f..55cd505928f42 100644 --- a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php +++ b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php @@ -11,9 +11,11 @@ use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; -use Magento\Framework\Model\AbstractModel; use Magento\UrlRewrite\Model\UrlFinderInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite as UrlRewriteDTO; +use Magento\Framework\Model\AbstractModel; +use Magento\Framework\EntityManager\TypeResolver; +use Magento\Framework\EntityManager\MetadataPool; /** * Returns URL rewrites list for the specified product @@ -25,13 +27,37 @@ class UrlRewrite implements ResolverInterface */ private $urlFinder; + /** + * @var array + */ + private $entityTypeMapping; + + /** + * @var MetadataPool + */ + private $metadataPool; + + /** + * @var TypeResolver + */ + private $typeResolver; + /** * @param UrlFinderInterface $urlFinder + * @param TypeResolver $typeResolver + * @param MetadataPool $metadataPool + * @param array $entityTypeMapping */ public function __construct( - UrlFinderInterface $urlFinder + UrlFinderInterface $urlFinder, + TypeResolver $typeResolver, + MetadataPool $metadataPool, + array $entityTypeMapping = [] ) { $this->urlFinder = $urlFinder; + $this->typeResolver = $typeResolver; + $this->metadataPool = $metadataPool; + $this->entityTypeMapping = $entityTypeMapping; } /** @@ -48,11 +74,24 @@ public function resolve( throw new LocalizedException(__('"model" value should be specified')); } - /** @var AbstractModel $entity */ + /** @var AbstractModel $entity */ $entity = $value['model']; $entityId = $entity->getEntityId(); - $urlRewriteCollection = $this->urlFinder->findAllByData([UrlRewriteDTO::ENTITY_ID => $entityId]); + $resolveEntityType = $this->typeResolver->resolve($entity); + $metadata = $this->metadataPool->getMetadata($resolveEntityType); + $entityType = $this->getEntityType($metadata->getEavEntityType()); + + $storeId = (int)$context->getExtensionAttributes()->getStore()->getId(); + + $data = [ + UrlRewriteDTO::ENTITY_TYPE => $entityType, + UrlRewriteDTO::ENTITY_ID => $entityId, + UrlRewriteDTO::STORE_ID => $storeId + ]; + + $urlRewriteCollection = $this->urlFinder->findAllByData($data); + $urlRewrites = []; /** @var UrlRewriteDTO $urlRewrite */ @@ -80,14 +119,32 @@ private function getUrlParameters(string $targetPath): array { $urlParameters = []; $targetPathParts = explode('/', trim($targetPath, '/')); + $count = count($targetPathParts) - 1; - for ($i = 3; ($i < sizeof($targetPathParts) - 1); $i += 2) { + /** $index starts from 3 to eliminate catalog/product/view/ part and fetch only name, + value data from from target path */ + //phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall + for ($index = 3; $index < $count; $index += 2) { $urlParameters[] = [ - 'name' => $targetPathParts[$i], - 'value' => $targetPathParts[$i + 1] + 'name' => $targetPathParts[$index], + 'value' => $targetPathParts[$index + 1] ]; } - return $urlParameters; } + + /** + * Get the entity type + * + * @param string $entityTypeMetadata + * @return string + */ + private function getEntityType(string $entityTypeMetadata) : string + { + $entityType = ''; + if ($entityTypeMetadata) { + $entityType = $this->entityTypeMapping[$entityTypeMetadata]; + } + return $entityType; + } } diff --git a/app/code/Magento/User/Block/Role/Tab/Info.php b/app/code/Magento/User/Block/Role/Tab/Info.php index 8a656efa97443..1b8b528aff774 100644 --- a/app/code/Magento/User/Block/Role/Tab/Info.php +++ b/app/code/Magento/User/Block/Role/Tab/Info.php @@ -6,7 +6,9 @@ namespace Magento\User\Block\Role\Tab; /** - * implementing now + * Info + * + * User role tab info * * @SuppressWarnings(PHPMD.DepthOfInheritance) */ @@ -18,6 +20,8 @@ class Info extends \Magento\Backend\Block\Widget\Form\Generic implements \Magent const IDENTITY_VERIFICATION_PASSWORD_FIELD = 'current_password'; /** + * Get tab label + * * @return \Magento\Framework\Phrase */ public function getTabLabel() @@ -26,6 +30,8 @@ public function getTabLabel() } /** + * Get tab title + * * @return string */ public function getTabTitle() @@ -34,6 +40,8 @@ public function getTabTitle() } /** + * Can show tab + * * @return bool */ public function canShowTab() @@ -42,6 +50,8 @@ public function canShowTab() } /** + * Is tab hidden + * * @return bool */ public function isHidden() @@ -50,6 +60,8 @@ public function isHidden() } /** + * Before html rendering + * * @return $this */ public function _beforeToHtml() @@ -60,6 +72,8 @@ public function _beforeToHtml() } /** + * Form initializatiion + * * @return void */ protected function _initForm() @@ -99,7 +113,7 @@ protected function _initForm() 'label' => __('Your Password'), 'id' => self::IDENTITY_VERIFICATION_PASSWORD_FIELD, 'title' => __('Your Password'), - 'class' => 'input-text validate-current-password required-entry', + 'class' => 'validate-current-password required-entry', 'required' => true ] ); diff --git a/app/code/Magento/User/Block/User/Edit/Tab/Main.php b/app/code/Magento/User/Block/User/Edit/Tab/Main.php index 27e00483733d0..3182393db8eaf 100644 --- a/app/code/Magento/User/Block/User/Edit/Tab/Main.php +++ b/app/code/Magento/User/Block/User/Edit/Tab/Main.php @@ -184,7 +184,7 @@ protected function _prepareForm() 'label' => __('Your Password'), 'id' => self::CURRENT_USER_PASSWORD_FIELD, 'title' => __('Your Password'), - 'class' => 'input-text validate-current-password required-entry', + 'class' => 'validate-current-password required-entry', 'required' => true ] ); diff --git a/app/code/Magento/User/Model/Backend/Config/ObserverConfig.php b/app/code/Magento/User/Model/Backend/Config/ObserverConfig.php index 6d921dfdcdd65..93b5389bc3608 100644 --- a/app/code/Magento/User/Model/Backend/Config/ObserverConfig.php +++ b/app/code/Magento/User/Model/Backend/Config/ObserverConfig.php @@ -4,13 +4,37 @@ * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\User\Model\Backend\Config; /** * User backend observer helper class + * + * Class \Magento\User\Model\Backend\Config\ObserverConfig */ class ObserverConfig { + /** + * Config path for lockout threshold + */ + private const XML_ADMIN_SECURITY_LOCKOUT_THRESHOLD = 'admin/security/lockout_threshold'; + + /** + * Config path for password change is forced or not + */ + private const XML_ADMIN_SECURITY_PASSWORD_IS_FORCED = 'admin/security/password_is_forced'; + + /** + * Config path for password lifetime + */ + private const XML_ADMIN_SECURITY_PASSWORD_LIFETIME = 'admin/security/password_lifetime'; + + /** + * Config path for maximum lockout failures + */ + private const XML_ADMIN_SECURITY_LOCKOUT_FAILURES = 'admin/security/lockout_failures'; + /** * Backend configuration interface * @@ -19,6 +43,8 @@ class ObserverConfig protected $backendConfig; /** + * Constructor + * * @param \Magento\Backend\App\ConfigInterface $backendConfig */ public function __construct( @@ -44,11 +70,12 @@ public function _isLatestPasswordExpired($latestPassword) /** * Get admin lock threshold from configuration + * * @return int */ public function getAdminLockThreshold() { - return 60 * (int)$this->backendConfig->getValue('admin/security/lockout_threshold'); + return 60 * (int)$this->backendConfig->getValue(self::XML_ADMIN_SECURITY_LOCKOUT_THRESHOLD); } /** @@ -58,7 +85,7 @@ public function getAdminLockThreshold() */ public function isPasswordChangeForced() { - return (bool)(int)$this->backendConfig->getValue('admin/security/password_is_forced'); + return (bool)(int)$this->backendConfig->getValue(self::XML_ADMIN_SECURITY_PASSWORD_IS_FORCED); } /** @@ -68,7 +95,7 @@ public function isPasswordChangeForced() */ public function getAdminPasswordLifetime() { - return 86400 * (int)$this->backendConfig->getValue('admin/security/password_lifetime'); + return 86400 * (int)$this->backendConfig->getValue(self::XML_ADMIN_SECURITY_PASSWORD_LIFETIME); } /** @@ -78,6 +105,6 @@ public function getAdminPasswordLifetime() */ public function getMaxFailures() { - return (int)$this->backendConfig->getValue('admin/security/lockout_failures'); + return (int)$this->backendConfig->getValue(self::XML_ADMIN_SECURITY_LOCKOUT_FAILURES); } } diff --git a/app/code/Magento/User/Model/User.php b/app/code/Magento/User/Model/User.php index 0c59f165f11f0..3b41d529b542b 100644 --- a/app/code/Magento/User/Model/User.php +++ b/app/code/Magento/User/Model/User.php @@ -216,9 +216,6 @@ protected function _construct() * Removing dependencies and leaving only entity's properties. * * @return string[] - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __sleep() { @@ -247,9 +244,6 @@ public function __sleep() * Restoring required objects after serialization. * * @return void - * - * @SuppressWarnings(PHPMD.SerializationAware) - * @deprecated Do not use PHP serialization. */ public function __wakeup() { @@ -419,10 +413,6 @@ public function getRoles() */ public function getRole() { - if ($this->getData('extracted_role')) { - $this->_role = $this->getData('extracted_role'); - $this->unsetData('extracted_role'); - } if (null === $this->_role) { $this->_role = $this->_roleFactory->create(); $roles = $this->getRoles(); diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminAddNewUserRoleActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminAddNewUserRoleActionGroup.xml index 76a83f5d5a5aa..28cbaba4b8241 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminAddNewUserRoleActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminAddNewUserRoleActionGroup.xml @@ -36,13 +36,6 @@ <selectOption userInput="{{role.resourceAccess}}" selector="{{AdminCreateRoleSection.roleResourceNew}}" stepKey="selectResourceAccess"/> <click selector="{{AdminCreateRoleSection.save}}" stepKey="saveUserRole"/> <waitForPageLoad stepKey="waitForSaving"/> - <see userInput="You saved the role." selector="{{AdminMessagesSection.successMessage}}" stepKey="seeMessage"/> - </actionGroup> - - <actionGroup name="AdminAddNewUserRoleWithCustomRoleScopes" extends="AdminAddNewUserRoleActionGroup"> - <arguments> - <argument name="customWebsiteName" type="string"/> - </arguments> - <checkOption selector="{{AdminCreateRoleSection.selectWebsite(customWebsiteName)}}" stepKey="checkWebsite" after="selectRoleResources"/> + <see userInput="You saved the role." selector="{{AdminMessagesSection.success}}" stepKey="seeMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminAddNewUserRoleWithCustomRoleScopesActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminAddNewUserRoleWithCustomRoleScopesActionGroup.xml new file mode 100644 index 0000000000000..142ed8af09fcf --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminAddNewUserRoleWithCustomRoleScopesActionGroup.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="AdminAddNewUserRoleWithCustomRoleScopesActionGroup" extends="AdminAddNewUserRoleActionGroup"> + <arguments> + <argument name="customWebsiteName" type="string"/> + </arguments> + <checkOption selector="{{AdminCreateRoleSection.selectWebsite(customWebsiteName)}}" stepKey="checkWebsite" after="selectRoleResources"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminAddRestrictedRoleActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminAddRestrictedRoleActionGroup.xml new file mode 100644 index 0000000000000..37f06b8c14ad8 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminAddRestrictedRoleActionGroup.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="AdminAddRestrictedRoleActionGroup" extends="AdminCreateRoleActionGroup"> + <remove keyForRemoval="navigateToNewRole"/> + <remove keyForRemoval="waitForPageLoad1"/> + <remove keyForRemoval="fillRoleName"/> + <remove keyForRemoval="enterPassword"/> + <remove keyForRemoval="clickRoleResourcesTab"/> + <remove keyForRemoval="waitForScopeSelection"/> + <remove keyForRemoval="clickSaveRoleButton"/> + <remove keyForRemoval="waitForPageLoad2"/> + <scrollTo selector="{{AdminEditRoleInfoSection.blockName('restrictedRole')}}" x="0" y="-100" stepKey="scrollToResourceElement" after="selectResourceAccessCustom"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateRoleActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateRoleActionGroup.xml index 9e8262d5564fb..15deee5fbd049 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateRoleActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateRoleActionGroup.xml @@ -29,48 +29,4 @@ <click selector="{{AdminEditRoleInfoSection.saveButton}}" stepKey="clickSaveRoleButton"/> <waitForPageLoad stepKey="waitForPageLoad2"/> </actionGroup> - - <actionGroup name="AdminFillUserRoleRequiredData" extends="AdminCreateRoleActionGroup"> - <remove keyForRemoval="clickRoleResourcesTab"/> - <remove keyForRemoval="waitForScopeSelection"/> - <remove keyForRemoval="selectResourceAccessCustom"/> - <remove keyForRemoval="waitForElementVisible"/> - <remove keyForRemoval="clickContentBlockCheckbox"/> - <remove keyForRemoval="clickSaveRoleButton"/> - <remove keyForRemoval="waitForPageLoad2"/> - </actionGroup> - <actionGroup name="AdminAddRestrictedRole" extends="AdminCreateRoleActionGroup"> - <remove keyForRemoval="navigateToNewRole"/> - <remove keyForRemoval="waitForPageLoad1"/> - <remove keyForRemoval="fillRoleName"/> - <remove keyForRemoval="enterPassword"/> - <remove keyForRemoval="clickRoleResourcesTab"/> - <remove keyForRemoval="waitForScopeSelection"/> - <remove keyForRemoval="clickSaveRoleButton"/> - <remove keyForRemoval="waitForPageLoad2"/> - <scrollTo selector="{{AdminEditRoleInfoSection.blockName('restrictedRole')}}" x="0" y="-100" stepKey="scrollToResourceElement" after="selectResourceAccessCustom"/> - </actionGroup> - - <!--Create new role--> - <actionGroup name="AdminCreateRole"> - <annotations> - <description>Clicks on 'Add New Role'. Fills in the provided details (Role, Resource, Scope and Websites). Clicks on Save. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <argument name="role" type="string" defaultValue=""/> - <argument name="resource" type="string" defaultValue="All"/> - <argument name="scope" type="string" defaultValue="Custom"/> - <argument name="websites" type="string" defaultValue="Main Website"/> - </arguments> - - <click selector="{{AdminCreateRoleSection.create}}" stepKey="clickToAddNewRole"/> - <fillField selector="{{AdminCreateRoleSection.name}}" userInput="{{role.name}}" stepKey="setRoleName"/> - <fillField stepKey="setPassword" selector="{{AdminCreateRoleSection.password}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> - <click selector="{{AdminCreateRoleSection.roleResources}}" stepKey="clickToOpenRoleResources"/> - <waitForPageLoad stepKey="waitForRoleResourcePage" time="5"/> - <click stepKey="checkSales" selector="//a[text()='Sales']"/> - <click selector="{{AdminCreateRoleSection.save}}" stepKey="clickToSaveRole"/> - <waitForPageLoad stepKey="waitForPageLoad" time="10"/> - <see userInput="You saved the role." stepKey="seeSuccessMessage"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml index 2738ef9f1cb3f..8abb4e9224b0a 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserActionGroup.xml @@ -37,34 +37,4 @@ <waitForPageLoad stepKey="waitForPageLoad2"/> <see userInput="You saved the user." stepKey="seeSuccessMessage"/> </actionGroup> - - <!--Create new user with role--> - <actionGroup name="AdminCreateUserWithRoleActionGroup"> - <annotations> - <description>Goes to the Admin Users grid page. Clicks on Create User. Fills in the provided Role and User.</description> - </annotations> - <arguments> - <argument name="role"/> - <argument name="user" defaultValue="newAdmin"/> - </arguments> - - <amOnPage url="{{AdminNewUserPage.url}}" stepKey="navigateToNewUser"/> - <waitForPageLoad stepKey="waitForUsersPage"/> - <fillField selector="{{AdminCreateUserSection.usernameTextField}}" userInput="{{user.username}}" stepKey="enterUserName"/> - <fillField selector="{{AdminCreateUserSection.firstNameTextField}}" userInput="{{user.firstName}}" stepKey="enterFirstName"/> - <fillField selector="{{AdminCreateUserSection.lastNameTextField}}" userInput="{{user.lastName}}" stepKey="enterLastName"/> - <fillField selector="{{AdminCreateUserSection.emailTextField}}" userInput="{{user.username}}@magento.com" stepKey="enterEmail"/> - <fillField selector="{{AdminCreateUserSection.passwordTextField}}" userInput="{{user.password}}" stepKey="enterPassword"/> - <fillField selector="{{AdminCreateUserSection.pwConfirmationTextField}}" userInput="{{user.password}}" stepKey="confirmPassword"/> - <fillField selector="{{AdminCreateUserSection.currentPasswordField}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterCurrentPassword"/> - <scrollToTopOfPage stepKey="scrollToTopOfPage"/> - <click stepKey="clickUserRole" selector="{{AdminCreateUserSection.userRoleTab}}"/> - <click stepKey="chooseRole" selector="{{AdminStoreSection.createdRoleInUserPage(role.name)}}"/> - <click selector="{{AdminCreateUserSection.saveButton}}" stepKey="clickSaveUser"/> - <waitForPageLoad stepKey="waitForSaveTheUser"/> - <see userInput="You saved the user." stepKey="seeSuccessMessage"/> - </actionGroup> - <actionGroup name="AdminCreateUserWithApiRoleActionGroup" extends="AdminCreateUserWithRoleActionGroup"> - <click stepKey="chooseRole" selector="{{AdminCreateUserSection.createdRoleInUserPage(role.rolename)}}"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithApiRoleActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithApiRoleActionGroup.xml new file mode 100644 index 0000000000000..d3f7278765d47 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithApiRoleActionGroup.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="AdminCreateUserWithApiRoleActionGroup" extends="AdminCreateUserWithRoleActionGroup"> + <click stepKey="chooseRole" selector="{{AdminCreateUserSection.createdRoleInUserPage(role.rolename)}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithRoleActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithRoleActionGroup.xml new file mode 100644 index 0000000000000..dd4f181204040 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithRoleActionGroup.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="AdminCreateUserWithRoleActionGroup"> + <annotations> + <description>Goes to the Admin Users grid page. Clicks on Create User. Fills in the provided Role and User.</description> + </annotations> + <arguments> + <argument name="role"/> + <argument name="user" defaultValue="newAdmin"/> + </arguments> + + <amOnPage url="{{AdminNewUserPage.url}}" stepKey="navigateToNewUser"/> + <waitForPageLoad stepKey="waitForUsersPage"/> + <fillField selector="{{AdminCreateUserSection.usernameTextField}}" userInput="{{user.username}}" stepKey="enterUserName"/> + <fillField selector="{{AdminCreateUserSection.firstNameTextField}}" userInput="{{user.firstName}}" stepKey="enterFirstName"/> + <fillField selector="{{AdminCreateUserSection.lastNameTextField}}" userInput="{{user.lastName}}" stepKey="enterLastName"/> + <fillField selector="{{AdminCreateUserSection.emailTextField}}" userInput="{{user.username}}@magento.com" stepKey="enterEmail"/> + <fillField selector="{{AdminCreateUserSection.passwordTextField}}" userInput="{{user.password}}" stepKey="enterPassword"/> + <fillField selector="{{AdminCreateUserSection.pwConfirmationTextField}}" userInput="{{user.password}}" stepKey="confirmPassword"/> + <fillField selector="{{AdminCreateUserSection.currentPasswordField}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterCurrentPassword"/> + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> + <click stepKey="clickUserRole" selector="{{AdminCreateUserSection.userRoleTab}}"/> + <click stepKey="chooseRole" selector="{{AdminStoreSection.createdRoleInUserPage(role.name)}}"/> + <click selector="{{AdminCreateUserSection.saveButton}}" stepKey="clickSaveUser"/> + <waitForPageLoad stepKey="waitForSaveTheUser"/> + <see userInput="You saved the user." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithRoleAndIsActiveActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithRoleAndIsActiveActionGroup.xml new file mode 100644 index 0000000000000..779dcbc512fd2 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminCreateUserWithRoleAndIsActiveActionGroup.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"> + <!--Create new user with role and active setting--> + <actionGroup name="AdminCreateUserWithRoleAndIsActiveActionGroup" extends="AdminCreateUserWithRoleActionGroup"> + <checkOption selector="{{AdminNewUserFormSection.userIsActive(user.is_active)}}" stepKey="checkIsActive" after="confirmPassword"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteCreatedRoleActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteCreatedRoleActionGroup.xml index c1659610feda5..813e22df227c8 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteCreatedRoleActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteCreatedRoleActionGroup.xml @@ -21,7 +21,4 @@ <waitForPageLoad stepKey="waitForPageLoad"/> <see stepKey="seeSuccessMessage" userInput="You deleted the role."/> </actionGroup> - <actionGroup name="AdminDeleteRoleByRoleNameActionGroup" extends="AdminDeleteCreatedRoleActionGroup"> - <click stepKey="clickToAddNewRole" selector="{{AdminDeleteRoleSection.role(role.rolename)}}"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteCreatedUserActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteCreatedUserActionGroup.xml index 6be5e80ec3e30..0939626660ad0 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteCreatedUserActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteCreatedUserActionGroup.xml @@ -15,7 +15,7 @@ <arguments> <argument name="user"/> </arguments> - + <amOnPage stepKey="amOnAdminUsersPage" url="{{AdminUsersPage.url}}"/> <click stepKey="openTheUser" selector="{{AdminDeleteUserSection.role(user.username)}}"/> <waitForPageLoad stepKey="waitForSingleUserPageToLoad"/> @@ -26,21 +26,4 @@ <click stepKey="clickToConfirm" selector="{{AdminDeleteUserSection.confirm}}"/> <see stepKey="seeDeleteMessageForUser" userInput="You deleted the user."/> </actionGroup> - <!--Delete User--> - <actionGroup name="AdminDeleteNewUserActionGroup"> - <annotations> - <description>Deletes a User that contains the name 'John'. PLEASE NOTE: The Action Group values are Hardcoded.</description> - </annotations> - <arguments> - <argument name="userName" type="string" defaultValue="John"/> - </arguments> - <click stepKey="clickOnUser" selector="{{AdminDeleteUserSection.theUser(userName)}}"/> - <fillField stepKey="typeCurrentPassword" selector="{{AdminDeleteUserSection.password}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> - <scrollToTopOfPage stepKey="scrollToTop"/> - <click stepKey="clickToDeleteUser" selector="{{AdminDeleteUserSection.delete}}"/> - <waitForPageLoad stepKey="waitForDeletePopupOpen" time="5"/> - <click stepKey="clickToConfirm" selector="{{AdminDeleteUserSection.confirm}}"/> - <waitForPageLoad stepKey="waitForPageLoad" time="10"/> - <see userInput="You deleted the user." stepKey="seeSuccessMessage" /> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteCustomUserActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteCustomUserActionGroup.xml new file mode 100644 index 0000000000000..598d95b8d562c --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteCustomUserActionGroup.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminDeleteCustomUserActionGroup"> + <annotations> + <description>Goes to the Admin Users grid page. Deletes the provided User. Validates that the Success Message is present and correct.</description> + </annotations> + <arguments> + <argument name="user"/> + </arguments> + + <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToUserGrid"/> + <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{user.username}}" stepKey="enterUserName"/> + <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearch"/> + <waitForPageLoad stepKey="waitForGridToLoad"/> + <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{user.username}}" stepKey="seeUser"/> + <click selector="{{AdminUserGridSection.searchResultFirstRow}}" stepKey="openUserEdit"/> + <waitForPageLoad stepKey="waitForUserEditPageLoad"/> + <fillField selector="{{AdminEditUserSection.currentPasswordField}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterThePassword"/> + <click selector="{{AdminMainActionsSection.delete}}" stepKey="deleteUser"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForConfirmModal"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> + <waitForPageLoad stepKey="waitForSave"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the user." stepKey="seeUserDeleteMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteNewUserActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteNewUserActionGroup.xml new file mode 100644 index 0000000000000..a4e5492f8e3e6 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteNewUserActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminDeleteNewUserActionGroup"> + <annotations> + <description>Deletes a User that contains the name 'John'. PLEASE NOTE: The Action Group values are Hardcoded.</description> + </annotations> + <arguments> + <argument name="userName" type="string" defaultValue="John"/> + </arguments> + <click stepKey="clickOnUser" selector="{{AdminDeleteUserSection.theUser(userName)}}"/> + <fillField stepKey="typeCurrentPassword" selector="{{AdminDeleteUserSection.password}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/> + <scrollToTopOfPage stepKey="scrollToTop"/> + <click stepKey="clickToDeleteUser" selector="{{AdminDeleteUserSection.delete}}"/> + <waitForPageLoad stepKey="waitForDeletePopupOpen" time="5"/> + <click stepKey="clickToConfirm" selector="{{AdminDeleteUserSection.confirm}}"/> + <waitForPageLoad stepKey="waitForPageLoad" time="10"/> + <see userInput="You deleted the user." stepKey="seeSuccessMessage" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteRoleByRoleNameActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteRoleByRoleNameActionGroup.xml new file mode 100644 index 0000000000000..82602c007586c --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteRoleByRoleNameActionGroup.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="AdminDeleteRoleByRoleNameActionGroup" extends="AdminDeleteCreatedRoleActionGroup"> + <click stepKey="clickToAddNewRole" selector="{{AdminDeleteRoleSection.role(role.rolename)}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteUserActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteUserActionGroup.xml index 084f60dcf8542..67075eb52a678 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteUserActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteUserActionGroup.xml @@ -27,27 +27,4 @@ <waitForPageLoad stepKey="waitForPageLoad"/> <see stepKey="seeDeleteMessageForUser" userInput="You deleted the user."/> </actionGroup> - - <actionGroup name="AdminDeleteCustomUserActionGroup"> - <annotations> - <description>Goes to the Admin Users grid page. Deletes the provided User. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <argument name="user"/> - </arguments> - - <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToUserGrid"/> - <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{user.username}}" stepKey="enterUserName"/> - <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearch"/> - <waitForPageLoad stepKey="waitForGridToLoad"/> - <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{user.username}}" stepKey="seeUser"/> - <click selector="{{AdminUserGridSection.searchResultFirstRow}}" stepKey="openUserEdit"/> - <waitForPageLoad stepKey="waitForUserEditPageLoad"/> - <fillField selector="{{AdminEditUserSection.currentPasswordField}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterThePassword"/> - <click selector="{{AdminMainActionsSection.delete}}" stepKey="deleteUser"/> - <waitForElementVisible selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForConfirmModal"/> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> - <waitForPageLoad stepKey="waitForSave"/> - <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the user." stepKey="seeUserDeleteMessage"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteUserRoleActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteUserRoleActionGroup.xml index d2c881b771973..84c9e26eed54c 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteUserRoleActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminDeleteUserRoleActionGroup.xml @@ -21,7 +21,7 @@ <click selector="{{AdminEditRoleInfoSection.deleteButton}}" stepKey="deleteUserRole"/> <waitForElementVisible selector="{{AdminConfirmationModalSection.message}}" stepKey="waitForConfirmModal"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> - <waitForElementVisible selector="{{AdminMessagesSection.successMessage}}" stepKey="waitSuccessMessage"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="You deleted the role." stepKey="seeUserRoleDeleteMessage"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the role." stepKey="seeUserRoleDeleteMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleFormActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleFormActionGroup.xml index 480695aa7a931..7b913382651ae 100644 --- a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleFormActionGroup.xml +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleFormActionGroup.xml @@ -17,13 +17,13 @@ <argument name="currentAdminPassword" type="string" defaultValue="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" /> </arguments> - <fillField selector="{{AdminCreateRoleSection.name}}" userInput="{{role.name}}" stepKey="fillRoleName"/> + <fillField selector="{{AdminCreateRoleSection.name}}" userInput="{{role.rolename}}" stepKey="fillRoleName"/> <fillField selector="{{AdminCreateRoleSection.password}}" userInput="{{currentAdminPassword}}" stepKey="fillCurrentUserPassword"/> <click selector="{{AdminCreateRoleSection.roleResources}}" stepKey="clickToOpenRoleResources"/> <waitForPageLoad stepKey="waitForRoleResourceTab" /> <selectOption userInput="{{role.resourceAccess}}" selector="{{AdminCreateRoleSection.resourceAccess}}" stepKey="selectResourceAccess" /> - <performOn stepKey="checkNeededResources" selector="{{AdminCreateRoleSection.resourceTree}}" function="function($I,$userRoles={{role.resources}}){foreach($userRoles as $userRole){$I->conditionalClick('//li[@data-id=\'' . $userRole . '\']//*[@class=\'jstree-checkbox\']','//li[@data-id=\'' . $userRole . '\' and contains(@class, \'jstree-checked\')]',false);}}" /> + <performOn stepKey="checkNeededResources" selector="{{AdminCreateRoleSection.resourceTree}}" function="function($I,$userRoles={{role.resource}}){foreach($userRoles as $userRole){$I->conditionalClick('//li[@data-id=\'' . $userRole . '\']//*[@class=\'jstree-checkbox\']','//li[@data-id=\'' . $userRole . '\' and contains(@class, \'jstree-checked\')]',false);}}" /> </actionGroup> </actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleRequiredDataActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleRequiredDataActionGroup.xml new file mode 100644 index 0000000000000..236db105fad14 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleRequiredDataActionGroup.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="AdminFillUserRoleRequiredDataActionGroup" extends="AdminCreateRoleActionGroup"> + <remove keyForRemoval="clickRoleResourcesTab"/> + <remove keyForRemoval="waitForScopeSelection"/> + <remove keyForRemoval="selectResourceAccessCustom"/> + <remove keyForRemoval="waitForElementVisible"/> + <remove keyForRemoval="clickContentBlockCheckbox"/> + <remove keyForRemoval="clickSaveRoleButton"/> + <remove keyForRemoval="waitForPageLoad2"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenAdminUsersPageActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenAdminUsersPageActionGroup.xml new file mode 100644 index 0000000000000..1c4a35eeff6cb --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenAdminUsersPageActionGroup.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="AdminOpenAdminUsersPageActionGroup"> + <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToAdminUsersGrid"/> + <waitForPageLoad stepKey="waitForAdminUsersPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUpdateUserRoleActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUpdateUserRoleActionGroup.xml new file mode 100644 index 0000000000000..9c632e70db55d --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminUpdateUserRoleActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminUpdateUserRoleActionGroup"> + <arguments> + <argument name="role" type="entity"/> + </arguments> + <fillField selector="{{AdminEditUserSection.currentPasswordField}}" userInput="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" stepKey="enterThePassword"/> + <scrollToTopOfPage stepKey="scrollToTop"/> + <waitForPageLoad stepKey="waitForPageScrollToTop" time="15"/> + <click selector="{{AdminNewUserFormSection.userRoleTab}}" stepKey="openUserRoleTab"/> + <waitForPageLoad stepKey="waitForUserRoleTabOpened" /> + <click selector="{{AdminNewUserFormSection.resetFilter}}" stepKey="resetGridFilter"/> + <waitForPageLoad stepKey="waitForFiltersReset"/> + <fillField selector="{{AdminNewUserFormSection.roleFilterField}}" userInput="{{role.name}}" stepKey="fillRoleFilterField"/> + <click selector="{{AdminNewUserFormSection.search}}" stepKey="clickSearchButton"/> + <waitForPageLoad stepKey="waitForFiltersApplied"/> + <checkOption selector="{{AdminNewUserFormSection.roleRadiobutton(role.name)}}" stepKey="assignRole"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.xml new file mode 100644 index 0000000000000..3499f4e0d951c --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertAdminUserIsInGridActionGroup.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="AssertAdminUserIsInGridActionGroup"> + <arguments> + <argument name="user" type="entity"/> + </arguments> + <click selector="{{AdminUserGridSection.resetButton}}" stepKey="resetGridFilter"/> + <waitForPageLoad stepKey="waitForFiltersReset" time="15"/> + <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{user.username}}" stepKey="enterUserName"/> + <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearch"/> + <waitForPageLoad stepKey="waitForGridToLoad" time="15"/> + <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{user.username}}" stepKey="seeUser"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.xml new file mode 100644 index 0000000000000..0747eab31588e --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AssertUserRoleRestrictedAccessActionGroup.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="AssertUserRoleRestrictedAccessActionGroup"> + <see selector="{{AdminHeaderSection.pageHeading}}" userInput="Sorry, you need permissions to view this content." stepKey="seeErrorMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/Data/UserData.xml b/app/code/Magento/User/Test/Mftf/Data/UserData.xml index d465851c62373..7947c8ee3c161 100644 --- a/app/code/Magento/User/Test/Mftf/Data/UserData.xml +++ b/app/code/Magento/User/Test/Mftf/Data/UserData.xml @@ -33,6 +33,12 @@ <item>1</item> </array> </entity> + <entity name="AdminUserWithUpdatedUserRoleToSales" extends="NewAdminUser"> + <data key="password">123123qA</data> + <data key="password_confirmation">123123qA</data> + <data key="role">{{roleSales.rolename}}</data> + </entity> + <entity name="EditAdminUser" type="user"> <data key="username" unique="suffix">admin</data> <data key="firstname">John</data> @@ -109,6 +115,36 @@ </array> </entity> + <entity name="activeAdmin" type="user" extends="NewAdminUser"> + <data key="name" unique="suffix">admin</data> + <data key="is_active">1</data> + </entity> + <entity name="inactiveAdmin" type="user" extends="NewAdminUser"> + <data key="name" unique="suffix">admin</data> + <data key="is_active">0</data> + </entity> + + <entity name="adminUserCorrectPassword" type="user"> + <data key="username">admin_user_with_correct_password</data> + <data key="firstname">John</data> + <data key="lastname">Doe</data> + <data key="email" unique="prefix">admin@example.com</data> + <data key="password">123123q</data> + <data key="password_confirmation">123123q</data> + <data key="interface_local">en_US</data> + <data key="interface_local_label">English (United States)</data> + <data key="is_active">true</data> + <data key="is_active_label">Active</data> + <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> + <data key="role">Administrators</data> + <array key="roles"> + <item>1</item> + </array> + </entity> + <entity name="adminUserIncorrectPassword" type="user"> + <data key="username">admin_user_with_correct_password</data> + <data key="password">123123123q</data> + </entity> <!-- Since User delete action is performed via POST request we created this entity to be able to delete it. Please use "AdminDeleteUserViaCurlActionGroup". diff --git a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml index a0d89bbf3fb9d..a39e6cf47c295 100644 --- a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml +++ b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml @@ -10,38 +10,49 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="adminRole" type="role"> <data key="name" unique="suffix">adminRole</data> + <data key="rolename" unique="suffix">adminRole</data> <data key="scope">1</data> <data key="access">1</data> </entity> - <entity name="roleAdministrator" type="role"> + <entity name="roleAdministrator" type="user_role"> <data key="name" unique="suffix">Administrator </data> + <data key="rolename" unique="suffix">Administrator </data> <data key="resourceAccess">All</data> - <data key="resources">[]</data> + <data key="all">1</data> + <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> + <data key="resource">[]</data> </entity> - <entity name="roleSales" type="role"> + <entity name="roleSales"> <data key="name" unique="suffix">Role Sales </data> + <data key="rolename" unique="suffix">Role Sales </data> <data key="resourceAccess">Custom</data> - <data key="resources">['Magento_Sales::sales','Magento_Sales::sales_operation','Magento_Sales::actions','Magento_Sales::sales_order','Magento_Sales::create','Magento_Sales::actions_view','Magento_Sales::email','Magento_Sales::reorder','Magento_Sales::actions_edit','Magento_Sales::cancel','Magento_Sales::review_payment','Magento_Sales::capture','Magento_Sales::invoice','Magento_Sales::creditmemo','Magento_Sales::hold','Magento_Sales::unhold','Magento_Sales::ship','Magento_Sales::comment','Magento_Sales::emails','Magento_Backend::system','Magento_Backend::system_other_settings','Magento_AdminNotification::adminnotification','Magento_AdminNotification::show_list']</data> + <data key="all">0</data> + <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> + <data key="resource">['Magento_Sales::sales','Magento_Sales::sales_operation','Magento_Sales::actions','Magento_Sales::sales_order','Magento_Sales::create','Magento_Sales::actions_view','Magento_Sales::email','Magento_Sales::reorder','Magento_Sales::actions_edit','Magento_Sales::cancel','Magento_Sales::review_payment','Magento_Sales::capture','Magento_Sales::invoice','Magento_Sales::creditmemo','Magento_Sales::hold','Magento_Sales::unhold','Magento_Sales::ship','Magento_Sales::comment','Magento_Sales::emails','Magento_Backend::system_other_settings','Magento_AdminNotification::adminnotification','Magento_AdminNotification::show_list']</data> </entity> <entity name="limitedRole" type="role"> <data key="name" unique="suffix">Limited</data> + <data key="rolename" unique="suffix">Limited</data> <data key="roleScopes">Custom</data> <data key="resourceAccess">All</data> </entity> <entity name="restrictedRole" type="role"> <data key="name" unique="suffix">Restricted</data> + <data key="rolename" unique="suffix">Restricted</data> <data key="roleScopes">Custom</data> <data key="resourceAccess">All</data> </entity> <!-- This admin created for checking turn off "Bulk Actions" --> <entity name="adminWithoutBulkActionRole" type="user_role"> + <data key="name">restrictedWebsiteRole</data> <data key="rolename">restrictedWebsiteRole</data> - <data key="current_password">123123q</data> + <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> + <data key="all">0</data> <data key="gws_is_all">0</data> <array key="gws_websites"> <item>1</item> @@ -78,4 +89,18 @@ <item>Magento_Backend::system</item> </array> </entity> + <entity name="adminProductInWebsiteRole" type="user_role"> + <data key="rolename" unique="suffix">restrictedWebsiteRole</data> + <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> + <data key="all">0</data> + </entity> + <entity name="adminRestrictedProductRole" type="user_role"> + <data key="rolename" unique="suffix">restrictedCatalogRole</data> + <data key="current_password">{{_ENV.MAGENTO_ADMIN_PASSWORD}}</data> + <data key="all">0</data> + </entity> + + <entity name="genericAdminRole" type="role"> + <data key="name">Administrators</data> + </entity> </entities> diff --git a/app/code/Magento/User/Test/Mftf/Metadata/user_role-meta.xml b/app/code/Magento/User/Test/Mftf/Metadata/user_role-meta.xml index 9d0132453c798..5384bd520b2c7 100644 --- a/app/code/Magento/User/Test/Mftf/Metadata/user_role-meta.xml +++ b/app/code/Magento/User/Test/Mftf/Metadata/user_role-meta.xml @@ -10,9 +10,10 @@ <operation name="CreateUserRole" dataType="user_role" type="create" auth="adminFormKey" url="/admin/user_role/saverole/" method="POST" successRegex="/messages-message-success/" returnRegex="" > <contentType>application/x-www-form-urlencoded</contentType> - <field key="rolename">string</field> - <field key="current_password">string</field> - <array key="resource"> + <field key="rolename" required="true">string</field> + <field key="current_password" required="true">string</field> + <field key="all" required="true">integer</field> + <array key="resource" required="false"> <value>string</value> </array> </operation> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminDeleteRoleSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminDeleteRoleSection.xml index 1b55d09d0597e..a3a82f6ce38e0 100644 --- a/app/code/Magento/User/Test/Mftf/Section/AdminDeleteRoleSection.xml +++ b/app/code/Magento/User/Test/Mftf/Section/AdminDeleteRoleSection.xml @@ -10,8 +10,10 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminDeleteRoleSection"> <element name="theRole" selector="//td[contains(text(), 'Role')]" type="button"/> + <element name="salesRole" selector="//td[contains(text(), 'Sales')]" type="button"/> <element name="current_pass" type="button" selector="#current_password"/> <element name="delete" selector="//button/span[contains(text(), 'Delete Role')]" type="button"/> <element name="confirm" selector="//*[@class='action-primary action-accept']" type="button"/> </section> </sections> + diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminDeleteUserSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminDeleteUserSection.xml new file mode 100644 index 0000000000000..21ca1cb36f988 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Section/AdminDeleteUserSection.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="AdminDeleteUserSection"> + <element name="role" parameterized="true" selector="//td[contains(text(), '{{roleName}}')]" type="button"/> + <element name="password" selector="#user_current_password" type="input"/> + <element name="delete" selector="//button/span[contains(text(), 'Delete User')]" type="button"/> + <element name="confirm" selector=".action-primary.action-accept" type="button"/> + </section> +</sections> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminNewUserFormSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminNewUserFormSection.xml index 9b030b216ce2c..8774261ea739d 100644 --- a/app/code/Magento/User/Test/Mftf/Section/AdminNewUserFormSection.xml +++ b/app/code/Magento/User/Test/Mftf/Section/AdminNewUserFormSection.xml @@ -19,6 +19,7 @@ <element name="password" type="input" selector="#page_tabs_main_section_content input[name='password']"/> <element name="passwordConfirmation" type="input" selector="#page_tabs_main_section_content input[name='password_confirmation']"/> <element name="interfaceLocale" type="select" selector="#page_tabs_main_section_content select[name='interface_locale']"/> + <element name="userIsActive" type="select" selector="#page_tabs_main_section_content select[id='user_is_active'] > option[value='{{isActive}}']" parameterized="true"/> <element name="currentPassword" type="input" selector="#page_tabs_main_section_content input[name='current_password']"/> <element name="userRoleTab" type="button" selector="#page_tabs_roles_section"/> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminStoreSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminStoreSection.xml new file mode 100644 index 0000000000000..9e47351e68701 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Section/AdminStoreSection.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminStoreSection"> + <element name="createdRoleInUserPage" type="text" selector="//tr//td[contains(text(), '{{roleName}}')]" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml new file mode 100644 index 0000000000000..753ab02f84053 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Test/AdminCreateActiveUserEntityTest.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateActiveUserEntityTest"> + <annotations> + <features value="User"/> + <stories value="Create Admin User"/> + <title value="Admin user should be able to create active admin user"/> + <description value="Admin user should be able to create active admin user"/> + <testCaseId value=""/> + <severity value="CRITICAL"/> + <group value="user"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + </before> + <after> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminDeleteUserActionGroup" stepKey="deleteUser"> + <argument name="user" value="activeAdmin"/> + </actionGroup> + </after> + + <actionGroup ref="AdminCreateUserWithRoleAndIsActiveActionGroup" stepKey="createAdminUser"> + <argument name="role" value="genericAdminRole"/> + <argument name="user" value="activeAdmin"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logoutMasterAdmin"/> + <amOnPage url="{{AdminLoginPage.url}}" stepKey="navigateToAdmin"/> + <fillField selector="{{AdminLoginFormSection.username}}" userInput="{{activeAdmin.username}}" stepKey="fillUsername"/> + <fillField selector="{{AdminLoginFormSection.password}}" userInput="{{activeAdmin.password}}" stepKey="fillPassword"/> + <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> + <closeAdminNotification stepKey="closeAdminNotification"/> + <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToAdminUsersGrid"/> + <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{activeAdmin.username}}" stepKey="fillUsernameSearch"/> + <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearchButton"/> + <waitForPageLoad time="10" stepKey="wait1"/> + <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{activeAdmin.username}}" stepKey="seeFoundUsername"/> + <actionGroup ref="logout" stepKey="logoutCreatedUser"/> + </test> +</tests> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml new file mode 100644 index 0000000000000..d0fdd72ebbdcc --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Test/AdminCreateInactiveUserEntityTest.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateInactiveUserEntityTest"> + <annotations> + <features value="User"/> + <stories value="Create Admin User"/> + <title value="Admin user should be able to create inactive admin user"/> + <description value="Admin user should be able to create inactive admin user"/> + <testCaseId value=""/> + <severity value="CRITICAL"/> + <group value="user"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + </before> + <after> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminDeleteUserActionGroup" stepKey="deleteUser"> + <argument name="user" value="inactiveAdmin"/> + </actionGroup> + </after> + + <actionGroup ref="AdminCreateUserWithRoleAndIsActiveActionGroup" stepKey="createAdminUser"> + <argument name="role" value="genericAdminRole"/> + <argument name="user" value="inactiveAdmin"/> + </actionGroup> + <amOnPage url="{{AdminUsersPage.url}}" stepKey="navigateToAdminUsersGrid"/> + <fillField selector="{{AdminUserGridSection.usernameFilterTextField}}" userInput="{{inactiveAdmin.username}}" stepKey="fillUsernameSearch"/> + <click selector="{{AdminUserGridSection.searchButton}}" stepKey="clickSearchButton"/> + <waitForPageLoad time="10" stepKey="wait1"/> + <see selector="{{AdminUserGridSection.usernameInFirstRow}}" userInput="{{inactiveAdmin.username}}" stepKey="seeFoundUsername"/> + <actionGroup ref="logout" stepKey="logoutMasterAdmin"/> + <amOnPage url="{{AdminLoginPage.url}}" stepKey="navigateToAdmin"/> + <fillField selector="{{AdminLoginFormSection.username}}" userInput="{{inactiveAdmin.username}}" stepKey="fillUsername"/> + <fillField selector="{{AdminLoginFormSection.password}}" userInput="{{inactiveAdmin.password}}" stepKey="fillPassword"/> + <click selector="{{AdminLoginFormSection.signIn}}" stepKey="clickLogin"/> + <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="seeUserErrorMessage" /> + </test> +</tests> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml new file mode 100644 index 0000000000000..3bd55a454c3b0 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Test/AdminLockAdminUserEntityTest.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminLockAdminUserEntityTest"> + <annotations> + <features value="User"/> + <stories value="Lock admin user during login"/> + <title value="Lock admin user after entering incorrect password specified number of times"/> + <description value="Lock admin user after entering incorrect password specified number of times"/> + <testCaseId value="MC-14267"/> + <severity value="CRITICAL"/> + <group value="user"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <magentoCLI command="config:set admin/captcha/enable 0" stepKey="disableAdminCaptcha"/> + <magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCaches1"/> + <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> + </before> + <after> + <magentoCLI command="config:set admin/captcha/enable 1" stepKey="enableAdminCaptcha"/> + <magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCaches"/> + <actionGroup ref="logout" stepKey="logOut"/> + </after> + + <!--Create New User--> + <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="goToNewUserPage"/> + <actionGroup ref="AdminFillNewUserFormRequiredFieldsActionGroup" stepKey="fillNewUserForm"> + <argument name="user" value="adminUserCorrectPassword"/> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveNewUser"/> + + <!--Set 'Maximum Login Failures to Lockout Account'--> + <actionGroup ref="AdminOpenConfigAdminPageActionGroup" stepKey="goToConfigAdminSectionPage"/> + <actionGroup ref="AdminExpandSecurityTabActionGroup" stepKey="openSecurityTab"/> + <actionGroup ref="AdminSetMaximumLoginFailuresToLockoutAccountActionGroup" stepKey="setMaximumLoginFailures"> + <argument name="qty" value="2"/> + </actionGroup> + <actionGroup ref="AdminSaveConfigActionGroup" stepKey="saveChanges"/> + + <!-- Log in to Admin Panel with incorrect password specified number of times--> + <actionGroup ref="logout" stepKey="logoutAsDefaultUser"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUserFirstAttempt"> + <argument name="adminUser" value="adminUserIncorrectPassword"/> + </actionGroup> + <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginErrorFirstAttempt"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUserSecondAttempt"> + <argument name="adminUser" value="adminUserIncorrectPassword"/> + </actionGroup> + <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginErrorSecondAttempt"/> + + <!-- Log in to Admin Panel with correct password--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsNewUserThirdAttempt"> + <argument name="adminUser" value="adminUserCorrectPassword"/> + </actionGroup> + <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginErrorThirdAttempt"/> + + <!--Login as default admin user--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsDefaultAdminUser"/> + + <!--Delete new User--> + <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteNewUser"> + <argument name="user" value="adminUserCorrectPassword"/> + </actionGroup> + + </test> +</tests> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml new file mode 100644 index 0000000000000..dfadee8ee6807 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUpdateUserTest"> + <annotations> + <features value="User"/> + <title value="Update admin user entity by changing user role"/> + <stories value="Update User" /> + <testCaseId value="MC-14264" /> + <severity value="MAJOR" /> + <description value="Change full access role for admin user to custom one with restricted permission (Sales)"/> + <group value="user"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="logIn"/> + + <!--Create New User--> + <actionGroup ref="AdminOpenNewUserPageActionGroup" stepKey="goToNewUserPage"/> + <actionGroup ref="AdminFillNewUserFormRequiredFieldsActionGroup" stepKey="fillNewUserForm"> + <argument name="user" value="NewAdminUser"/> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveNewUser"/> + + <!--Create New Role--> + <actionGroup ref="AdminOpenCreateRolePageActionGroup" stepKey="goToNewRolePage"/> + <actionGroup ref="AdminFillUserRoleFormActionGroup" stepKey="fillNewRoleForm"> + <argument name="role" value="roleSales"/> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveNewRole"/> + </before> + <after> + <!--Delete new User--> + <actionGroup ref="logout" stepKey="logoutAsSaleRoleUser"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsDefaultAdminUser"/> + <actionGroup ref="AdminDeleteCustomUserActionGroup" stepKey="deleteNewUser"> + <argument name="user" value="AdminUserWithUpdatedUserRoleToSales"/> + </actionGroup> + + <!--Delete new Role--> + <actionGroup ref="AdminDeleteUserRoleActionGroup" stepKey="deleteCustomRole"> + <argument name="roleName" value="{{roleSales.rolename}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logOut"/> + </after> + + + <!--Assign new role--> + <actionGroup ref="AdminOpenUserEditPageActionGroup" stepKey="openUserEditPage"> + <argument name="user" value="NewAdminUser"/> + </actionGroup> + <actionGroup ref="AdminFillNewUserFormRequiredFieldsActionGroup" stepKey="fillUserForm"> + <argument name="user" value="AdminUserWithUpdatedUserRoleToSales"/> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserFormActionGroup" stepKey="saveUser"/> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="You saved the user."/> + </actionGroup> + + <actionGroup ref="AssertAdminUserIsInGridActionGroup" stepKey="seeUserInGrid"> + <argument name="user" value="AdminUserWithUpdatedUserRoleToSales"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logOutFromAdminPanel"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsSaleRoleUser"> + <argument name="adminUser" value="AdminUserWithUpdatedUserRoleToSales"/> + </actionGroup> + <actionGroup ref="AssertAdminSuccessLoginActionGroup" stepKey="seeSuccessloginMessage"/> + <actionGroup ref="AdminOpenAdminUsersPageActionGroup" stepKey="navigateToAdminUsersPage"/> + <actionGroup ref="AssertUserRoleRestrictedAccessActionGroup" stepKey="seeErrorMessage"/> + </test> +</tests> diff --git a/app/code/Magento/User/Test/Unit/Model/Authorization/AdminSessionUserContextTest.php b/app/code/Magento/User/Test/Unit/Model/Authorization/AdminSessionUserContextTest.php new file mode 100644 index 0000000000000..23681c4b8da26 --- /dev/null +++ b/app/code/Magento/User/Test/Unit/Model/Authorization/AdminSessionUserContextTest.php @@ -0,0 +1,89 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\User\Test\Unit\Model\Authorization; + +use Magento\Authorization\Model\UserContextInterface; + +/** + * Tests Magento\User\Model\Authorization\AdminSessionUserContext + */ +class AdminSessionUserContextTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + protected $objectManager; + + /** + * @var \Magento\User\Model\Authorization\AdminSessionUserContext + */ + protected $adminSessionUserContext; + + /** + * @var \Magento\Backend\Model\Auth\Session + */ + protected $adminSession; + + protected function setUp() + { + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->adminSession = $this->getMockBuilder(\Magento\Backend\Model\Auth\Session::class) + ->disableOriginalConstructor() + ->setMethods(['hasUser', 'getUser', 'getId']) + ->getMock(); + + $this->adminSessionUserContext = $this->objectManager->getObject( + \Magento\User\Model\Authorization\AdminSessionUserContext::class, + ['adminSession' => $this->adminSession] + ); + } + + public function testGetUserIdExist() + { + $userId = 1; + + $this->setupUserId($userId); + + $this->assertEquals($userId, $this->adminSessionUserContext->getUserId()); + } + + public function testGetUserIdDoesNotExist() + { + $userId = null; + + $this->setupUserId($userId); + + $this->assertEquals($userId, $this->adminSessionUserContext->getUserId()); + } + + public function testGetUserType() + { + $this->assertEquals(UserContextInterface::USER_TYPE_ADMIN, $this->adminSessionUserContext->getUserType()); + } + + /** + * @param int|null $userId + * @return void + */ + public function setupUserId($userId) + { + $this->adminSession->expects($this->once()) + ->method('hasUser') + ->will($this->returnValue($userId)); + + if ($userId) { + $this->adminSession->expects($this->once()) + ->method('getUser') + ->will($this->returnSelf()); + + $this->adminSession->expects($this->once()) + ->method('getId') + ->will($this->returnValue($userId)); + } + } +} diff --git a/app/code/Magento/User/Test/Unit/Model/Backend/Config/ObserverConfigTest.php b/app/code/Magento/User/Test/Unit/Model/Backend/Config/ObserverConfigTest.php new file mode 100644 index 0000000000000..395c45bc676a8 --- /dev/null +++ b/app/code/Magento/User/Test/Unit/Model/Backend/Config/ObserverConfigTest.php @@ -0,0 +1,142 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\User\Test\Unit\Model\Backend\Config; + +use Magento\User\Model\Backend\Config\ObserverConfig; +use Magento\Backend\App\ConfigInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * Unit Test for \Magento\User\Model\Backend\Config\ObserverConfig class + * + * Class \Magento\User\Test\Unit\Model\Backend\Config\ObserverConfigTest + */ +class ObserverConfigTest extends \PHPUnit\Framework\TestCase +{ + /** + * Config path for lockout threshold + */ + private const XML_ADMIN_SECURITY_LOCKOUT_THRESHOLD = 'admin/security/lockout_threshold'; + + /** + * Config path for password change is forced or not + */ + private const XML_ADMIN_SECURITY_PASSWORD_IS_FORCED = 'admin/security/password_is_forced'; + + /** + * Config path for password lifetime + */ + private const XML_ADMIN_SECURITY_PASSWORD_LIFETIME = 'admin/security/password_lifetime'; + + /** + * Config path for maximum lockout failures + */ + private const XML_ADMIN_SECURITY_LOCKOUT_FAILURES = 'admin/security/lockout_failures'; + + /** @var ObserverConfig */ + private $model; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|ConfigInterface + */ + private $backendConfigMock; + + /** + * Set environment for test + */ + protected function setUp() + { + $this->backendConfigMock = $this->createMock(ConfigInterface::class); + + $objectManager = new ObjectManagerHelper($this); + $this->model = $objectManager->getObject( + ObserverConfig::class, + [ + 'backendConfig' => $this->backendConfigMock + ] + ); + } + + /** + * Test when admin password lifetime = 0 days + */ + public function testIsLatestPasswordExpiredWhenNoAdminLifeTime() + { + $this->backendConfigMock->expects(self::any())->method('getValue') + ->with(self::XML_ADMIN_SECURITY_PASSWORD_LIFETIME) + ->willReturn('0'); + $this->assertEquals(false, $this->model->_isLatestPasswordExpired([])); + } + + /** + * Test when admin password lifetime = 2 days + */ + public function testIsLatestPasswordExpiredWhenHasAdminLifeTime() + { + $this->backendConfigMock->expects(self::any())->method('getValue') + ->with(self::XML_ADMIN_SECURITY_PASSWORD_LIFETIME) + ->willReturn('2'); + $this->assertEquals(true, $this->model->_isLatestPasswordExpired(['last_updated' => 1571428052])); + } + + /** + * Test when security lockout threshold = 100 minutes + */ + public function testGetAdminLockThreshold() + { + $this->backendConfigMock->expects(self::any())->method('getValue') + ->with(self::XML_ADMIN_SECURITY_LOCKOUT_THRESHOLD) + ->willReturn('100'); + $this->assertEquals(6000, $this->model->getAdminLockThreshold()); + } + + /** + * Test when password change force is true + */ + public function testIsPasswordChangeForcedTrue() + { + $this->backendConfigMock->expects(self::any())->method('getValue') + ->with(self::XML_ADMIN_SECURITY_PASSWORD_IS_FORCED) + ->willReturn('1'); + $this->assertEquals(true, $this->model->isPasswordChangeForced()); + } + + /** + * Test when password change force is false + */ + public function testIsPasswordChangeForcedFalse() + { + $this->backendConfigMock->expects(self::any())->method('getValue') + ->with(self::XML_ADMIN_SECURITY_PASSWORD_IS_FORCED) + ->willReturn('0'); + $this->assertEquals(false, $this->model->isPasswordChangeForced()); + } + + /** + * Test when admin password lifetime = 2 days + */ + public function testGetAdminPasswordLifetime() + { + $this->backendConfigMock->expects(self::any())->method('getValue') + ->with(self::XML_ADMIN_SECURITY_PASSWORD_LIFETIME) + ->willReturn('2'); + $this->assertEquals(172800, $this->model->getAdminPasswordLifetime()); + } + + /** + * Test when max failures = 5 (times) + */ + public function testGetMaxFailures() + { + $this->backendConfigMock->expects(self::any())->method('getValue') + ->with(self::XML_ADMIN_SECURITY_LOCKOUT_FAILURES) + ->willReturn('5'); + $this->assertEquals(5, $this->model->getMaxFailures()); + } +} diff --git a/app/code/Magento/User/Test/Unit/Model/UserTest.php b/app/code/Magento/User/Test/Unit/Model/UserTest.php index ab06c8754b2f0..670316c2500fc 100644 --- a/app/code/Magento/User/Test/Unit/Model/UserTest.php +++ b/app/code/Magento/User/Test/Unit/Model/UserTest.php @@ -44,6 +44,31 @@ protected function setUp() ); } + /** + * @return void + */ + public function testSleep() + { + $excludedProperties = [ + '_eventManager', + '_cacheManager', + '_registry', + '_appState', + '_userData', + '_config', + '_validatorObject', + '_roleFactory', + '_encryptor', + '_transportBuilder', + '_storeManager', + '_validatorBeforeSave' + ]; + $actualResult = $this->model->__sleep(); + $this->assertNotEmpty($actualResult); + $expectedResult = array_intersect($actualResult, $excludedProperties); + $this->assertEmpty($expectedResult); + } + /** * @return void */ diff --git a/app/code/Magento/User/registration.php b/app/code/Magento/User/registration.php index c00bc0a633803..9a81d456f5f57 100644 --- a/app/code/Magento/User/registration.php +++ b/app/code/Magento/User/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_User', __DIR__); diff --git a/app/code/Magento/User/view/adminhtml/email/new_user_notification.html b/app/code/Magento/User/view/adminhtml/email/new_user_notification.html index 891faf5fb8c2b..87f4e4669c4b6 100644 --- a/app/code/Magento/User/view/adminhtml/email/new_user_notification.html +++ b/app/code/Magento/User/view/adminhtml/email/new_user_notification.html @@ -6,7 +6,13 @@ --> <!--@subject {{trans "New admin user '%user_name' created" user_name=$user.name}} @--> <!--@vars { -"var store.getFrontendName()|escape":"Store Name" +"var store.frontend_name":"Store Name", +"var user.name":"User Name", +"var user.first_name":"User First Name", +"var user.last_name":"User Last Name", +"var user.email":"User Email", +"var store_email":"Store Email", +"var store_phone":"Store Phone" } @--> {{trans "Hello,"}} @@ -15,4 +21,4 @@ {{trans "If you have not authorized this action, please contact us immediately at %store_email" store_email=$store_email |escape}}{{depend store_phone}} {{trans "or call us at %store_phone" store_phone=$store_phone |escape}}{{/depend}}. {{trans "Thanks,"}} -{{var store.getFrontendName()}} +{{var store.frontend_name}} diff --git a/app/code/Magento/User/view/adminhtml/email/password_reset_confirmation.html b/app/code/Magento/User/view/adminhtml/email/password_reset_confirmation.html index 62bac389e6dc4..dacfa640464a3 100644 --- a/app/code/Magento/User/view/adminhtml/email/password_reset_confirmation.html +++ b/app/code/Magento/User/view/adminhtml/email/password_reset_confirmation.html @@ -6,6 +6,9 @@ --> <!--@subject {{trans "Password Reset Confirmation for %name" name=$user.name}} @--> <!--@vars { +"var store.frontend_name":"Store Name", +"var user.id":"Account Holder Id", +"var user.rp_token":"Reset Password Token", "var user.name":"Account Holder Name", "store url=\"admin\/auth\/resetpassword\/\" _query_id=$user.id _query_token=$user.rp_token":"Reset Password URL" } @--> @@ -21,4 +24,4 @@ {{trans "If you did not make this request, you can ignore this email and your password will remain the same."}} {{trans "Thank you,"}} -{{var store.getFrontendName()}} +{{var store.frontend_name}} diff --git a/app/code/Magento/User/view/adminhtml/email/user_notification.html b/app/code/Magento/User/view/adminhtml/email/user_notification.html index 3b6ffb2ce14b1..82657531a10df 100644 --- a/app/code/Magento/User/view/adminhtml/email/user_notification.html +++ b/app/code/Magento/User/view/adminhtml/email/user_notification.html @@ -6,13 +6,18 @@ --> <!--@subject {{trans "New %changes for %user_name" changes=$changes user_name=$user.name}} @--> <!--@vars { -"var store.getFrontendName()|escape":"Store Name" +"var store.frontend_name":"Store Name", +"var store_name":"Store Name", +"var store_email":"Store Email", +"var store_phone":"Store Phone", +"var changes":"Changes", +"var user.name":"User Name" } @--> {{trans "Hello,"}} -{{trans "We have received a request to change the following information associated with your account at %store_name: %changes." store_name=$store.getFrontendName() changes=$changes}} +{{trans "We have received a request to change the following information associated with your account at %store_name: %changes." store_name=$store.frontend_name changes=$changes}} {{trans "If you have not authorized this action, please contact us immediately at %store_email" store_email=$store_email |escape}}{{depend store_phone}} {{trans "or call us at %store_phone" store_phone=$store_phone |escape}}{{/depend}}. {{trans "Thanks,"}} -{{var store.getFrontendName()}} +{{var store.frontend_name}} diff --git a/app/code/Magento/User/view/adminhtml/templates/role/edit.phtml b/app/code/Magento/User/view/adminhtml/templates/role/edit.phtml index edee167dc1b8a..97308204be854 100644 --- a/app/code/Magento/User/view/adminhtml/templates/role/edit.phtml +++ b/app/code/Magento/User/view/adminhtml/templates/role/edit.phtml @@ -38,13 +38,13 @@ <label class="label"><span><?= $block->escapeHtml(__('Resources')) ?></span></label> <div class="control"> - <div class="tree x-tree" data-role="resource-tree" data-mage-init='<?= /* @noEscape */ - $block->getJsonSerializer()->serialize([ + <div class="tree x-tree" data-role="resource-tree" data-mage-init='<?= + $block->escapeHtmlAttr($block->getJsonSerializer()->serialize([ 'rolesTree' => [ "treeInitData" => $block->getTree(), "treeInitSelectedData" => $block->getSelectedResources(), ], - ]); ?>'> + ])); ?>'> </div> </div> </div> diff --git a/app/code/Magento/Usps/registration.php b/app/code/Magento/Usps/registration.php index 988089ccda954..32fe2a6a49fbd 100644 --- a/app/code/Magento/Usps/registration.php +++ b/app/code/Magento/Usps/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Usps', __DIR__); diff --git a/app/code/Magento/Variable/Test/Mftf/ActionGroup/CreateCustomVariableActionGroup.xml b/app/code/Magento/Variable/Test/Mftf/ActionGroup/CreateCustomVariableActionGroup.xml index 1aa9a3fa6c6ab..75f09b478170e 100644 --- a/app/code/Magento/Variable/Test/Mftf/ActionGroup/CreateCustomVariableActionGroup.xml +++ b/app/code/Magento/Variable/Test/Mftf/ActionGroup/CreateCustomVariableActionGroup.xml @@ -21,20 +21,4 @@ <fillField selector="{{CustomVariableSection.variablePlain}}" userInput="{{customVariable.plain}}" stepKey="fillVariablePlain"/> <click selector="{{CustomVariableSection.saveCustomVariable}}" stepKey="clickSaveVariable"/> </actionGroup> - - <actionGroup name="DeleteCustomVariableActionGroup"> - <annotations> - <description>Goes to the Custom Variable grid page. Deletes the Custom Variable. PLEASE NOTE: The Custom Variable that is deleted is Hardcoded using 'customVariable'.</description> - </annotations> - - <amOnPage url="admin/admin/system_variable/" stepKey="goToVarialeGrid"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <click selector="{{CustomVariableSection.GridCustomVariableCode(customVariable.code)}}" stepKey="goToCustomVariableEditPage"/> - <waitForPageLoad stepKey="waitForPageLoad2"/> - <waitForElementVisible selector="{{CustomVariableSection.delete}}" stepKey="waitForDeleteBtn"/> - <click selector="{{CustomVariableSection.delete}}" stepKey="deleteCustomVariable"/> - <waitForText userInput="Are you sure you want to do this?" stepKey="waitForText"/> - <click selector="{{CustomVariableSection.confirmDelete}}" stepKey="confirmDelete"/> - <waitForPageLoad stepKey="waitForPageLoad3"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Variable/Test/Mftf/ActionGroup/DeleteCustomVariableActionGroup.xml b/app/code/Magento/Variable/Test/Mftf/ActionGroup/DeleteCustomVariableActionGroup.xml new file mode 100644 index 0000000000000..df77d7d1fb9d0 --- /dev/null +++ b/app/code/Magento/Variable/Test/Mftf/ActionGroup/DeleteCustomVariableActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="DeleteCustomVariableActionGroup"> + <annotations> + <description>Goes to the Custom Variable grid page. Deletes the Custom Variable. PLEASE NOTE: The Custom Variable that is deleted is Hardcoded using 'customVariable'.</description> + </annotations> + + <amOnPage url="admin/admin/system_variable/" stepKey="goToVarialeGrid"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <click selector="{{CustomVariableSection.GridCustomVariableCode(customVariable.code)}}" stepKey="goToCustomVariableEditPage"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + <waitForElementVisible selector="{{CustomVariableSection.delete}}" stepKey="waitForDeleteBtn"/> + <click selector="{{CustomVariableSection.delete}}" stepKey="deleteCustomVariable"/> + <waitForText userInput="Are you sure you want to do this?" stepKey="waitForText"/> + <click selector="{{CustomVariableSection.confirmDelete}}" stepKey="confirmDelete"/> + <waitForPageLoad stepKey="waitForPageLoad3"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Variable/Test/Unit/Model/Variable/DataTest.php b/app/code/Magento/Variable/Test/Unit/Model/Variable/DataTest.php new file mode 100644 index 0000000000000..50191de66efbf --- /dev/null +++ b/app/code/Magento/Variable/Test/Unit/Model/Variable/DataTest.php @@ -0,0 +1,128 @@ +<?php +/*** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Variable\Test\Unit\Model\Variable; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use PHPUnit\Framework\TestCase; +use Magento\Variable\Model\Variable\Data as VariableDataModel; +use Magento\Variable\Model\ResourceModel\Variable\CollectionFactory as VariableCollectionFactory; +use Magento\Variable\Model\ResourceModel\Variable\Collection as VariableCollection; +use Magento\Variable\Model\Source\Variables as StoreVariables; + +class DataTest extends TestCase +{ + /** + * @var VariableDataModel + */ + private $model; + + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + /** + * @var StoreVariables|PHPUnit_Framework_MockObject_MockObject + */ + private $storesVariablesMock; + + /** + * @var VariableCollectionFactory|PHPUnit_Framework_MockObject_MockObject + */ + private $variableCollectionFactoryMock; + + /** + * Set up before tests + */ + protected function setUp() + { + $this->storesVariablesMock = $this->createMock(StoreVariables::class); + $this->variableCollectionFactoryMock = $this->getMockBuilder( + VariableCollectionFactory::class + )->disableOriginalConstructor()->setMethods(['create'])->getMock(); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->model = $this->objectManagerHelper->getObject( + VariableDataModel::class, + [ + 'collectionFactory' => $this->variableCollectionFactoryMock, + 'storesVariables' => $this->storesVariablesMock + ] + ); + } + + /** + * Test getDefaultVariables() function + */ + public function testGetDefaultVariables() + { + $storesVariablesData = [ + [ + 'value' => 'test 1', + 'label' => 'Test Label 1', + 'group_label' => 'Group Label 1' + ], + [ + 'value' => 'test 2', + 'label' => 'Test Label 2', + 'group_label' => 'Group Label 2' + ] + ]; + $expectedResult = [ + [ + 'code' => 'test 1', + 'variable_name' => 'Group Label 1 / Test Label 1', + 'variable_type' => StoreVariables::DEFAULT_VARIABLE_TYPE + ], + [ + 'code' => 'test 2', + 'variable_name' => 'Group Label 2 / Test Label 2', + 'variable_type' => StoreVariables::DEFAULT_VARIABLE_TYPE + ] + ]; + $this->storesVariablesMock->expects($this->any())->method('getData')->willReturn($storesVariablesData); + + $this->assertEquals($expectedResult, $this->model->getDefaultVariables()); + } + + /** + * Test getCustomVariables() function + */ + public function testGetCustomVariables() + { + $customVariables = [ + [ + 'code' => 'test 1', + 'name' => 'Test 1' + ], + [ + 'code' => 'test 2', + 'name' => 'Test 2' + ] + ]; + $expectedResult = [ + [ + 'code' => 'test 1', + 'variable_name' => 'Custom Variable / Test 1', + 'variable_type' => StoreVariables::CUSTOM_VARIABLE_TYPE + ], + [ + 'code' => 'test 2', + 'variable_name' => 'Custom Variable / Test 2', + 'variable_type' => StoreVariables::CUSTOM_VARIABLE_TYPE + ] + ]; + $variableCollectionMock = $this->createMock(VariableCollection::class); + $this->variableCollectionFactoryMock->expects($this->once())->method('create') + ->willReturn($variableCollectionMock); + $variableCollectionMock->expects($this->any())->method('getData')->willReturn($customVariables); + + $this->assertEquals($expectedResult, $this->model->getCustomVariables()); + } +} diff --git a/app/code/Magento/Variable/registration.php b/app/code/Magento/Variable/registration.php index a35a0da8b2c79..c68fbb63c2e64 100644 --- a/app/code/Magento/Variable/registration.php +++ b/app/code/Magento/Variable/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Variable', __DIR__); diff --git a/app/code/Magento/Vault/registration.php b/app/code/Magento/Vault/registration.php index 704dd359d139c..4959a7f61cd7c 100644 --- a/app/code/Magento/Vault/registration.php +++ b/app/code/Magento/Vault/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Vault', __DIR__); diff --git a/app/code/Magento/Version/Controller/Index/Index.php b/app/code/Magento/Version/Controller/Index/Index.php index 0db9b5f80d483..53bcd4b4ff700 100644 --- a/app/code/Magento/Version/Controller/Index/Index.php +++ b/app/code/Magento/Version/Controller/Index/Index.php @@ -4,11 +4,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Version\Controller\Index; -use Magento\Framework\App\Action\HttpGetActionInterface as HttpGetActionInterface; use Magento\Framework\App\Action\Action; use Magento\Framework\App\Action\Context; +use Magento\Framework\App\Action\HttpGetActionInterface as HttpGetActionInterface; use Magento\Framework\App\ProductMetadataInterface; /** @@ -16,6 +18,8 @@ */ class Index extends Action implements HttpGetActionInterface { + const DEV_PREFIX = 'dev-'; + /** * @var ProductMetadataInterface */ @@ -32,25 +36,22 @@ public function __construct(Context $context, ProductMetadataInterface $productM } /** - * Sets the response body to ProductName/Major.MinorVersion (Edition). E.g.: Magento/0.42 (Community). Omits patch - * version from response + * Sets the response body to ProductName/Major.MinorVersion (Edition). * * @return void */ - public function execute() + public function execute(): void { $version = $this->productMetadata->getVersion(); $versionParts = explode('.', $version); - if ((!isset($versionParts[0]) || !isset($versionParts[1])) - || $this->isGitBasedInstallation($version) - ) { + if ($this->isGitBasedInstallation($version) || !$this->isCorrectVersion($versionParts)) { return; } - $majorMinorVersion = $versionParts[0] . '.' . $versionParts[1]; + $this->getResponse()->setBody( $this->productMetadata->getName() . '/' . - $majorMinorVersion . ' (' . - $this->productMetadata->getEdition() . ')' + $this->getMajorMinorVersion($versionParts) . + ' (' . $this->productMetadata->getEdition() . ')' ); } @@ -60,9 +61,30 @@ public function execute() * @param string $fullVersion * @return bool */ - private function isGitBasedInstallation($fullVersion) + private function isGitBasedInstallation($fullVersion): bool + { + return 0 === strpos($fullVersion, self::DEV_PREFIX); + } + + /** + * Verifies if the Magento version is correct + * + * @param array $versionParts + * @return bool + */ + private function isCorrectVersion(array $versionParts): bool + { + return isset($versionParts[0]) && isset($versionParts[1]); + } + + /** + * Returns string only with Major and Minor version number + * + * @param array $versionParts + * @return string + */ + private function getMajorMinorVersion(array $versionParts): string { - $versionParts = explode('-', $fullVersion); - return (isset($versionParts[0]) && $versionParts[0] == 'dev'); + return $versionParts[0] . '.' . $versionParts[1]; } } diff --git a/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php b/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php index 6f8daa9d8008d..3fc2cecabe990 100644 --- a/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php +++ b/app/code/Magento/Version/Test/Unit/Controller/Index/IndexTest.php @@ -7,16 +7,14 @@ namespace Magento\Version\Test\Unit\Controller\Index; -use Magento\Version\Controller\Index\Index as VersionIndex; use Magento\Framework\App\Action\Context; use Magento\Framework\App\ProductMetadataInterface; use Magento\Framework\App\ResponseInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Version\Controller\Index\Index as VersionIndex; +use PHPUnit\Framework\TestCase; -/** - * Class \Magento\Version\Test\Unit\Controller\Index\IndexTest - */ -class IndexTest extends \PHPUnit\Framework\TestCase +class IndexTest extends TestCase { /** * @var VersionIndex @@ -26,72 +24,80 @@ class IndexTest extends \PHPUnit\Framework\TestCase /** * @var Context */ - private $context; + private $contextMock; /** * @var ProductMetadataInterface */ - private $productMetadata; + private $productMetadataMock; /** * @var ResponseInterface */ - private $response; + private $responseMock; /** * Prepare test preconditions */ protected function setUp() { - $this->context = $this->getMockBuilder(Context::class) + $this->contextMock = $this->getMockBuilder(Context::class) ->disableOriginalConstructor() ->getMock(); - $this->productMetadata = $this->getMockBuilder(ProductMetadataInterface::class) + $this->productMetadataMock = $this->getMockBuilder(ProductMetadataInterface::class) ->disableOriginalConstructor() ->setMethods(['getName', 'getEdition', 'getVersion']) ->getMock(); - $this->response = $this->getMockBuilder(ResponseInterface::class) + $this->responseMock = $this->getMockBuilder(ResponseInterface::class) ->disableOriginalConstructor() ->setMethods(['setBody', 'sendResponse']) ->getMock(); - $this->context->expects($this->any()) + $this->contextMock->expects($this->any()) ->method('getResponse') - ->willReturn($this->response); + ->willReturn($this->responseMock); - $helper = new ObjectManager($this); + $objectManager = new ObjectManager($this); - $this->model = $helper->getObject( - 'Magento\Version\Controller\Index\Index', + $this->model = $objectManager->getObject( + VersionIndex::class, [ - 'context' => $this->context, - 'productMetadata' => $this->productMetadata + 'context' => $this->contextMock, + 'productMetadata' => $this->productMetadataMock ] ); } /** - * Test with Git Base version + * Git Base version does not return information about version */ - public function testExecuteWithGitBase() + public function testGitBasedInstallationDoesNotReturnVersion(): void { - $this->productMetadata->expects($this->any())->method('getVersion')->willReturn('dev-2.3'); + $this->productMetadataMock->expects($this->any()) + ->method('getVersion') + ->willReturn('dev-2.3'); + + $this->responseMock->expects($this->never()) + ->method('setBody'); + $this->assertNull($this->model->execute()); } /** - * Test with Community Version + * Magento Community returns information about major and minor version of product */ - public function testExecuteWithCommunityVersion() + public function testCommunityVersionDisplaysMajorMinorVersionAndEditionName(): void { - $this->productMetadata->expects($this->any())->method('getVersion')->willReturn('2.3.3'); - $this->productMetadata->expects($this->any())->method('getEdition')->willReturn('Community'); - $this->productMetadata->expects($this->any())->method('getName')->willReturn('Magento'); - $this->response->expects($this->once())->method('setBody') + $this->productMetadataMock->expects($this->any())->method('getVersion')->willReturn('2.3.3'); + $this->productMetadataMock->expects($this->any())->method('getEdition')->willReturn('Community'); + $this->productMetadataMock->expects($this->any())->method('getName')->willReturn('Magento'); + + $this->responseMock->expects($this->once())->method('setBody') ->with('Magento/2.3 (Community)') ->will($this->returnSelf()); + $this->model->execute(); } } diff --git a/app/code/Magento/Version/etc/frontend/routes.xml b/app/code/Magento/Version/etc/frontend/routes.xml index a3988030478ff..3550a0c6a15f5 100644 --- a/app/code/Magento/Version/etc/frontend/routes.xml +++ b/app/code/Magento/Version/etc/frontend/routes.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> <router id="standard"> <route id="magento_version" frontName="magento_version"> - <module name="Magento_Version" /> + <module name="Magento_Version"/> </route> </router> </config> diff --git a/app/code/Magento/Version/etc/module.xml b/app/code/Magento/Version/etc/module.xml index fe8ace51ea07d..b21ef687e164d 100644 --- a/app/code/Magento/Version/etc/module.xml +++ b/app/code/Magento/Version/etc/module.xml @@ -6,6 +6,6 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_Version" > + <module name="Magento_Version"> </module> </config> diff --git a/app/code/Magento/Version/registration.php b/app/code/Magento/Version/registration.php index 24daee14ec3da..e2a8d758ae15b 100644 --- a/app/code/Magento/Version/registration.php +++ b/app/code/Magento/Version/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Version', __DIR__); diff --git a/app/code/Magento/Webapi/registration.php b/app/code/Magento/Webapi/registration.php index 1ce2579e4e7c8..1836a29955e86 100644 --- a/app/code/Magento/Webapi/registration.php +++ b/app/code/Magento/Webapi/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Webapi', __DIR__); diff --git a/app/code/Magento/WebapiAsync/Model/Config.php b/app/code/Magento/WebapiAsync/Model/Config.php index 16c24643ba355..7980be479dfa5 100644 --- a/app/code/Magento/WebapiAsync/Model/Config.php +++ b/app/code/Magento/WebapiAsync/Model/Config.php @@ -179,7 +179,7 @@ private function generateTopicNameFromService($serviceInterface, $serviceMethod, */ private function generateKey($typeName, $methodName, $delimiter = '\\', $lcfirst = true) { - $parts = explode($delimiter, ltrim($typeName, $delimiter)); + $parts = explode($delimiter, trim($typeName, $delimiter)); foreach ($parts as &$part) { $part = ltrim($part, ':'); if ($lcfirst === true) { diff --git a/app/code/Magento/WebapiSecurity/registration.php b/app/code/Magento/WebapiSecurity/registration.php index a25eea9deb660..fc6d8e76c9754 100644 --- a/app/code/Magento/WebapiSecurity/registration.php +++ b/app/code/Magento/WebapiSecurity/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_WebapiSecurity', __DIR__); diff --git a/app/code/Magento/Weee/Block/Sales/Order/Totals.php b/app/code/Magento/Weee/Block/Sales/Order/Totals.php index 8aeefecb14cc9..bc04b3a3985f3 100644 --- a/app/code/Magento/Weee/Block/Sales/Order/Totals.php +++ b/app/code/Magento/Weee/Block/Sales/Order/Totals.php @@ -6,6 +6,8 @@ namespace Magento\Weee\Block\Sales\Order; /** + * Wee tax total column block + * * @api * @since 100.0.2 */ @@ -54,6 +56,8 @@ public function initTotals() $weeeTotal = $this->weeeData->getTotalAmounts($items, $store); $weeeBaseTotal = $this->weeeData->getBaseTotalAmounts($items, $store); if ($weeeTotal) { + $totals = $this->getParentBlock()->getTotals(); + // Add our total information to the set of other totals $total = new \Magento\Framework\DataObject( [ @@ -63,10 +67,10 @@ public function initTotals() 'base_value' => $weeeBaseTotal ] ); - if ($this->getBeforeCondition()) { - $this->getParentBlock()->addTotalBefore($total, $this->getBeforeCondition()); + if (isset($totals['grand_total_incl'])) { + $this->getParentBlock()->addTotalBefore($total, 'grand_total'); } else { - $this->getParentBlock()->addTotal($total, $this->getAfterCondition()); + $this->getParentBlock()->addTotalBefore($total, $this->getBeforeCondition()); } } return $this; diff --git a/app/code/Magento/Weee/README.md b/app/code/Magento/Weee/README.md index 61f9b8d6d8970..ef433ec4c96f9 100644 --- a/app/code/Magento/Weee/README.md +++ b/app/code/Magento/Weee/README.md @@ -2,10 +2,10 @@ The Magento_Weee module enables the application of fees/fixed product taxes (FPT) on certain types of products, usually related to electronic devices and recycling. Fixed product taxes can be used to setup a WEEE tax that is a fixed amount, rather than a percentage of the product price. FPT can be configured to be displayed at various places in Magento. Rules, amounts, and display options can be configured in the backend. This module extends the existing functionality of Magento_Tax. -The Magento_Wee module includes the following: +The Magento_Weee module includes the following: * ability to add different number of fixed product taxes to product. They are treated as a product attribute; -* configuration of where Weee appears (on category, product, sales, invoice, or credit memo pages) and whether FPT should be taxed; +* configuration of where WEEE appears (on category, product, sales, invoice, or credit memo pages) and whether FPT should be taxed; * a new line item in the totals section. # System requirements diff --git a/app/code/Magento/Weee/Test/Mftf/Test/AdminFixedTaxValSavedForSpecificWebsiteTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/AdminFixedTaxValSavedForSpecificWebsiteTest.xml index 85ed044644d5c..6cbdbe0db267d 100644 --- a/app/code/Magento/Weee/Test/Mftf/Test/AdminFixedTaxValSavedForSpecificWebsiteTest.xml +++ b/app/code/Magento/Weee/Test/Mftf/Test/AdminFixedTaxValSavedForSpecificWebsiteTest.xml @@ -11,6 +11,7 @@ <test name="AdminFixedTaxValSavedForSpecificWebsiteTest"> <annotations> <features value="Tax"/> + <stories value="Website Specific Fixed Product Tax"/> <title value="Fixed Product Tax value is saved correctly for Specific Website"/> <description value="Fixed Product Tax value is saved correctly for Specific Website"/> <severity value="MAJOR"/> @@ -74,7 +75,7 @@ <actionGroup ref="SelectProductInWebsitesActionGroup" stepKey="selectWebsiteInProduct"> <argument name="website" value="{{NewWebSiteData.name}}"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProductFirstTime"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductFirstTime"/> <!-- Add Fixed Product Tax attribute --> <comment userInput="Add Fixed Product Tax attribute" stepKey="addFixedProdTaxAttr"/> <actionGroup ref="AdminProductAddFPTValueActionGroup" stepKey="addFixedProductTaxAttr"> @@ -82,7 +83,7 @@ <argument name="stateForFPT" value="California"/> <argument name="valueForFPT" value="10"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProductSecondTime"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductSecondTime"/> <!-- Check if created tax attribute is saved --> <comment userInput="Check if created tax attribute is saved" stepKey="checkThatTaxAttributeIsSaved"/> <seeElement selector="{{AdminProductAddFPTValueSection.setTaxValueForFPT($$createProductFPTAttribute.attribute_code$$)}}" stepKey="checkIfTaxAttributeSaved"/> @@ -103,10 +104,10 @@ <seeElement selector="{{AdminProductAddFPTValueSection.setWebSiteForFPTOption($$createProductFPTAttribute.attribute_code$$, 'All Websites')}}" stepKey="checkAllWebsitesInDropDown"/> <seeElement selector="{{AdminProductAddFPTValueSection.setWebSiteForFPTOption($$createProductFPTAttribute.attribute_code$$, 'Main Website')}}" stepKey="checkMainWebsiteInDropDown"/> <seeElement selector="{{AdminProductAddFPTValueSection.setWebSiteForFPTOption($$createProductFPTAttribute.attribute_code$$, NewWebSiteData.name)}}" stepKey="checkSecondWebsitesInDropDown"/> - <actionGroup ref="unassignWebsiteFromProductActionGroup" stepKey="unassignWebsiteInProduct"> + <actionGroup ref="UnassignWebsiteFromProductActionGroup" stepKey="unassignWebsiteInProduct"> <argument name="website" value="{{_defaultWebsite.name}}"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProductThirdTime"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductThirdTime"/> <waitForPageLoad stepKey="waitForSavedProductLoad"/> <seeElement selector="{{AdminProductAddFPTValueSection.setWebSiteForFPTOption($$createProductFPTAttribute.attribute_code$$, 'All Websites')}}" stepKey="checkAllWebsitesInDropDownSecondTime"/> <dontSeeElement selector="{{AdminProductAddFPTValueSection.setWebSiteForFPTOption($$createProductFPTAttribute.attribute_code$$, 'Main Website')}}" stepKey="checkNoMainWebsiteInDropDown"/> diff --git a/app/code/Magento/Weee/Test/Mftf/Test/AdminRemoveProductWeeeAttributeOptionTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/AdminRemoveProductWeeeAttributeOptionTest.xml index 3aeed3095dc45..6164f037048ba 100644 --- a/app/code/Magento/Weee/Test/Mftf/Test/AdminRemoveProductWeeeAttributeOptionTest.xml +++ b/app/code/Magento/Weee/Test/Mftf/Test/AdminRemoveProductWeeeAttributeOptionTest.xml @@ -35,12 +35,12 @@ <argument name="stateForFPT" value="California"/> <argument name="valueForFPT" value="10"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProductInitial"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductInitial"/> </before> <after> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductListing"/> <waitForPageLoad stepKey="waitForProductListingPageLoad"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetGridToDefaultKeywordSearch"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> <actionGroup ref="logout" stepKey="logout"/> <deleteData createDataKey="createProductFPTAttribute" stepKey="deleteProductFPTAttribute"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> @@ -56,7 +56,7 @@ </actionGroup> <!-- Step 2: Remove weee attribute options --> <click selector="{{AdminProductAddFPTValueSection.removeRowByIndex('$$createProductFPTAttribute.attribute_code$$','1')}}" stepKey="removeAttributeOption"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <!-- Assert weee attribute options are empty --> <dontSeeElement selector="{{AdminProductAddFPTValueSection.removeRowByIndex('$$createProductFPTAttribute.attribute_code$$','1')}}" stepKey="dontSeeOptions"/> </test> diff --git a/app/code/Magento/Weee/Test/Unit/Plugin/Catalog/Controller/Adminhtml/Product/Initialization/Helper/ProcessTaxAttributeTest.php b/app/code/Magento/Weee/Test/Unit/Plugin/Catalog/Controller/Adminhtml/Product/Initialization/Helper/ProcessTaxAttributeTest.php new file mode 100644 index 0000000000000..2c88d3e0f0251 --- /dev/null +++ b/app/code/Magento/Weee/Test/Unit/Plugin/Catalog/Controller/Adminhtml/Product/Initialization/Helper/ProcessTaxAttributeTest.php @@ -0,0 +1,157 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Weee\Test\Unit\Plugin\Catalog\Controller\Adminhtml\Product\Initialization\Helper; + +use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper; +use Magento\Catalog\Model\Product; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Weee\Plugin\Catalog\Controller\Adminhtml\Product\Initialization\Helper\ProcessTaxAttribute; +use PHPUnit\Framework\MockObject\Matcher\InvokedCount as InvokedCountMatcher; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class ProcessTaxAttributeTest extends TestCase +{ + /** + * Weee frontend input + */ + private const WEEE_FRONTEND_INPUT = 'weee'; + + /** + * Text frontend input + */ + private const TEXT_FRONTEND_INPUT = 'text'; + + /** + * Stub weee attribute code + */ + private const STUB_WEEE_ATTRIBUTE_CODE = 'weee_1'; + + /** + * Stub weee attribute value + */ + private const STUB_WEEE_ATTRIBUTE_VALUE = 1122; + + /** + * @var ProcessTaxAttribute + */ + private $plugin; + + /** + * @var Helper|MockObject + */ + private $subjectMock; + + /** + * @var Product|MockObject + */ + private $productMock; + + /** + * @var Product|MockObject + */ + private $resultMock; + + /** + * Prepare environment for test + */ + protected function setUp() + { + $this->subjectMock = $this->createMock(Helper::class); + $this->resultMock = $this->createMock(Product::class); + $this->productMock = $this->createMock(Product::class); + + $objectManager = new ObjectManager($this); + $this->plugin = $objectManager->getObject(ProcessTaxAttribute::class); + } + + /** + * Test afterInitializeFromData when attributes are empty + */ + public function testAfterInitializeFromDataWhenAttributesAreEmpty() + { + $this->resultMock->expects($this->any())->method('getAttributes') + ->willReturn([]); + + $this->resultMock->expects($this->never())->method('setData'); + + $this->plugin->afterInitializeFromData($this->subjectMock, $this->resultMock, $this->productMock, []); + } + + /** + * Test afterInitializeFromData when attributes do not include weee frontend input + */ + public function testAfterInitializeFromDataWhenAttributesDoNotIncludeWeee() + { + /** @var AbstractAttribute|MockObject $attributeMock */ + $attributeMock = $this->createMock(AbstractAttribute::class); + + $attributeMock->expects($this->any())->method('getFrontendInput') + ->willReturn(self::TEXT_FRONTEND_INPUT); + + $this->resultMock->expects($this->any())->method('getAttributes') + ->willReturn([$attributeMock]); + + $this->resultMock->expects($this->never())->method('setData'); + + $this->plugin->afterInitializeFromData($this->subjectMock, $this->resultMock, $this->productMock, []); + } + + /** + * Test afterInitializeFromData when attributes include weee + * + * @param array $productData + * @param InvokedCountMatcher $expected + * @dataProvider afterInitializeFromDataWhenAttributesIncludeWeeeDataProvider + */ + public function testAfterInitializeFromDataWhenAttributesIncludeWeee($productData, $expected) + { + /** @var AbstractAttribute|MockObject $attributeMock */ + $attributeMock = $this->createMock(AbstractAttribute::class); + + $attributeMock->expects($this->any())->method('getFrontendInput') + ->willReturn(self::WEEE_FRONTEND_INPUT); + $attributeMock->expects($this->any())->method('getAttributeCode') + ->willReturn(self::STUB_WEEE_ATTRIBUTE_CODE); + $this->resultMock->expects($this->any())->method('getAttributes') + ->willReturn([$attributeMock]); + + $this->resultMock->expects($expected)->method('setData') + ->with(self::STUB_WEEE_ATTRIBUTE_CODE, []) + ->willReturnSelf(); + + $this->plugin->afterInitializeFromData( + $this->subjectMock, + $this->resultMock, + $this->productMock, + $productData + ); + } + + /** + * ProductData data provider for testAfterInitializeFromDataWhenAttributesIncludeWeee + * + * @return array + */ + public function afterInitializeFromDataWhenAttributesIncludeWeeeDataProvider() + { + return [ + 'Product data includes wee' => [ + [ + self::STUB_WEEE_ATTRIBUTE_CODE => self::STUB_WEEE_ATTRIBUTE_VALUE + ], + $this->never() + ], + 'Product data does not include wee' => [ + [], + $this->once() + ] + ]; + } +} diff --git a/app/code/Magento/Weee/Test/Unit/Plugin/Ui/DataProvider/WeeeSettingsTest.php b/app/code/Magento/Weee/Test/Unit/Plugin/Ui/DataProvider/WeeeSettingsTest.php new file mode 100644 index 0000000000000..0ac9c55313538 --- /dev/null +++ b/app/code/Magento/Weee/Test/Unit/Plugin/Ui/DataProvider/WeeeSettingsTest.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Weee\Test\Unit\Plugin\Ui\DataProvider; + +use Magento\Catalog\Ui\DataProvider\Product\Listing\DataProvider; +use Magento\Framework\App\Config; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Weee\Model\Config as WeeeConfig; +use Magento\Weee\Plugin\Ui\DataProvider\WeeeSettings; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class WeeeSettingsTest extends TestCase +{ + /** + * Stub settings fpt display product list + */ + private const STUB_FPT_DISPLAY_PRODUCT_LIST = '1'; + + /** + * @var WeeeSettings + */ + private $plugin; + + /** + * @var DataProvider|MockObject + */ + protected $subjectMock; + + /** + * @var Config|MockObject + */ + private $configMock; + + /** + * Prepare environment for test + */ + protected function setUp() + { + $this->configMock = $this->createMock(Config::class); + $this->subjectMock = $this->createMock(DataProvider::class); + + $objectManager = new ObjectManager($this); + $this->plugin = $objectManager->getObject( + WeeeSettings::class, + [ + 'config' => $this->configMock + ] + ); + } + + /** + * Test plugin afterGetData + */ + public function testAfterGetDataWhenConfigIsYesResultIsEmpty() + { + $this->configMock->expects($this->any())->method('getValue') + ->with(WeeeConfig::XML_PATH_FPT_DISPLAY_PRODUCT_LIST) + ->willReturn(self::STUB_FPT_DISPLAY_PRODUCT_LIST); + + $this->assertEquals( + [ + 'displayWeee' => self::STUB_FPT_DISPLAY_PRODUCT_LIST + ], + $this->plugin->afterGetData($this->subjectMock, []) + ); + } +} diff --git a/app/code/Magento/Weee/registration.php b/app/code/Magento/Weee/registration.php index 874705ab5c6b1..73623ce882acb 100644 --- a/app/code/Magento/Weee/registration.php +++ b/app/code/Magento/Weee/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Weee', __DIR__); diff --git a/app/code/Magento/WeeeGraphQl/Test/Unit/FixedProductTaxTest.php b/app/code/Magento/WeeeGraphQl/Test/Unit/FixedProductTaxTest.php new file mode 100644 index 0000000000000..9e5812282545a --- /dev/null +++ b/app/code/Magento/WeeeGraphQl/Test/Unit/FixedProductTaxTest.php @@ -0,0 +1,150 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\WeeeGraphQl\Test\Unit; + +use Magento\Framework\DataObject; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\GraphQl\Model\Query\ContextExtensionInterface; +use Magento\Weee\Helper\Data as WeeeHelper; +use Magento\WeeeGraphQl\Model\Resolver\FixedProductTax; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +class FixedProductTaxTest extends TestCase +{ + const STUB_STORE_ID = 1; + + /** + * @var MockObject|ContextInterface + */ + private $contextMock; + + /** + * @var MockObject|ContextExtensionInterface + */ + private $extensionAttributesMock; + + /** + * @var FixedProductTax + */ + private $resolver; + + /** + * @var MockObject|WeeeHelper + */ + private $weeeHelperMock; + + /** + * @var MockObject|DataObject + */ + private $productMock; + + /** + * Build the Testing Environment + */ + protected function setUp() + { + $this->contextMock = $this->getMockBuilder(ContextInterface::class) + ->setMethods(['getExtensionAttributes']) + ->getMock(); + + $this->extensionAttributesMock = $this->getMockBuilder(ContextExtensionInterface::class) + ->setMethods(['getStore']) + ->getMock(); + + $this->contextMock->method('getExtensionAttributes') + ->willReturn($this->extensionAttributesMock); + + $this->productMock = $this->getMockBuilder(DataObject::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->weeeHelperMock = $this->getMockBuilder(WeeeHelper::class) + ->disableOriginalConstructor() + ->setMethods(['isEnabled', 'getProductWeeeAttributesForDisplay']) + ->getMock(); + + $objectManager = new ObjectManager($this); + $this->resolver = $objectManager->getObject(FixedProductTax::class, [ + 'weeeHelper' => $this->weeeHelperMock + ]); + } + + /** + * Verifies if the Exception is being thrown when no Product Model passed to resolver + */ + public function testExceptionWhenNoModelSpecified(): void + { + $this->expectException(LocalizedException::class); + $this->expectExceptionMessageRegExp('/value should be specified/'); + + $this->resolver->resolve( + $this->getFieldStub(), + null, + $this->getResolveInfoStub() + ); + } + + /** + * Verifies that Attributes for display are not being fetched if feature not enabled in store + */ + public function testNotGettingAttributesWhenWeeeDisabledForStore(): void + { + // Given + $this->extensionAttributesMock->method('getStore') + ->willreturn(self::STUB_STORE_ID); + + // When + $this->weeeHelperMock->method('isEnabled') + ->with(self::STUB_STORE_ID) + ->willReturn(false); + + // Then + $this->weeeHelperMock->expects($this->never()) + ->method('getProductWeeeAttributesForDisplay'); + + $this->resolver->resolve( + $this->getFieldStub(), + $this->contextMock, + $this->getResolveInfoStub(), + ['model' => $this->productMock] + ); + } + + /** + * Returns stub for Field + * + * @return MockObject|Field + */ + private function getFieldStub(): Field + { + /** @var MockObject|Field $fieldMock */ + $fieldMock = $this->getMockBuilder(Field::class) + ->disableOriginalConstructor() + ->getMock(); + return $fieldMock; + } + + /** + * Returns stub for ResolveInfo + * + * @return MockObject|ResolveInfo + */ + private function getResolveInfoStub(): ResolveInfo + { + /** @var MockObject|ResolveInfo $resolveInfoMock */ + $resolveInfoMock = $this->getMockBuilder(ResolveInfo::class) + ->disableOriginalConstructor() + ->getMock(); + return $resolveInfoMock; + } +} diff --git a/app/code/Magento/Widget/Model/Template/FilterEmulate.php b/app/code/Magento/Widget/Model/Template/FilterEmulate.php index 9e3b571587a50..4312003c7f34e 100644 --- a/app/code/Magento/Widget/Model/Template/FilterEmulate.php +++ b/app/code/Magento/Widget/Model/Template/FilterEmulate.php @@ -3,18 +3,44 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Widget\Model\Template; +/** + * Class FilterEmulate + * + * @package Magento\Widget\Model\Template + */ class FilterEmulate extends Filter { /** * Generate widget with emulation frontend area * * @param string[] $construction - * @return string + * + * @return mixed|string + * @throws \Exception */ public function widgetDirective($construction) { return $this->_appState->emulateAreaCode('frontend', [$this, 'generateWidget'], [$construction]); } + + /** + * Filter the string as template with frontend area emulation + * + * @param string $value + * + * @return string + * @throws \Exception + */ + public function filterDirective($value) : string + { + return $this->_appState->emulateAreaCode( + \Magento\Framework\App\Area::AREA_FRONTEND, + [$this, 'filter'], + [$value] + ); + } } diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateAndSaveWidgetActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateAndSaveWidgetActionGroup.xml index 00f593a2d3bc8..5e564fcd799ae 100644 --- a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateAndSaveWidgetActionGroup.xml +++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateAndSaveWidgetActionGroup.xml @@ -12,8 +12,8 @@ <annotations> <description>EXTENDS: AdminCreateWidgetActionGroup. Clicks on Save. Validates that the Success Message is present and correct.</description> </annotations> - + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveWidget"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateDynamicBlocksRotatorWidgetActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateDynamicBlocksRotatorWidgetActionGroup.xml new file mode 100644 index 0000000000000..16efe55202bb0 --- /dev/null +++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateDynamicBlocksRotatorWidgetActionGroup.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="AdminCreateDynamicBlocksRotatorWidgetActionGroup" extends="AdminCreateWidgetActionGroup"> + <annotations> + <description>EXTENDS: AdminCreateWidgetActionGroup. Creates a Dynamic Block Rotate Widget.</description> + </annotations> + + <selectOption selector="{{AdminNewWidgetSection.displayMode}}" userInput="{{widget.display_mode}}" stepKey="selectDisplayMode"/> + <selectOption selector="{{AdminNewWidgetSection.restrictTypes}}" userInput="{{widget.restrict_type}}" stepKey="selectRestrictType"/> + <click selector="{{AdminNewWidgetSection.saveAndContinue}}" stepKey="clickSaveWidget"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateProductLinkWidgetActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateProductLinkWidgetActionGroup.xml new file mode 100644 index 0000000000000..cb82f5ad068fd --- /dev/null +++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateProductLinkWidgetActionGroup.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="AdminCreateProductLinkWidgetActionGroup" extends="AdminCreateWidgetActionGroup"> + <annotations> + <description>EXTENDS: AdminCreateWidgetActionGroup. Creates a Product List Widget using the provided Product. Validates that the Success Message is present and correct.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <selectOption selector="{{AdminNewWidgetSection.selectTemplate}}" userInput="{{widget.template}}" after="waitForPageLoad" stepKey="setTemplate"/> + <waitForAjaxLoad after="setTemplate" stepKey="waitForPageLoad2"/> + <click selector="{{AdminNewWidgetSection.selectProduct}}" after="clickWidgetOptions" stepKey="clickSelectProduct"/> + <fillField selector="{{AdminNewWidgetSelectProductPopupSection.filterBySku}}" userInput="{{product.sku}}" after="clickSelectProduct" stepKey="fillProductNameInFilter"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" after="fillProductNameInFilter" stepKey="applyFilter"/> + <click selector="{{AdminNewWidgetSelectProductPopupSection.firstRow}}" after="applyFilter" stepKey="selectProduct"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveWidget"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateProductsListWidgetActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateProductsListWidgetActionGroup.xml new file mode 100644 index 0000000000000..e3845adc9cd4a --- /dev/null +++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateProductsListWidgetActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateProductsListWidgetActionGroup" extends="AdminCreateWidgetActionGroup"> + <annotations> + <description>EXTENDS: AdminCreateWidgetActionGroup. Creates a Product List Widget. Validates that the Success Message is present and correct.</description> + </annotations> + + <click selector="{{AdminNewWidgetSection.addNewCondition}}" stepKey="clickAddNewCondition"/> + <selectOption selector="{{AdminNewWidgetSection.selectCondition}}" userInput="{{widget.condition}}" stepKey="selectCondition"/> + <waitForElement selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="waitRuleParameter"/> + <click selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="clickRuleParameter"/> + <click selector="{{AdminNewWidgetSection.openChooser}}" stepKey="clickChooser"/> + <waitForAjaxLoad stepKey="waitForAjaxLoad"/> + <click selector="{{AdminNewWidgetSection.selectAll}}" stepKey="clickSelectAll"/> + <click selector="{{AdminNewWidgetSection.applyParameter}}" stepKey="clickApplyRuleParameter"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveWidget"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml index 797abdb6f56ae..e657b3eb73b53 100644 --- a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml +++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml @@ -30,73 +30,4 @@ <scrollToTopOfPage stepKey="scrollToTopOfPage"/> <click selector="{{AdminNewWidgetSection.widgetOptions}}" stepKey="clickWidgetOptions"/> </actionGroup> - - <!--Create Product List Widget--> - <actionGroup name="AdminCreateProductsListWidgetActionGroup" extends="AdminCreateWidgetActionGroup"> - <annotations> - <description>EXTENDS: AdminCreateWidgetActionGroup. Creates a Product List Widget. Validates that the Success Message is present and correct.</description> - </annotations> - - <click selector="{{AdminNewWidgetSection.addNewCondition}}" stepKey="clickAddNewCondition"/> - <selectOption selector="{{AdminNewWidgetSection.selectCondition}}" userInput="{{widget.condition}}" stepKey="selectCondition"/> - <waitForElement selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="waitRuleParameter"/> - <click selector="{{AdminNewWidgetSection.ruleParameter}}" stepKey="clickRuleParameter"/> - <click selector="{{AdminNewWidgetSection.openChooser}}" stepKey="clickChooser"/> - <waitForAjaxLoad stepKey="waitForAjaxLoad"/> - <click selector="{{AdminNewWidgetSection.selectAll}}" stepKey="clickSelectAll"/> - <click selector="{{AdminNewWidgetSection.applyParameter}}" stepKey="clickApplyRuleParameter"/> - <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveWidget"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> - </actionGroup> - - <!--Create Dynamic Block Rotate Widget--> - <actionGroup name="AdminCreateDynamicBlocksRotatorWidgetActionGroup" extends="AdminCreateWidgetActionGroup"> - <annotations> - <description>EXTENDS: AdminCreateWidgetActionGroup. Creates a Dynamic Block Rotate Widget.</description> - </annotations> - - <selectOption selector="{{AdminNewWidgetSection.displayMode}}" userInput="{{widget.display_mode}}" stepKey="selectDisplayMode"/> - <selectOption selector="{{AdminNewWidgetSection.restrictTypes}}" userInput="{{widget.restrict_type}}" stepKey="selectRestrictType"/> - <click selector="{{AdminNewWidgetSection.saveAndContinue}}" stepKey="clickSaveWidget"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> - </actionGroup> - - <actionGroup name="AdminDeleteWidgetActionGroup"> - <annotations> - <description>Goes to the Admin Widget grid page. Deletes the provided Widget. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <argument name="widget"/> - </arguments> - - <amOnPage url="{{AdminWidgetsPage.url}}" stepKey="amOnAdmin"/> - <waitForPageLoad stepKey="waitWidgetsLoad"/> - <fillField selector="{{AdminWidgetsSection.widgetTitleSearch}}" userInput="{{widget.name}}" stepKey="fillTitle"/> - <click selector="{{AdminWidgetsSection.searchButton}}" stepKey="clickContinue"/> - <click selector="{{AdminWidgetsSection.searchResult}}" stepKey="clickSearchResult"/> - <waitForPageLoad stepKey="waitForResultLoad"/> - <click selector="{{AdminMainActionsSection.delete}}" stepKey="clickDelete"/> - <waitForAjaxLoad stepKey="waitForAjaxLoad"/> - <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> - <waitForPageLoad stepKey="waitForDeleteLoad"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="The widget instance has been deleted" stepKey="seeSuccess"/> - </actionGroup> - - <actionGroup name="AdminCreateProductLinkWidgetActionGroup" extends="AdminCreateWidgetActionGroup"> - <annotations> - <description>EXTENDS: AdminCreateWidgetActionGroup. Creates a Product List Widget using the provided Product. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <argument name="product"/> - </arguments> - - <selectOption selector="{{AdminNewWidgetSection.selectTemplate}}" userInput="{{widget.template}}" after="waitForPageLoad" stepKey="setTemplate"/> - <waitForAjaxLoad after="setTemplate" stepKey="waitForPageLoad2"/> - <click selector="{{AdminNewWidgetSection.selectProduct}}" after="clickWidgetOptions" stepKey="clickSelectProduct"/> - <fillField selector="{{AdminNewWidgetSelectProductPopupSection.filterBySku}}" userInput="{{product.sku}}" after="clickSelectProduct" stepKey="fillProductNameInFilter"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" after="fillProductNameInFilter" stepKey="applyFilter"/> - <click selector="{{AdminNewWidgetSelectProductPopupSection.firstRow}}" after="applyFilter" stepKey="selectProduct"/> - <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveWidget"/> - <see selector="{{AdminMessagesSection.successMessage}}" userInput="The widget instance has been saved" stepKey="seeSuccess"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminDeleteWidgetActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminDeleteWidgetActionGroup.xml new file mode 100644 index 0000000000000..889c8a9477534 --- /dev/null +++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminDeleteWidgetActionGroup.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="AdminDeleteWidgetActionGroup"> + <annotations> + <description>Goes to the Admin Widget grid page. Deletes the provided Widget. Validates that the Success Message is present and correct.</description> + </annotations> + <arguments> + <argument name="widget"/> + </arguments> + + <amOnPage url="{{AdminWidgetsPage.url}}" stepKey="amOnAdmin"/> + <waitForPageLoad stepKey="waitWidgetsLoad"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> + <fillField selector="{{AdminWidgetsSection.widgetTitleSearch}}" userInput="{{widget.name}}" stepKey="fillTitle"/> + <click selector="{{AdminWidgetsSection.searchButton}}" stepKey="clickContinue"/> + <click selector="{{AdminWidgetsSection.searchResult}}" stepKey="clickSearchResult"/> + <waitForPageLoad stepKey="waitForResultLoad"/> + <click selector="{{AdminMainActionsSection.delete}}" stepKey="clickDelete"/> + <waitForAjaxLoad stepKey="waitForAjaxLoad"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete"/> + <waitForPageLoad stepKey="waitForDeleteLoad"/> + <see selector="{{AdminMessagesSection.success}}" userInput="The widget instance has been deleted" stepKey="seeSuccess"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminFillSpecificPageWidgetMainFieldsActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminFillSpecificPageWidgetMainFieldsActionGroup.xml new file mode 100644 index 0000000000000..c0fa53da7c688 --- /dev/null +++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminFillSpecificPageWidgetMainFieldsActionGroup.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="AdminFillSpecificPageWidgetMainFieldsActionGroup"> + <annotations> + <description>Fill widget main fields and widget layout by index for specified page DisplayOn option</description> + </annotations> + <arguments> + <argument name="widget" type="entity" defaultValue="ProductsListWidget"/> + <argument name="index" type="string" defaultValue="0"/> + </arguments> + <selectOption selector="{{AdminNewWidgetSection.widgetType}}" userInput="{{widget.type}}" stepKey="setWidgetType"/> + <selectOption selector="{{AdminNewWidgetSection.widgetDesignTheme}}" userInput="{{widget.design_theme}}" stepKey="setWidgetDesignTheme"/> + <click selector="{{AdminNewWidgetSection.continue}}" stepKey="clickContinue"/> + <fillField selector="{{AdminNewWidgetSection.widgetTitle}}" userInput="{{widget.name}}" stepKey="fillTitle"/> + <selectOption selector="{{AdminNewWidgetSection.widgetStoreIds}}" parameterArray="{{widget.store_ids}}" stepKey="setWidgetStoreIds"/> + <fillField selector="{{AdminNewWidgetSection.widgetSortOrder}}" userInput="{{widget.sort_order}}" stepKey="fillSortOrder"/> + <click selector="{{AdminNewWidgetSection.addLayoutUpdate}}" stepKey="clickAddLayoutUpdate"/> + <waitForElementVisible selector="{{AdminNewWidgetSection.selectDisplayOn}}" stepKey="waitForSelectElement"/> + <selectOption selector="{{AdminNewWidgetSection.displayOnByIndex(index)}}" userInput="{{widget.display_on}}" stepKey="setDisplayOn"/> + <waitForPageLoad stepKey="waitForDisplayOnChangesApplied"/> + <selectOption selector="{{AdminNewWidgetSection.layoutByIndex(index)}}" userInput="{{widget.page}}" stepKey="selectPage"/> + <selectOption selector="{{AdminNewWidgetSection.templateByIndex(index)}}" userInput="{{widget.template}}" stepKey="selectTemplate"/> + <scrollTo selector="{{AdminNewWidgetSection.containerByIndex(index)}}" stepKey="scrollToSelectContainerElement"/> + <waitForPageLoad stepKey="waitForScroll"/> + <selectOption selector="{{AdminNewWidgetSection.containerByIndex(index)}}" userInput="{{widget.container}}" stepKey="setContainer"/> + <waitForPageLoad stepKey="waitForContainerChangesApplied"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml b/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml index eebd6c10b5085..0777e6cbd58d9 100644 --- a/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml +++ b/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml @@ -11,12 +11,17 @@ <section name="AdminNewWidgetSection"> <element name="widgetType" type="select" selector="#code"/> <element name="widgetDesignTheme" type="select" selector="#theme_id"/> - <element name="continue" type="button" selector="#continue_button"/> + <element name="continue" type="button" timeout="30" selector="#continue_button"/> <element name="widgetTitle" type="input" selector="#title"/> <element name="widgetStoreIds" type="select" selector="#store_ids"/> + <element name="widgetSortOrder" type="input" selector="#sort_order"/> <element name="addLayoutUpdate" type="button" selector=".action-default.scalable.action-add"/> <element name="selectDisplayOn" type="select" selector="#widget_instance[0][page_group]"/> <element name="selectContainer" type="select" selector="#all_pages_0>table>tbody>tr>td:nth-child(1)>div>div>select"/> + <element name="displayOnByIndex" type="select" selector="select[name='widget_instance[{{index}}][page_group]']" parameterized="true"/> + <element name="layoutByIndex" type="select" selector="select[name='widget_instance[{{index}}][pages][layout_handle]']" parameterized="true"/> + <element name="containerByIndex" type="select" selector="select[name='widget_instance[{{index}}][pages][block]']" parameterized="true"/> + <element name="templateByIndex" type="select" selector="select[name='widget_instance[{{index}}][pages][template]']" parameterized="true"/> <element name="selectTemplate" type="select" selector=".widget-layout-updates .block_template_container .select"/> <element name="widgetOptions" type="select" selector="#widget_instace_tabs_properties_section"/> <element name="addNewCondition" type="select" selector=".rule-param.rule-param-new-child"/> diff --git a/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml b/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml index c63e76d851933..e2fb9cb50f88a 100644 --- a/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml +++ b/app/code/Magento/Widget/Test/Mftf/Test/NewProductsListWidgetTest.xml @@ -23,7 +23,7 @@ <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> </before> <after> @@ -49,4 +49,4 @@ <fillField selector="{{CmsNewPagePageSeoSection.urlKey}}" userInput="{{_newDefaultCmsPage.identifier}}" stepKey="fillPageUrlKey"/> <click selector="{{CmsNewPagePageActionsSection.saveAndContinueEdit}}" stepKey="clickSaveCmsPage"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Widget/registration.php b/app/code/Magento/Widget/registration.php index 27e924eba367e..c7821664fdaca 100644 --- a/app/code/Magento/Widget/registration.php +++ b/app/code/Magento/Widget/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Widget', __DIR__); diff --git a/app/code/Magento/Wishlist/Controller/Index/Cart.php b/app/code/Magento/Wishlist/Controller/Index/Cart.php index da37609d688e7..870c4231f97c9 100644 --- a/app/code/Magento/Wishlist/Controller/Index/Cart.php +++ b/app/code/Magento/Wishlist/Controller/Index/Cart.php @@ -186,7 +186,7 @@ public function execute() 'You added %1 to your shopping cart.', $this->escaper->escapeHtml($item->getProduct()->getName()) ); - $this->messageManager->addSuccess($message); + $this->messageManager->addSuccessMessage($message); } if ($this->cartHelper->getShouldRedirectToCart()) { @@ -214,7 +214,7 @@ public function execute() $resultJson->setData(['backUrl' => $redirectUrl]); return $resultJson; } - + $resultRedirect->setUrl($redirectUrl); return $resultRedirect; } diff --git a/app/code/Magento/Wishlist/Controller/Index/Configure.php b/app/code/Magento/Wishlist/Controller/Index/Configure.php index 93a05ffc0ec93..a273a37deeff3 100644 --- a/app/code/Magento/Wishlist/Controller/Index/Configure.php +++ b/app/code/Magento/Wishlist/Controller/Index/Configure.php @@ -11,9 +11,11 @@ use Magento\Framework\Controller\ResultFactory; /** + * Wishlist Configure Controller + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Configure extends \Magento\Wishlist\Controller\AbstractIndex +class Configure extends \Magento\Wishlist\Controller\AbstractIndex implements Action\HttpGetActionInterface { /** * Core registry @@ -102,11 +104,11 @@ public function execute() return $resultPage; } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $resultRedirect->setPath('*'); return $resultRedirect; } catch (\Exception $e) { - $this->messageManager->addError(__('We can\'t configure the product right now.')); + $this->messageManager->addErrorMessage(__('We can\'t configure the product right now.')); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); $resultRedirect->setPath('*'); return $resultRedirect; diff --git a/app/code/Magento/Wishlist/Controller/Index/Remove.php b/app/code/Magento/Wishlist/Controller/Index/Remove.php index 84c59b5be3d1e..ea798a8b57cda 100644 --- a/app/code/Magento/Wishlist/Controller/Index/Remove.php +++ b/app/code/Magento/Wishlist/Controller/Index/Remove.php @@ -14,9 +14,11 @@ use Magento\Wishlist\Model\Product\AttributeValueProvider; /** + * Wishlist Remove Controller + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Remove extends \Magento\Wishlist\Controller\AbstractIndex +class Remove extends \Magento\Wishlist\Controller\AbstractIndex implements Action\HttpPostActionInterface { /** * @var WishlistProviderInterface @@ -88,11 +90,11 @@ public function execute() ] ); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __('We can\'t delete the item from Wish List right now because of an error: %1.', $e->getMessage()) ); } catch (\Exception $e) { - $this->messageManager->addError(__('We can\'t delete the item from the Wish List right now.')); + $this->messageManager->addErrorMessage(__('We can\'t delete the item from the Wish List right now.')); } $this->_objectManager->get(\Magento\Wishlist\Helper\Data::class)->calculate(); diff --git a/app/code/Magento/Wishlist/Controller/Index/Send.php b/app/code/Magento/Wishlist/Controller/Index/Send.php index 5d867ac74752b..283400a6f94ea 100644 --- a/app/code/Magento/Wishlist/Controller/Index/Send.php +++ b/app/code/Magento/Wishlist/Controller/Index/Send.php @@ -25,9 +25,8 @@ use Magento\Customer\Model\Customer; /** - * Class Send + * Class Send Email Wishlist Controller * - * @package Magento\Wishlist\Controller\Index * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Send extends \Magento\Wishlist\Controller\AbstractIndex implements Action\HttpPostActionInterface @@ -204,7 +203,7 @@ public function execute() $error = __('Please enter an email address.'); } else { if (count($emails) > $emailsLeft) { - $error = __('This wish list can be shared %1 more times.', $emailsLeft); + $error = __('Maximum of %1 emails can be sent.', $emailsLeft); } else { foreach ($emails as $index => $email) { $email = trim($email); @@ -219,7 +218,7 @@ public function execute() } if ($error) { - $this->messageManager->addError($error); + $this->messageManager->addErrorMessage($error); $this->wishlistSession->setSharingForm($this->getRequest()->getPostValue()); $resultRedirect->setPath('*/*/share'); return $resultRedirect; @@ -285,12 +284,12 @@ public function execute() $this->inlineTranslation->resume(); $this->_eventManager->dispatch('wishlist_share', ['wishlist' => $wishlist]); - $this->messageManager->addSuccess(__('Your wish list has been shared.')); + $this->messageManager->addSuccessMessage(__('Your wish list has been shared.')); $resultRedirect->setPath('*/*', ['wishlist_id' => $wishlist->getId()]); return $resultRedirect; } catch (\Exception $e) { $this->inlineTranslation->resume(); - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); $this->wishlistSession->setSharingForm($this->getRequest()->getPostValue()); $resultRedirect->setPath('*/*/share'); return $resultRedirect; @@ -319,7 +318,6 @@ protected function addLayoutHandles(ResultLayout $resultLayout) * * @param int $wishlistId * @param \Magento\Framework\View\Result\Layout $resultLayout - * @return mixed */ protected function getRssLink($wishlistId, ResultLayout $resultLayout) { diff --git a/app/code/Magento/Wishlist/Controller/Index/Update.php b/app/code/Magento/Wishlist/Controller/Index/Update.php index b56aa4b5b3c8d..e5fbd4b93f82e 100644 --- a/app/code/Magento/Wishlist/Controller/Index/Update.php +++ b/app/code/Magento/Wishlist/Controller/Index/Update.php @@ -103,7 +103,7 @@ public function execute() $item->delete(); } catch (\Exception $e) { $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); - $this->messageManager->addError(__('We can\'t delete item from Wish List right now.')); + $this->messageManager->addErrorMessage(__('We can\'t delete item from Wish List right now.')); } } @@ -118,7 +118,7 @@ public function execute() ); $updatedItems++; } catch (\Exception $e) { - $this->messageManager->addError( + $this->messageManager->addErrorMessage( __( 'Can\'t save description %1', $this->_objectManager->get(\Magento\Framework\Escaper::class)->escapeHtml($description) @@ -133,7 +133,7 @@ public function execute() $wishlist->save(); $this->_objectManager->get(\Magento\Wishlist\Helper\Data::class)->calculate(); } catch (\Exception $e) { - $this->messageManager->addError(__('Can\'t update wish list')); + $this->messageManager->addErrorMessage(__('Can\'t update wish list')); } } diff --git a/app/code/Magento/Wishlist/Controller/Index/UpdateItemOptions.php b/app/code/Magento/Wishlist/Controller/Index/UpdateItemOptions.php index 2a98fa1b7fcd5..6fae77fd604e5 100644 --- a/app/code/Magento/Wishlist/Controller/Index/UpdateItemOptions.php +++ b/app/code/Magento/Wishlist/Controller/Index/UpdateItemOptions.php @@ -14,9 +14,11 @@ use Magento\Wishlist\Controller\WishlistProviderInterface; /** + * Wishlist UpdateItemOptions Controller + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class UpdateItemOptions extends \Magento\Wishlist\Controller\AbstractIndex +class UpdateItemOptions extends \Magento\Wishlist\Controller\AbstractIndex implements Action\HttpPostActionInterface { /** * @var WishlistProviderInterface @@ -85,7 +87,7 @@ public function execute() } if (!$product || !$product->isVisibleInCatalog()) { - $this->messageManager->addError(__('We can\'t specify a product.')); + $this->messageManager->addErrorMessage(__('We can\'t specify a product.')); $resultRedirect->setPath('*/'); return $resultRedirect; } @@ -114,11 +116,11 @@ public function execute() $this->_objectManager->get(\Magento\Wishlist\Helper\Data::class)->calculate(); $message = __('%1 has been updated in your Wish List.', $product->getName()); - $this->messageManager->addSuccess($message); + $this->messageManager->addSuccessMessage($message); } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); } catch (\Exception $e) { - $this->messageManager->addError(__('We can\'t update your Wish List right now.')); + $this->messageManager->addErrorMessage(__('We can\'t update your Wish List right now.')); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); } $resultRedirect->setPath('*/*', ['wishlist_id' => $wishlist->getId()]); diff --git a/app/code/Magento/Wishlist/Controller/Shared/Cart.php b/app/code/Magento/Wishlist/Controller/Shared/Cart.php index b41b51057636f..38f100602972a 100644 --- a/app/code/Magento/Wishlist/Controller/Shared/Cart.php +++ b/app/code/Magento/Wishlist/Controller/Shared/Cart.php @@ -9,6 +9,7 @@ use Magento\Checkout\Helper\Cart as CartHelper; use Magento\Checkout\Model\Cart as CustomerCart; use Magento\Framework\App\Action\Context as ActionContext; +use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\Controller\ResultFactory; use Magento\Framework\Escaper; use Magento\Framework\Exception\LocalizedException; @@ -18,9 +19,11 @@ use Magento\Wishlist\Model\ResourceModel\Item\Option\Collection as OptionCollection; /** + * Wishlist Cart Controller + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Cart extends \Magento\Framework\App\Action\Action +class Cart extends \Magento\Framework\App\Action\Action implements HttpGetActionInterface { /** * @var CustomerCart @@ -103,19 +106,19 @@ public function execute() 'You added %1 to your shopping cart.', $this->escaper->escapeHtml($item->getProduct()->getName()) ); - $this->messageManager->addSuccess($message); + $this->messageManager->addSuccessMessage($message); } if ($this->cartHelper->getShouldRedirectToCart()) { $redirectUrl = $this->cartHelper->getCartUrl(); } } catch (ProductException $e) { - $this->messageManager->addError(__('This product(s) is out of stock.')); + $this->messageManager->addErrorMessage(__('This product(s) is out of stock.')); } catch (LocalizedException $e) { - $this->messageManager->addNotice($e->getMessage()); + $this->messageManager->addNoticeMessage($e->getMessage()); $redirectUrl = $item->getProductUrl(); } catch (\Exception $e) { - $this->messageManager->addException($e, __('We can\'t add the item to the cart right now.')); + $this->messageManager->addExceptionMessage($e, __('We can\'t add the item to the cart right now.')); } /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); diff --git a/app/code/Magento/Wishlist/Controller/WishlistProvider.php b/app/code/Magento/Wishlist/Controller/WishlistProvider.php index 4740ea9947ef7..ae59d3a13d6eb 100644 --- a/app/code/Magento/Wishlist/Controller/WishlistProvider.php +++ b/app/code/Magento/Wishlist/Controller/WishlistProvider.php @@ -1,13 +1,18 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Wishlist\Controller; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\App\RequestInterface; +/** + * WishlistProvider Controller + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) + */ class WishlistProvider implements WishlistProviderInterface { /** @@ -54,7 +59,8 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc + * * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function getWishlist($wishlistId = null) @@ -85,10 +91,10 @@ public function getWishlist($wishlistId = null) ); } } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { - $this->messageManager->addError($e->getMessage()); + $this->messageManager->addErrorMessage($e->getMessage()); return false; } catch (\Exception $e) { - $this->messageManager->addException($e, __('We can\'t create the Wish List right now.')); + $this->messageManager->addExceptionMessage($e, __('We can\'t create the Wish List right now.')); return false; } $this->wishlist = $wishlist; diff --git a/app/code/Magento/Wishlist/Model/ItemCarrier.php b/app/code/Magento/Wishlist/Model/ItemCarrier.php index 6cf295084eca8..6198eb289a0f4 100644 --- a/app/code/Magento/Wishlist/Model/ItemCarrier.php +++ b/app/code/Magento/Wishlist/Model/ItemCarrier.php @@ -10,6 +10,7 @@ use Magento\Checkout\Helper\Cart as CartHelper; use Magento\Checkout\Model\Cart; use Magento\Customer\Model\Session; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\App\Response\RedirectInterface; use Magento\Framework\Exception\LocalizedException; use Psr\Log\LoggerInterface as Logger; @@ -18,7 +19,10 @@ use Magento\Wishlist\Helper\Data as WishlistHelper; /** + * Wishlist ItemCarrier Controller + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class ItemCarrier { @@ -182,7 +186,7 @@ public function moveAllToCart(Wishlist $wishlist, $qtys) if ($messages) { foreach ($messages as $message) { - $this->messageManager->addError($message); + $this->messageManager->addErrorMessage($message); } $redirectUrl = $indexUrl; } @@ -192,7 +196,7 @@ public function moveAllToCart(Wishlist $wishlist, $qtys) try { $wishlist->save(); } catch (\Exception $e) { - $this->messageManager->addError(__('We can\'t update the Wish List right now.')); + $this->messageManager->addErrorMessage(__('We can\'t update the Wish List right now.')); $redirectUrl = $indexUrl; } @@ -202,7 +206,7 @@ public function moveAllToCart(Wishlist $wishlist, $qtys) $products[] = '"' . $product->getName() . '"'; } - $this->messageManager->addSuccess( + $this->messageManager->addSuccessMessage( __('%1 product(s) have been added to shopping cart: %2.', count($addedProducts), join(', ', $products)) ); diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/AssertMoveProductToWishListSuccessMessageActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/AssertMoveProductToWishListSuccessMessageActionGroup.xml new file mode 100644 index 0000000000000..37d26f984b3eb --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/AssertMoveProductToWishListSuccessMessageActionGroup.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="AssertMoveProductToWishListSuccessMessageActionGroup"> + <annotations> + <description>Moves a product from the cart to the wishlist.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + </arguments> + <click selector="{{CheckoutCartProductSection.moveToWishlistByProductName(productName)}}" stepKey="moveToWishlist"/> + <waitForPageLoad stepKey="waitForMove"/> + <see userInput="{{productName}} has been moved to your wish list." selector="{{CheckoutCartMessageSection.successMessage}}" stepKey="assertSuccess"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/AssertProductDetailsInWishlistActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/AssertProductDetailsInWishlistActionGroup.xml new file mode 100644 index 0000000000000..4498882a206d1 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/AssertProductDetailsInWishlistActionGroup.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="AssertProductIsPresentInWishListActionGroup"> + <annotations> + <description>Go to storefront customer wishlist page and assert product name and price is present.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + <argument name="productPrice" type="string"/> + </arguments> + <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="goToWishList"/> + <waitForPageLoad stepKey="waitForWishList"/> + <waitForElement selector="{{StorefrontCustomerWishlistProductSection.ProductTitleByName(productName)}}" time="30" stepKey="assertProductName"/> + <see userInput="{{productPrice}}" selector="{{StorefrontCustomerWishlistProductSection.ProductPriceByName(productName)}}" stepKey="assertProductPrice"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/AssertProductIsPresentInWishListActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/AssertProductIsPresentInWishListActionGroup.xml new file mode 100644 index 0000000000000..85c91e1a0f14c --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/AssertProductIsPresentInWishListActionGroup.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertProductDetailsInWishlistActionGroup"> + <annotations> + <description>Assert product name and price in wishlist on hover.</description> + </annotations> + <arguments> + <argument name="productName" type="string"/> + <argument name="label" type="string"/> + <argument name="labelValue" type="string"/> + </arguments> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName(productName)}}" stepKey="moveMouseOverProductInfo"/> + <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductAddToCartByName(productName)}}" stepKey="seeAddToCart"/> + <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductImageByName(productName)}}" stepKey="seeImage"/> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsByName(productName)}}" stepKey="moveMouseOverProductDetails"/> + <see userInput="{{label}}" selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsLabelByName(productName)}}" stepKey="seeLabel"/> + <see userInput="{{labelValue}}" selector="{{StorefrontCustomerWishlistProductSection.productSeeDetailsValueByName(productName)}}" stepKey="seeLabelValue"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontAssertCustomerWishlistIsEmptyActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontAssertCustomerWishlistIsEmptyActionGroup.xml new file mode 100644 index 0000000000000..fad33a405c5e4 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontAssertCustomerWishlistIsEmptyActionGroup.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="StorefrontAssertCustomerWishlistIsEmptyActionGroup"> + <dontSeeElement selector="{{StorefrontCustomerWishlistProductSection.pager}}" stepKey="checkThatPagerIsAbsent"/> + <see selector="{{StorefrontCustomerWishlistProductSection.wishlistEmpty}}" userInput="You have no items in your wish list." stepKey="checkNoItemsMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerAddCategoryProductToWishlistActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerAddCategoryProductToWishlistActionGroup.xml new file mode 100644 index 0000000000000..336841901a7ec --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerAddCategoryProductToWishlistActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCustomerAddCategoryProductToWishlistActionGroup"> + <annotations> + <description>Adds the provided Product to the Wish List from a Storefront Category page. Validates that the Success Message is present and correct.</description> + </annotations> + <arguments> + <argument name="productVar"/> + </arguments> + + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(productVar.name)}}" stepKey="addCategoryProductToWishlistMoveMouseOverProduct"/> + <click selector="{{StorefrontCategoryProductSection.ProductAddToWishlistByName(productVar.name)}}" stepKey="addCategoryProductToWishlistClickAddProductToWishlist"/> + <waitForElement selector="{{StorefrontCustomerWishlistSection.successMsg}}" time="30" stepKey="addCategoryProductToWishlistWaitForSuccessMessage"/> + <see selector="{{StorefrontCustomerWishlistSection.successMsg}}" userInput="{{productVar.name}} has been added to your Wish List." stepKey="addCategoryProductToWishlistSeeProductNameAddedToWishlist"/> + <seeCurrentUrlMatches regex="~/wishlist_id/\d+/$~" stepKey="seeCurrentUrlMatches"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerAddProductToCartFromWishlistUsingSidebarActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerAddProductToCartFromWishlistUsingSidebarActionGroup.xml new file mode 100644 index 0000000000000..a28a80c57fb67 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerAddProductToCartFromWishlistUsingSidebarActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCustomerAddProductToCartFromWishlistUsingSidebarActionGroup"> + <annotations> + <description>Add the provided Product to the Cart from the Wish List side bar menu. Validates that the Success Message is present and correct.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <click selector="{{StorefrontCustomerWishlistSidebarSection.ProductAddToCartByName(product.name)}}" stepKey="AddProductToCartFromWishlistUsingSidebarClickAddToCartFromWishlist"/> + <waitForElement selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="AddProductToCartFromWishlistUsingSidebarWaitForSuccessMessage"/> + <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added {{product.name}} to your shopping cart." stepKey="AddProductToCartFromWishlistUsingSidebarSeeProductNameAddedToCartFromWishlist"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerAddProductToWishlistActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerAddProductToWishlistActionGroup.xml new file mode 100644 index 0000000000000..69d8365096999 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerAddProductToWishlistActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCustomerAddProductToWishlistActionGroup"> + <annotations> + <description>Adds the provided Product to the Wish List from the Storefront Product page. Validates that the Success Message is present and correct.</description> + </annotations> + <arguments> + <argument name="productVar"/> + </arguments> + + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.productAddToWishlist}}" stepKey="WaitForWishList" time="30"/> + <click selector="{{StorefrontProductInfoMainSection.productAddToWishlist}}" stepKey="addProductToWishlistClickAddToWishlist"/> + <waitForElement selector="{{StorefrontCustomerWishlistSection.successMsg}}" time="30" stepKey="addProductToWishlistWaitForSuccessMessage"/> + <see selector="{{StorefrontCustomerWishlistSection.successMsg}}" userInput="{{productVar.name}} has been added to your Wish List. Click here to continue shopping." stepKey="addProductToWishlistSeeProductNameAddedToWishlist"/> + <seeCurrentUrlMatches regex="~/wishlist_id/\d+/$~" stepKey="seeCurrentUrlMatches"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerCheckProductInWishlistActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerCheckProductInWishlistActionGroup.xml new file mode 100644 index 0000000000000..7380ee2172532 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerCheckProductInWishlistActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCustomerCheckProductInWishlistActionGroup"> + <annotations> + <description>Validates that the provided Product details (Price and Name) are present in the Storefront Customer Dashboard Wish List.</description> + </annotations> + <arguments> + <argument name="productVar"/> + </arguments> + + <waitForElement selector="{{StorefrontCustomerWishlistProductSection.ProductTitleByName(productVar.name)}}" time="30" stepKey="assertWishlistProductName"/> + <see userInput="${{productVar.price}}.00" selector="{{StorefrontCustomerWishlistProductSection.ProductPriceByName(productVar.name)}}" stepKey="AssertWishlistProductPrice"/> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName(productVar.name)}}" stepKey="wishlistMoveMouseOverProduct"/> + <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductAddToCartByName(productVar.name)}}" stepKey="AssertWishlistAddToCart"/> + <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductImageByName(productVar.name)}}" stepKey="AssertWishlistProductImage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerCheckProductInWishlistSidebarActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerCheckProductInWishlistSidebarActionGroup.xml new file mode 100644 index 0000000000000..04277897c0cb2 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerCheckProductInWishlistSidebarActionGroup.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="StorefrontCustomerCheckProductInWishlistSidebarActionGroup"> + <annotations> + <description>Validates that the provided Product details (Name) are present in the Wish List side bar menu.</description> + </annotations> + <arguments> + <argument name="productVar"/> + </arguments> + + <waitForElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductTitleByName(productVar.name)}}" time="30" stepKey="assertWishlistSidebarProductName"/> + <see userInput="${{productVar.price}}.00" selector="{{StorefrontCustomerWishlistSidebarSection.ProductPriceByName(productVar.name)}}" stepKey="AssertWishlistSidebarProductPrice"/> + <seeElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductAddToCartByName(productVar.name)}}" stepKey="AssertWishlistSidebarAddToCart"/> + <seeElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductImageByName(productVar.name)}}" stepKey="AssertWishlistSidebarProductImage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerEditProductInWishlistActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerEditProductInWishlistActionGroup.xml new file mode 100644 index 0000000000000..f4fe023411e67 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerEditProductInWishlistActionGroup.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="StorefrontCustomerEditProductInWishlistActionGroup"> + <annotations> + <description>Edits the provided Product on the Storefront Wish List page. Fills in the provided Description and Quantity. Validates that the Success Message is present and correct.</description> + </annotations> + <arguments> + <argument name="product"/> + <argument name="description" type="string"/> + <argument name="quantity" type="string"/> + </arguments> + + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName(product.name)}}" stepKey="mouseOverOnProduct"/> + <fillField selector="{{StorefrontCustomerWishlistProductSection.ProductDescription(product.name)}}" userInput="{{description}}" stepKey="fillDescription"/> + <fillField selector="{{StorefrontCustomerWishlistProductSection.ProductQuantity(product.name)}}" userInput="{{quantity}}" stepKey="fillQuantity"/> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductAddAllToCart}}" stepKey="mouseOver"/> + <click selector="{{StorefrontCustomerWishlistProductSection.ProductUpdateWishList}}" stepKey="submitUpdateWishlist"/> + <see selector="{{StorefrontCustomerWishlistProductSection.ProductSuccessUpdateMessage}}" userInput="{{product.name}} has been updated in your Wish List." stepKey="successMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerRemoveProductFromWishlistUsingSidebarActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerRemoveProductFromWishlistUsingSidebarActionGroup.xml new file mode 100644 index 0000000000000..a563e57c25230 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerRemoveProductFromWishlistUsingSidebarActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontCustomerRemoveProductFromWishlistUsingSidebarActionGroup"> + <annotations> + <description>Removes the provided Product from the Wish List side bar menu. Validates that the Success Message is present and correct.</description> + </annotations> + <arguments> + <argument name="product"/> + </arguments> + + <click selector="{{StorefrontCustomerWishlistSidebarSection.ProductRemoveByName(product.name)}}" stepKey="RemoveProductFromWishlistUsingSidebarClickRemoveItemFromWishlist"/> + <waitForElement selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="RemoveProductFromWishlistUsingSidebarWaitForSuccessMessage"/> + <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="{{product.name}} has been removed from your Wish List." stepKey="RemoveProductFromWishlistUsingSidebarSeeProductNameRemovedFromWishlist"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerShareWishlistActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerShareWishlistActionGroup.xml new file mode 100644 index 0000000000000..1f7ac9fc85f50 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerShareWishlistActionGroup.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="StorefrontCustomerShareWishlistActionGroup"> + <annotations> + <description>Shares the Wish List from the Storefront Wish List page. PLEASE NOTE: The details for sharing are Hardcoded using 'Wishlist'.</description> + </annotations> + + <click selector="{{StorefrontCustomerWishlistProductSection.productShareWishList}}" stepKey="clickMyWishListButton"/> + <fillField userInput="{{Wishlist.shareInfo_emails}}" selector="{{StorefrontCustomerWishlistShareSection.ProductShareWishlistEmail}}" stepKey="fillEmailsForShare"/> + <fillField userInput="{{Wishlist.shareInfo_message}}" selector="{{StorefrontCustomerWishlistShareSection.ProductShareWishlistTextMessage}}" stepKey="fillShareMessage"/> + <click selector="{{StorefrontCustomerWishlistShareSection.ProductShareWishlistButton}}" stepKey="sendWishlist"/> + <see selector="{{StorefrontCustomerWishlistProductSection.productSuccessShareMessage}}" userInput="Your wish list has been shared." stepKey="successMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml deleted file mode 100644 index cda7f5f3267b0..0000000000000 --- a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerWishlistActionGroup.xml +++ /dev/null @@ -1,138 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!-- Add Product to wishlist from the category page and check message --> - <actionGroup name="StorefrontCustomerAddCategoryProductToWishlistActionGroup"> - <annotations> - <description>Adds the provided Product to the Wish List from a Storefront Category page. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <argument name="productVar"/> - </arguments> - - <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(productVar.name)}}" stepKey="addCategoryProductToWishlistMoveMouseOverProduct"/> - <click selector="{{StorefrontCategoryProductSection.ProductAddToWishlistByName(productVar.name)}}" stepKey="addCategoryProductToWishlistClickAddProductToWishlist"/> - <waitForElement selector="{{StorefrontCustomerWishlistSection.successMsg}}" time="30" stepKey="addCategoryProductToWishlistWaitForSuccessMessage"/> - <see selector="{{StorefrontCustomerWishlistSection.successMsg}}" userInput="{{productVar.name}} has been added to your Wish List." stepKey="addCategoryProductToWishlistSeeProductNameAddedToWishlist"/> - <seeCurrentUrlMatches regex="~/wishlist_id/\d+/$~" stepKey="seeCurrentUrlMatches"/> - </actionGroup> - - <!-- Add Product to wishlist from the product page and check message --> - <actionGroup name="StorefrontCustomerAddProductToWishlistActionGroup"> - <annotations> - <description>Adds the provided Product to the Wish List from the Storefront Product page. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <argument name="productVar"/> - </arguments> - - <waitForElementVisible selector="{{StorefrontProductInfoMainSection.productAddToWishlist}}" stepKey="WaitForWishList"/> - <click selector="{{StorefrontProductInfoMainSection.productAddToWishlist}}" stepKey="addProductToWishlistClickAddToWishlist"/> - <waitForElement selector="{{StorefrontCustomerWishlistSection.successMsg}}" time="30" stepKey="addProductToWishlistWaitForSuccessMessage"/> - <see selector="{{StorefrontCustomerWishlistSection.successMsg}}" userInput="{{productVar.name}} has been added to your Wish List. Click here to continue shopping." stepKey="addProductToWishlistSeeProductNameAddedToWishlist"/> - <seeCurrentUrlMatches regex="~/wishlist_id/\d+/$~" stepKey="seeCurrentUrlMatches"/> - </actionGroup> - - <!-- Check product in wishlist --> - <actionGroup name="StorefrontCustomerCheckProductInWishlist"> - <annotations> - <description>Validates that the provided Product details (Price and Name) are present in the Storefront Customer Dashboard Wish List.</description> - </annotations> - <arguments> - <argument name="productVar"/> - </arguments> - - <waitForElement selector="{{StorefrontCustomerWishlistProductSection.ProductTitleByName(productVar.name)}}" time="30" stepKey="assertWishlistProductName"/> - <see userInput="${{productVar.price}}.00" selector="{{StorefrontCustomerWishlistProductSection.ProductPriceByName(productVar.name)}}" stepKey="AssertWishlistProductPrice"/> - <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName(productVar.name)}}" stepKey="wishlistMoveMouseOverProduct"/> - <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductAddToCartByName(productVar.name)}}" stepKey="AssertWishlistAddToCart"/> - <seeElement selector="{{StorefrontCustomerWishlistProductSection.ProductImageByName(productVar.name)}}" stepKey="AssertWishlistProductImage"/> - </actionGroup> - - <!-- Check product in wishlist sidebar --> - <actionGroup name="StorefrontCustomerCheckProductInWishlistSidebar"> - <annotations> - <description>Validates that the provided Product details (Name) are present in the Wish List side bar menu.</description> - </annotations> - <arguments> - <argument name="productVar"/> - </arguments> - - <waitForElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductTitleByName(productVar.name)}}" time="30" stepKey="assertWishlistSidebarProductName"/> - <see userInput="${{productVar.price}}.00" selector="{{StorefrontCustomerWishlistSidebarSection.ProductPriceByName(productVar.name)}}" stepKey="AssertWishlistSidebarProductPrice"/> - <seeElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductAddToCartByName(productVar.name)}}" stepKey="AssertWishlistSidebarAddToCart"/> - <seeElement selector="{{StorefrontCustomerWishlistSidebarSection.ProductImageByName(productVar.name)}}" stepKey="AssertWishlistSidebarProductImage"/> - </actionGroup> - - <!--Remove a product from the wishlist using the sidebar --> - <actionGroup name="StorefrontCustomerRemoveProductFromWishlistUsingSidebar"> - <annotations> - <description>Removes the provided Product from the Wish List side bar menu. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <argument name="product"/> - </arguments> - - <click selector="{{StorefrontCustomerWishlistSidebarSection.ProductRemoveByName(product.name)}}" stepKey="RemoveProductFromWishlistUsingSidebarClickRemoveItemFromWishlist"/> - <waitForElement selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="RemoveProductFromWishlistUsingSidebarWaitForSuccessMessage"/> - <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="{{product.name}} has been removed from your Wish List." stepKey="RemoveProductFromWishlistUsingSidebarSeeProductNameRemovedFromWishlist"/> - </actionGroup> - - <!--Add a product to the cart from the wishlist using the sidebar --> - <actionGroup name="StorefrontCustomerAddProductToCartFromWishlistUsingSidebar"> - <annotations> - <description>Add the provided Product to the Cart from the Wish List side bar menu. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <argument name="product"/> - </arguments> - - <click selector="{{StorefrontCustomerWishlistSidebarSection.ProductAddToCartByName(product.name)}}" stepKey="AddProductToCartFromWishlistUsingSidebarClickAddToCartFromWishlist"/> - <waitForElement selector="{{StorefrontCategoryMainSection.SuccessMsg}}" time="30" stepKey="AddProductToCartFromWishlistUsingSidebarWaitForSuccessMessage"/> - <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added {{product.name}} to your shopping cart." stepKey="AddProductToCartFromWishlistUsingSidebarSeeProductNameAddedToCartFromWishlist"/> - </actionGroup> - - <actionGroup name="StorefrontCustomerEditProductInWishlist"> - <annotations> - <description>Edits the provided Product on the Storefront Wish List page. Fills in the provided Description and Quantity. Validates that the Success Message is present and correct.</description> - </annotations> - <arguments> - <argument name="product"/> - <argument name="description" type="string"/> - <argument name="quantity" type="string"/> - </arguments> - - <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName(product.name)}}" stepKey="mouseOverOnProduct"/> - <fillField selector="{{StorefrontCustomerWishlistProductSection.ProductDescription(product.name)}}" userInput="{{description}}" stepKey="fillDescription"/> - <fillField selector="{{StorefrontCustomerWishlistProductSection.ProductQuantity(product.name)}}" userInput="{{quantity}}" stepKey="fillQuantity"/> - <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductAddAllToCart}}" stepKey="mouseOver"/> - <click selector="{{StorefrontCustomerWishlistProductSection.ProductUpdateWishList}}" stepKey="submitUpdateWishlist"/> - <see selector="{{StorefrontCustomerWishlistProductSection.ProductSuccessUpdateMessage}}" userInput="{{product.name}} has been updated in your Wish List." stepKey="successMessage"/> - </actionGroup> - - <!-- Share wishlist --> - <actionGroup name="StorefrontCustomerShareWishlistActionGroup"> - <annotations> - <description>Shares the Wish List from the Storefront Wish List page. PLEASE NOTE: The details for sharing are Hardcoded using 'Wishlist'.</description> - </annotations> - - <click selector="{{StorefrontCustomerWishlistProductSection.productShareWishList}}" stepKey="clickMyWishListButton"/> - <fillField userInput="{{Wishlist.shareInfo_emails}}" selector="{{StorefrontCustomerWishlistShareSection.ProductShareWishlistEmail}}" stepKey="fillEmailsForShare"/> - <fillField userInput="{{Wishlist.shareInfo_message}}" selector="{{StorefrontCustomerWishlistShareSection.ProductShareWishlistTextMessage}}" stepKey="fillShareMessage"/> - <click selector="{{StorefrontCustomerWishlistShareSection.ProductShareWishlistButton}}" stepKey="sendWishlist"/> - <see selector="{{StorefrontCustomerWishlistProductSection.productSuccessShareMessage}}" userInput="Your wish list has been shared." stepKey="successMessage"/> - </actionGroup> - - <!-- Check that wishlist is empty --> - <actionGroup name="StorefrontAssertCustomerWishlistIsEmpty"> - <dontSeeElement selector="{{StorefrontCustomerWishlistProductSection.pager}}" stepKey="checkThatPagerIsAbsent"/> - <see selector="{{StorefrontCustomerWishlistProductSection.wishlistEmpty}}" userInput="You have no items in your wish list." stepKey="checkNoItemsMessage"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml index 0b6c2f1191c40..e75b29944117b 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistProductSection.xml @@ -24,5 +24,9 @@ <element name="productSuccessShareMessage" type="text" selector="div.message-success"/> <element name="pager" type="block" selector=".toolbar .pager"/> <element name="wishlistEmpty" type="block" selector=".form-wishlist-items .message.info.empty"/> + <element name="removeProduct" type="button" selector=".products-grid a.btn-remove" timeout="30"/> + <element name="productSeeDetailsByName" type="block" selector="//a[contains(text(), '{{productName}}')]/ancestor::div/div[contains(@class, 'product-item-tooltip')]" parameterized="true"/> + <element name="productSeeDetailsLabelByName" type="block" selector="//a[contains(text(), '{{productName}}')]/ancestor::div/div[contains(@class, 'product-item-tooltip')]//dt[@class='label']" parameterized="true"/> + <element name="productSeeDetailsValueByName" type="block" selector="//a[contains(text(), '{{productName}}')]/ancestor::div/div[contains(@class, 'product-item-tooltip')]//dd[@class='values']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/AdminCustomerWishListShareOptionsInputValidationTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/AdminCustomerWishListShareOptionsInputValidationTest.xml index da51bdf917e37..32c16ff7f5a55 100755 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/AdminCustomerWishListShareOptionsInputValidationTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/AdminCustomerWishListShareOptionsInputValidationTest.xml @@ -26,7 +26,7 @@ <argument name="emailTextLengthLimit" value="{{Wishlist.default_email_text_length_limit}}"/> </actionGroup> <checkOption selector="{{WishListShareOptionsSection.useSystemValueForWishListEmailTextLimit}}" stepKey="checkUseSystemValueForWishListEmailTextLimit"/> - <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> </after> <actionGroup ref="setEmailTextLengthLimitActionGroup" stepKey="setEmailTextLengthLimitToMin"> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfProdAddToCartWishListWithUnselectedAttrTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfProdAddToCartWishListWithUnselectedAttrTest.xml index 4e6a062c7993d..d1d9a439c335d 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfProdAddToCartWishListWithUnselectedAttrTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfProdAddToCartWishListWithUnselectedAttrTest.xml @@ -23,7 +23,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> <createData entity="ApiCategory" stepKey="createCategory"/> <!--Create Configurable product--> - <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> <argument name="product" value="_defaultProduct"/> <argument name="category" value="$$createCategory$$"/> </actionGroup> @@ -33,7 +33,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <!-- Delete the first simple product --> - <actionGroup stepKey="deleteProduct1" ref="deleteProductBySku"> + <actionGroup stepKey="deleteProduct1" ref="DeleteProductBySkuActionGroup"> <argument name="sku" value="{{_defaultProduct.sku}}"/> </actionGroup> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml index 0489ec750b7e0..c9c539d215165 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/ConfigurableProductChildImageShouldBeShownOnWishListTest.xml @@ -23,7 +23,7 @@ <createData entity="ApiCategory" stepKey="createCategory"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> - <actionGroup ref="createConfigurableProduct" stepKey="createProduct"> + <actionGroup ref="CreateConfigurableProductActionGroup" stepKey="createProduct"> <argument name="product" value="_defaultProduct"/> <argument name="category" value="$$createCategory$$"/> </actionGroup> @@ -36,24 +36,24 @@ </after> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetFiltersIfPresent"/> - <actionGroup ref="searchProductGridByKeyword" stepKey="searchProductGrid"> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetFiltersIfPresent"/> + <actionGroup ref="SearchProductGridByKeywordActionGroup" stepKey="searchProductGrid"> <argument name="keyword" value="_defaultProduct.name"/> </actionGroup> <click selector="{{AdminProductGridSection.selectRowBasedOnName(_defaultProduct.name)}}" stepKey="selectProductToAddImage"/> <waitForPageLoad stepKey="waitForProductEditPageLoad"/> - <actionGroup ref="addProductImage" stepKey="addImageForParentProduct"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImageForParentProduct"> <argument name="image" value="MagentoLogo"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex1"/> <waitForPageLoad stepKey="waitForProductIndexPageLoad1"/> <click selector="{{AdminProductGridSection.selectRowBasedOnName(colorProductAttribute1.name)}}" stepKey="selectProductToAddImage1"/> <waitForPageLoad stepKey="waitForProductEditPageLoad1"/> - <actionGroup ref="addProductImage" stepKey="addImageForChildProduct"> + <actionGroup ref="AddProductImageActionGroup" stepKey="addImageForChildProduct"> <argument name="image" value="TestImageNew"/> </actionGroup> - <actionGroup ref="saveProductForm" stepKey="saveSimpleProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveSimpleProduct"/> <!--Sign in as customer --> <amOnPage url="{{StorefrontCustomerSignInPage.url}}" stepKey="amOnSignInPage"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index 7eb42d1fbfed9..d5a5fccb30be3 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -17,10 +17,10 @@ <actionGroup ref="StorefrontCustomerAddCategoryProductToWishlistActionGroup" after="wishlistGotoCategory1" stepKey="wishlistAddSimpleProduct1ToWishlist"> <argument name="productVar" value="$$createSimpleProduct1$$"/> </actionGroup> - <actionGroup ref="StorefrontCustomerCheckProductInWishlist" after="wishlistAddSimpleProduct1ToWishlist" stepKey="wishlistCheckSimpleProduct1InWishlist"> + <actionGroup ref="StorefrontCustomerCheckProductInWishlistActionGroup" after="wishlistAddSimpleProduct1ToWishlist" stepKey="wishlistCheckSimpleProduct1InWishlist"> <argument name="productVar" value="$$createSimpleProduct1$$"/> </actionGroup> - <actionGroup ref="StorefrontCustomerCheckProductInWishlistSidebar" after="wishlistCheckSimpleProduct1InWishlist" stepKey="wishlistCheckSimpleProduct1InWishlistSidebar"> + <actionGroup ref="StorefrontCustomerCheckProductInWishlistSidebarActionGroup" after="wishlistCheckSimpleProduct1InWishlist" stepKey="wishlistCheckSimpleProduct1InWishlistSidebar"> <argument name="productVar" value="$$createSimpleProduct1$$"/> </actionGroup> @@ -31,10 +31,10 @@ <actionGroup ref="StorefrontCustomerAddProductToWishlistActionGroup" after="wishlistClickSimpleProduct2" stepKey="wishlistAddSimpleProduct2ToWishlist"> <argument name="productVar" value="$$createSimpleProduct2$$"/> </actionGroup> - <actionGroup ref="StorefrontCustomerCheckProductInWishlist" after="wishlistAddSimpleProduct2ToWishlist" stepKey="wishlistCheckSimpleProduct2InWishlist"> + <actionGroup ref="StorefrontCustomerCheckProductInWishlistActionGroup" after="wishlistAddSimpleProduct2ToWishlist" stepKey="wishlistCheckSimpleProduct2InWishlist"> <argument name="productVar" value="$$createSimpleProduct2$$"/> </actionGroup> - <actionGroup ref="StorefrontCustomerCheckProductInWishlistSidebar" after="wishlistCheckSimpleProduct2InWishlist" stepKey="wishlistCheckSimpleProduct2InWishlistSidebar"> + <actionGroup ref="StorefrontCustomerCheckProductInWishlistSidebarActionGroup" after="wishlistCheckSimpleProduct2InWishlist" stepKey="wishlistCheckSimpleProduct2InWishlistSidebar"> <argument name="productVar" value="$$createSimpleProduct2$$"/> </actionGroup> <comment userInput="End of adding products to wishlist" after="wishlistCheckSimpleProduct2InWishlistSidebar" stepKey="endOfAddingProductsToWishlist" /> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml index aeb1d134d8e22..82021e75e0983 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml @@ -42,11 +42,11 @@ <actionGroup ref="DeleteCustomStoreActionGroup" stepKey="deleteCustomStoreGroup"> <argument name="storeGroupName" value="$$storeGroup.group[name]$$"/> </actionGroup> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearWebsitesGridFilter"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearWebsitesGridFilter"/> <!--Clear products filter--> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearProductsFilters"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsFilters"/> <!--Logout everywhere--> <actionGroup ref="logout" stepKey="adminLogout"/> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml index 16a18dd27b123..a6cab4b9f9715 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddProductsToCartFromWishlistUsingSidebarTest.xml @@ -26,12 +26,16 @@ <requiredEntity createDataKey="categorySecond"/> </createData> <createData entity="Simple_US_Customer" stepKey="customer"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> <deleteData createDataKey="categoryFirst" stepKey="deleteCategoryFirst"/> <deleteData createDataKey="categorySecond" stepKey="deleteCategorySecond"/> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> <deleteData createDataKey="customer" stepKey="deleteCustomer"/> </after> <!-- Sign in as customer --> @@ -40,7 +44,7 @@ </actionGroup> <!-- Add product from first category to the wishlist --> <amOnPage url="{{StorefrontCategoryPage.url($$categoryFirst.name$$)}}" stepKey="navigateToCategoryFirstPage"/> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="browseAssertCategoryProduct1"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct1"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <actionGroup ref="StorefrontCustomerAddCategoryProductToWishlistActionGroup" stepKey="addSimpleProduct1ToWishlist"> @@ -48,11 +52,11 @@ </actionGroup> <!--Add product to the cart from the Wishlist using the sidebar from the second category page--> <amOnPage url="{{StorefrontCategoryPage.url($$categorySecond.name$$)}}" stepKey="navigateToCategorySecondPage"/> - <actionGroup ref="StorefrontSwitchCategoryViewToListMode" stepKey="switchCategoryViewToListMode"/> - <actionGroup ref="StorefrontCustomerCheckProductInWishlistSidebar" stepKey="checkSimpleProduct1InWishlistSidebar"> + <actionGroup ref="StorefrontSwitchCategoryViewToListModeActionGroup" stepKey="switchCategoryViewToListMode"/> + <actionGroup ref="StorefrontCustomerCheckProductInWishlistSidebarActionGroup" stepKey="checkSimpleProduct1InWishlistSidebar"> <argument name="productVar" value="$$simpleProduct1$$"/> </actionGroup> - <actionGroup ref="StorefrontCustomerAddProductToCartFromWishlistUsingSidebar" stepKey="addProduct1ToCartFromWishlistUsingSidebar"> + <actionGroup ref="StorefrontCustomerAddProductToCartFromWishlistUsingSidebarActionGroup" stepKey="addProduct1ToCartFromWishlistUsingSidebar"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <!--Check that a customer on the same page as before--> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleDynamicProductFromWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleDynamicProductFromWishlistTest.xml new file mode 100644 index 0000000000000..f5d7eb4c6ed02 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleDynamicProductFromWishlistTest.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontDeleteBundleDynamicProductFromWishlistTest"> + <annotations> + <stories value="Wishlist"/> + <title value="Delete Dynamic Bundle Product from Wishlist on Frontend"/> + <description value="Delete Dynamic Bundle Product from Wishlist on Frontend"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14215"/> + <group value="wishlist"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"> + <field key="price">100.00</field> + </createData> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"> + <field key="price">100.00</field> + </createData> + <!--Create Bundle product--> + <createData entity="BundleProductPriceViewRange" stepKey="createBundleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="DropDownBundleOption" stepKey="createBundleOption1_1"> + <requiredEntity createDataKey="createBundleProduct"/> + <field key="required">True</field> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct2"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToProduct"> + <argument name="productId" value="$$createBundleProduct.id$$"/> + </actionGroup> + <scrollTo selector="{{AdminProductFormBundleSection.contentDropDown}}" stepKey="scrollToBundleSection"/> + <selectOption userInput="Separately" selector="{{AdminProductFormBundleSection.shipmentType}}" stepKey="selectSeparately"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteProduct2"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Login as a customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Navigate to catalog page --> + <actionGroup ref="OpenStoreFrontProductPageActionGroup" stepKey="openProductFromCategory"> + <argument name="productUrlKey" value="$$createBundleProduct.custom_attributes[url_key]$$"/> + </actionGroup> + + <!-- Add created product to Wishlist according to dataset and assert add product to wishlist success message --> + <actionGroup ref="StorefrontCustomerAddProductToWishlistActionGroup" stepKey="addProductToWishlist"> + <argument name="productVar" value="$$createBundleProduct$$"/> + </actionGroup> + + <!-- Navigate to My Account > My Wishlist --> + <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="amOnWishListPage"/> + <waitForPageLoad stepKey="waitForWishlistPageLoad"/> + + <!-- Click "Remove item" --> + <scrollTo selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName($$createBundleProduct.name$$)}}" stepKey="scrollToProduct"/> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName($$createBundleProduct.name$$)}}" stepKey="mouseOverOnProduct"/> + <click selector="{{StorefrontCustomerWishlistProductSection.removeProduct}}" stepKey="clickRemoveButton"/> + + <!-- Assert Wishlist is empty --> + <actionGroup ref="StorefrontAssertCustomerWishlistIsEmptyActionGroup" stepKey="assertWishlistIsEmpty"/> + </test> +</tests> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleFixedProductFromWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleFixedProductFromWishlistTest.xml new file mode 100644 index 0000000000000..cdd86bfecccc8 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteBundleFixedProductFromWishlistTest.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="StorefrontDeleteBundleFixedProductFromWishlistTest"> + <annotations> + <stories value="Wishlist"/> + <title value="Delete Fixed Bundle Product from Wishlist on Frontend"/> + <description value="Delete Fixed Bundle Product from Wishlist on Frontend"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14216"/> + <group value="wishlist"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"> + <field key="price">100.00</field> + </createData> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"> + <field key="price">100.00</field> + </createData> + <!-- Create bundle product --> + <createData entity="ApiFixedBundleProduct" stepKey="createBundleProduct"/> + <createData entity="DropDownBundleOption" stepKey="createBundleOption1_1"> + <requiredEntity createDataKey="createBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink"> + <field key="price_type">0</field> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct2"> + <field key="price_type">0</field> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + <magentoCLI stepKey="reindex" command="indexer:reindex"/> + <magentoCLI stepKey="flushCache" command="cache:flush"/> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteProduct2"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Login as a customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Navigate to catalog page --> + <actionGroup ref="OpenStoreFrontProductPageActionGroup" stepKey="openProductFromCategory"> + <argument name="productUrlKey" value="$$createBundleProduct.custom_attributes[url_key]$$"/> + </actionGroup> + + <!-- Add created product to Wishlist according to dataset and assert add product to wishlist success message --> + <actionGroup ref="StorefrontCustomerAddProductToWishlistActionGroup" stepKey="addProductToWishlist"> + <argument name="productVar" value="$$createBundleProduct$$"/> + </actionGroup> + + <!-- Navigate to My Account > My Wishlist --> + <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="amOnWishListPage"/> + <waitForPageLoad stepKey="waitForWishlistPageLoad"/> + + <!-- Click "Remove item" --> + <scrollTo selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName($$createBundleProduct.name$$)}}" stepKey="scrollToProduct"/> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName($$createBundleProduct.name$$)}}" stepKey="mouseOverOnProduct"/> + <click selector="{{StorefrontCustomerWishlistProductSection.removeProduct}}" stepKey="clickRemoveButton"/> + + <!-- Assert Wishlist is empty --> + <actionGroup ref="StorefrontAssertCustomerWishlistIsEmptyActionGroup" stepKey="assertWishlistIsEmpty"/> + </test> +</tests> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromWishlistTest.xml new file mode 100644 index 0000000000000..6a718ebdfcf0f --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromWishlistTest.xml @@ -0,0 +1,150 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontDeleteConfigurableProductFromWishlistTest"> + <annotations> + <stories value="Wishlist"/> + <title value="Delete Configurable Product from Wishlist on Frontend"/> + <description value="Delete Configurable Product from Wishlist on Frontend"/> + <severity value="MAJOR"/> + <testCaseId value="MC-14217"/> + <group value="wishlist"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="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> + <magentoCLI stepKey="reindex" command="indexer:reindex"/> + <magentoCLI stepKey="flushCache" command="cache:flush"/> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="createConfigChildProduct3" stepKey="deleteSimpleProduct3"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteProductAttribute"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- 1. Login as a customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- 2. Navigate to catalog page --> + <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="openProductFromCategory"> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + + <!-- 3. Add created product to Wishlist according to dataset and assert add product to wishlist success message --> + <actionGroup ref="StorefrontCustomerAddCategoryProductToWishlistActionGroup" stepKey="addProductToWishlist"> + <argument name="productVar" value="$$createConfigProduct$$"/> + </actionGroup> + + <!-- Navigate to My Account > My Wishlist --> + <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="amOnWishListPage"/> + <waitForPageLoad stepKey="waitForWishlistPageLoad"/> + + <!-- Click "Remove item" --> + <scrollTo selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName($$createConfigProduct.name$$)}}" stepKey="scrollToProduct"/> + <moveMouseOver selector="{{StorefrontCustomerWishlistProductSection.ProductInfoByName($$createConfigProduct.name$$)}}" stepKey="mouseOverOnProduct"/> + <click selector="{{StorefrontCustomerWishlistProductSection.removeProduct}}" stepKey="clickRemoveButton"/> + + <!-- Assert Wishlist is empty --> + <actionGroup ref="StorefrontAssertCustomerWishlistIsEmptyActionGroup" stepKey="assertWishlistIsEmpty"/> + </test> +</tests> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml new file mode 100644 index 0000000000000..0f94a08328c95 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest.xml @@ -0,0 +1,166 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontMoveConfigurableProductFromShoppingCartToWishlistTest"> + <annotations> + <stories value="Wishlist"/> + <title value="Move Configurable Product from Shopping Cart to Wishlist"/> + <description value="Move Configurable Product from Shopping Cart to Wishlist"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14211"/> + <group value="wishlist"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <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> + + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="createConfigChildProduct3" stepKey="deleteSimpleProduct3"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteProductAttribute"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- 1. Login as a customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Open Product page --> + <actionGroup ref="OpenProductFromCategoryPageActionGroup" stepKey="openProductFromCategory"> + <argument name="category" value="$$createCategory$$"/> + <argument name="product" value="$$createConfigProduct$$"/> + </actionGroup> + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption2.label$$" stepKey="selectOption1"/> + <scrollTo selector="{{StorefrontProductInfoMainSection.productAddToWishlist}}" y="-200" stepKey="scroll"/> + + <!-- Add product to the cart and Assert add product to cart success message--> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartVirtualProductFromStorefrontProductPage"> + <argument name="productName" value="$$createConfigProduct.name$$"/> + </actionGroup> + + <!-- Select Mini Cart and select 'View And Edit Cart' --> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="selectViewAndEditCart"/> + + <!-- Assert move product to wishlist success message --> + <actionGroup ref="AssertMoveProductToWishListSuccessMessageActionGroup" stepKey="moveToWishlist"> + <argument name="productName" value="$$createConfigProduct.name$$"/> + </actionGroup> + + <!-- Assert product is present in wishlist --> + <actionGroup ref="AssertProductIsPresentInWishListActionGroup" stepKey="assertProductPresent"> + <argument name="productName" value="$$createConfigProduct.name$$"/> + <argument name="productPrice" value="$20.00"/> + </actionGroup> + + <!-- Assert product details in Wishlist --> + <actionGroup ref="AssertProductDetailsInWishlistActionGroup" stepKey="assertProductDetails"> + <argument name="productName" value="$$createConfigProduct.name$$"/> + <argument name="label" value="$$createConfigProductAttribute.default_value$$"/> + <argument name="labelValue" value="$$getConfigAttributeOption2.label$$"/> + </actionGroup> + + <actionGroup ref="AssertShoppingCartIsEmptyActionGroup" stepKey="assertCartIsEmpty"/> + </test> +</tests> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml new file mode 100644 index 0000000000000..3a823efbcdf61 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest.xml @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontMoveDynamicBundleProductFromShoppingCartToWishlistTest"> + <annotations> + <stories value="Wishlist"/> + <title value="Move Dynamic Bundle Product from Shopping Cart to Wishlist"/> + <description value="Move Dynamic Bundle Product from Shopping Cart to Wishlist"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14212"/> + <group value="wishlist"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"> + <field key="price">100.00</field> + </createData> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"> + <field key="price">20.00</field> + </createData> + <!--Create Bundle product--> + <createData entity="ApiBundleProductPriceViewRange" stepKey="createBundleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="DropDownBundleOption" stepKey="createBundleOption1_1"> + <requiredEntity createDataKey="createBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct2"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="GoToProductPageViaIDActionGroup" stepKey="goToProduct"> + <argument name="productId" value="$$createBundleProduct.id$$"/> + </actionGroup> + <scrollTo selector="{{AdminProductFormBundleSection.contentDropDown}}" stepKey="scrollToBundleSection"/> + <selectOption userInput="Separately" selector="{{AdminProductFormBundleSection.shipmentType}}" stepKey="selectSeparately"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + <magentoCLI stepKey="reindex" command="indexer:reindex"/> + <magentoCLI stepKey="flushCache" command="cache:flush"/> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteProduct2"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- 1. Login as a customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Open Product page --> + <actionGroup ref="OpenStoreFrontProductPageActionGroup" stepKey="openProductFromCategory"> + <argument name="productUrlKey" value="$$createBundleProduct.custom_attributes[url_key]$$"/> + </actionGroup> + <actionGroup ref="StorefrontSelectCustomizeAndAddToTheCartButtonActionGroup" stepKey="clickCustomizeButton"/> + <selectOption selector="{{StorefrontBundledSection.dropDownOptionOneProducts($$createBundleOption1_1.title$$)}}" userInput="$$simpleProduct1.sku$$ +$100.00" stepKey="selectOption0Product0"/> + <fillField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity($$createBundleOption1_1.title$$)}}" userInput="1" stepKey="fillQuantity00"/> + + <!-- Add product to the cart and Assert add product to cart success message--> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartVirtualProductFromStorefrontProductPage"> + <argument name="productName" value="$$createBundleProduct.name$$"/> + </actionGroup> + + <!-- Select Mini Cart and select 'View And Edit Cart' --> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="selectViewAndEditCart"/> + + <!-- Assert move product to wishlist success message --> + <actionGroup ref="AssertMoveProductToWishListSuccessMessageActionGroup" stepKey="moveToWishlist"> + <argument name="productName" value="$$createBundleProduct.name$$"/> + </actionGroup> + + <!-- Assert product is present in wishlist --> + <actionGroup ref="AssertProductIsPresentInWishListActionGroup" stepKey="assertProductPresent"> + <argument name="productName" value="$$createBundleProduct.name$$"/> + <argument name="productPrice" value="$100.00"/> + </actionGroup> + + <!-- Assert product details in Wishlist --> + <actionGroup ref="AssertProductDetailsInWishlistActionGroup" stepKey="assertProductDetails"> + <argument name="productName" value="$$createBundleProduct.name$$"/> + <argument name="label" value="$$createBundleOption1_1.title$$"/> + <argument name="labelValue" value="$$simpleProduct1.sku$$ $100.00"/> + </actionGroup> + + <actionGroup ref="AssertShoppingCartIsEmptyActionGroup" stepKey="assertCartIsEmpty"/> + </test> +</tests> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveFixedBundleProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveFixedBundleProductFromShoppingCartToWishlistTest.xml new file mode 100644 index 0000000000000..2d83043f81318 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveFixedBundleProductFromShoppingCartToWishlistTest.xml @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontMoveFixedBundleProductFromShoppingCartToWishlistTest"> + <annotations> + <stories value="Wishlist"/> + <title value="Move Fixed Bundle Product from Shopping Cart to Wishlist"/> + <description value="Move Fixed Bundle Product from Shopping Cart to Wishlist"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14213"/> + <group value="wishlist"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <!-- Create bundle product --> + <createData entity="ApiFixedBundleProduct" stepKey="createBundleProduct"/> + <createData entity="DropDownBundleOption" stepKey="createBundleOption1_1"> + <requiredEntity createDataKey="createBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink"> + <field key="price_type">0</field> + <field key="price">100</field> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct2"> + <field key="price_type">0</field> + <field key="price">100</field> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct2"/> + </createData> + <magentoCLI stepKey="reindex" command="indexer:reindex"/> + <magentoCLI stepKey="flushCache" command="cache:flush"/> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteProduct2"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- 1. Login as a customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Open Product page --> + <actionGroup ref="OpenStoreFrontProductPageActionGroup" stepKey="openProductFromCategory"> + <argument name="productUrlKey" value="$$createBundleProduct.custom_attributes[url_key]$$"/> + </actionGroup> + <actionGroup ref="StorefrontSelectCustomizeAndAddToTheCartButtonActionGroup" stepKey="clickCustomizeButton"/> + <selectOption selector="{{StorefrontBundledSection.dropDownOptionOneProducts($$createBundleOption1_1.title$$)}}" userInput="$$simpleProduct1.sku$$ +$100.00" stepKey="selectOption0Product0"/> + <fillField selector="{{StorefrontBundledSection.dropDownOptionOneQuantity($$createBundleOption1_1.title$$)}}" userInput="1" stepKey="fillQuantity00"/> + + <!-- Add product to the cart and Assert add product to cart success message--> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartVirtualProductFromStorefrontProductPage"> + <argument name="productName" value="$$createBundleProduct.name$$"/> + </actionGroup> + + <!-- Select Mini Cart and select 'View And Edit Cart' --> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="selectViewAndEditCart"/> + + <!-- Assert move product to wishlist success message --> + <actionGroup ref="AssertMoveProductToWishListSuccessMessageActionGroup" stepKey="moveToWishlist"> + <argument name="productName" value="$$createBundleProduct.name$$"/> + </actionGroup> + + <!-- Assert product is present in wishlist --> + <actionGroup ref="AssertProductIsPresentInWishListActionGroup" stepKey="assertProductPresent"> + <argument name="productName" value="$$createBundleProduct.name$$"/> + <argument name="productPrice" value="$101.23"/> + </actionGroup> + + <!-- Assert product details in Wishlist --> + <actionGroup ref="AssertProductDetailsInWishlistActionGroup" stepKey="assertProductDetails"> + <argument name="productName" value="$$createBundleProduct.name$$"/> + <argument name="label" value="$$createBundleOption1_1.title$$"/> + <argument name="labelValue" value="$$simpleProduct1.sku$$ $100.00"/> + </actionGroup> + + <actionGroup ref="AssertShoppingCartIsEmptyActionGroup" stepKey="assertCartIsEmpty"/> + </test> +</tests> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveVirtualProductFromShoppingCartToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveVirtualProductFromShoppingCartToWishlistTest.xml new file mode 100644 index 0000000000000..ad10b8d01bbbd --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontMoveVirtualProductFromShoppingCartToWishlistTest.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontMoveVirtualProductFromShoppingCartToWishlistTest"> + <annotations> + <stories value="Wishlist"/> + <title value="Move Virtual Product from Shopping Cart to Wishlist"/> + <description value="Move Virtual Product from Shopping Cart to Wishlist"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14210"/> + <group value="wishlist"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="defaultVirtualProduct" stepKey="createProduct"> + <field key="price">40</field> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- 1. Login as a customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Open Virtual Product page --> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="OpenStoreFrontProductPage"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + + <!-- Add Virtual product to the cart and Assert add product to cart success message--> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartVirtualProductFromStorefrontProductPage"> + <argument name="productName" value="$$createProduct.name$$"/> + </actionGroup> + + <!-- Select Mini Cart and select 'View And Edit Cart' --> + <actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="selectViewAndEditCart"/> + + <!-- Assert move product to wishlist success message --> + <actionGroup ref="AssertMoveProductToWishListSuccessMessageActionGroup" stepKey="moveToWishlist"> + <argument name="productName" value="$$createProduct.name$$"/> + </actionGroup> + + <!-- Assert product is present in wishlist --> + <actionGroup ref="AssertProductIsPresentInWishListActionGroup" stepKey="assertProductPresent"> + <argument name="productName" value="$$createProduct.name$$"/> + <argument name="productPrice" value="$$createProduct.price$$"/> + </actionGroup> + + <!-- Assert cart is empty --> + <actionGroup ref="AssertShoppingCartIsEmptyActionGroup" stepKey="assertCartIsEmpty"/> + </test> +</tests> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml index 6c73cb6708ae4..97551a596e978 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontRemoveProductsFromWishlistUsingSidebarTest.xml @@ -45,7 +45,7 @@ </actionGroup> <!-- Add product from first category to the wishlist --> <amOnPage url="{{StorefrontCategoryPage.url($$categoryFirst.name$$)}}" stepKey="navigateToCategoryFirstPage"/> - <actionGroup ref="StorefrontCheckCategorySimpleProduct" stepKey="browseAssertCategoryProduct1"> + <actionGroup ref="StorefrontCheckCategorySimpleProductActionGroup" stepKey="browseAssertCategoryProduct1"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <actionGroup ref="StorefrontCustomerAddCategoryProductToWishlistActionGroup" stepKey="addSimpleProduct1ToWishlist"> @@ -53,11 +53,11 @@ </actionGroup> <!--Remove product from the Wishlist using the sidebar from the second category page--> <amOnPage url="{{StorefrontCategoryPage.url($$categorySecond.name$$)}}" stepKey="navigateToCategorySecondPage"/> - <actionGroup ref="StorefrontSwitchCategoryViewToListMode" stepKey="switchCategoryViewToListMode"/> - <actionGroup ref="StorefrontCustomerCheckProductInWishlistSidebar" stepKey="checkSimpleProduct1InWishlistSidebar"> + <actionGroup ref="StorefrontSwitchCategoryViewToListModeActionGroup" stepKey="switchCategoryViewToListMode"/> + <actionGroup ref="StorefrontCustomerCheckProductInWishlistSidebarActionGroup" stepKey="checkSimpleProduct1InWishlistSidebar"> <argument name="productVar" value="$$simpleProduct1$$"/> </actionGroup> - <actionGroup ref="StorefrontCustomerRemoveProductFromWishlistUsingSidebar" stepKey="removeProduct1FromWishlistUsingSidebar"> + <actionGroup ref="StorefrontCustomerRemoveProductFromWishlistUsingSidebarActionGroup" stepKey="removeProduct1FromWishlistUsingSidebar"> <argument name="product" value="$$simpleProduct1$$"/> </actionGroup> <!--Check that a customer on the same page as before--> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontShareWishlistEntityTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontShareWishlistEntityTest.xml index 87c5ed950949f..bb566ef2d03a4 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontShareWishlistEntityTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontShareWishlistEntityTest.xml @@ -26,6 +26,10 @@ <requiredEntity createDataKey="category"/> </createData> <createData entity="Simple_US_Customer" stepKey="customer"/> + + <!-- Perform reindex and flush cache --> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> </before> <after> <deleteData createDataKey="category" stepKey="deleteCategory"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontUpdateWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontUpdateWishlistTest.xml index b8a84a327b58f..08698658588ae 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontUpdateWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontUpdateWishlistTest.xml @@ -43,11 +43,11 @@ <argument name="productVar" value="$$product$$"/> </actionGroup> - <actionGroup ref="StorefrontCustomerCheckProductInWishlist" stepKey="checkProductInWishlist"> + <actionGroup ref="StorefrontCustomerCheckProductInWishlistActionGroup" stepKey="checkProductInWishlist"> <argument name="productVar" value="$$product$$"/> </actionGroup> - <actionGroup ref="StorefrontCustomerEditProductInWishlist" stepKey="updateProductInWishlist"> + <actionGroup ref="StorefrontCustomerEditProductInWishlistActionGroup" stepKey="updateProductInWishlist"> <argument name="product" value="$$product$$"/> <argument name="description" value="some text"/> <argument name="quantity" value="2"/> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/WishListWithDisabledProductTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/WishListWithDisabledProductTest.xml index af216c139e7fe..3aabb9e94c364 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/WishListWithDisabledProductTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/WishListWithDisabledProductTest.xml @@ -35,16 +35,16 @@ <actionGroup ref="StorefrontCustomerAddProductToWishlistActionGroup" stepKey="addProductToWishlist"> <argument name="productVar" value="$$createProduct$$"/> </actionGroup> - <actionGroup ref="StorefrontCustomerCheckProductInWishlist" stepKey="checkProductInWishlist"> + <actionGroup ref="StorefrontCustomerCheckProductInWishlistActionGroup" stepKey="checkProductInWishlist"> <argument name="productVar" value="$$createProduct$$"/> </actionGroup> <openNewTab stepKey="openNewTab"/> <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminArea"/> <amOnPage url="{{AdminProductEditPage.url($$createProduct.id$$)}}" stepKey="goToProductEditPage"/> - <actionGroup ref="AdminSetProductDisabled" stepKey="disableProduct"/> - <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + <actionGroup ref="AdminSetProductDisabledActionGroup" stepKey="disableProduct"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> <closeTab stepKey="closeSecondTab"/> <reloadPage stepKey="refreshPage"/> - <actionGroup ref="StorefrontAssertCustomerWishlistIsEmpty" stepKey="checkProductIsAbsentInWishlistIsEmpty"/> + <actionGroup ref="StorefrontAssertCustomerWishlistIsEmptyActionGroup" stepKey="checkProductIsAbsentInWishlistIsEmpty"/> </test> </tests> diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/CartTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/CartTest.php index e9061f1f3d5f8..c1f1378c22da6 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/CartTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/CartTest.php @@ -172,7 +172,7 @@ protected function setUp() $this->messageManagerMock = $this->getMockBuilder(\Magento\Framework\Message\ManagerInterface::class) ->disableOriginalConstructor() - ->setMethods(['addSuccess']) + ->setMethods(['addSuccessMessage']) ->getMockForAbstractClass(); $this->urlMock = $this->getMockBuilder(\Magento\Framework\UrlInterface::class) @@ -566,7 +566,7 @@ protected function prepareExecuteWithQuantityArray($isAjax = false) ->willReturn($productName); $this->messageManagerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('You added ' . $productName . ' to your shopping cart.', null) ->willReturnSelf(); @@ -581,7 +581,7 @@ protected function prepareExecuteWithQuantityArray($isAjax = false) $this->helperMock->expects($this->once()) ->method('calculate') ->willReturnSelf(); - + return $refererUrl; } diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/RemoveTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/RemoveTest.php index bb4ae44fcc31d..2f4d0e6ba48ab 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/RemoveTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/RemoveTest.php @@ -244,7 +244,7 @@ public function testExecuteWithoutWishlist() ->method('create') ->with(\Magento\Wishlist\Model\Item::class) ->willReturn($item); - + $this->wishlistProvider ->expects($this->once()) ->method('getWishlist') @@ -273,7 +273,7 @@ public function testExecuteCanNotSaveWishlist() $this->messageManager ->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('We can\'t delete the item from Wish List right now because of an error: Message.') ->willReturn(true); @@ -356,7 +356,7 @@ public function testExecuteCanNotSaveWishlistAndWithRedirect() $this->messageManager ->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('We can\'t delete the item from the Wish List right now.') ->willReturn(true); diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/SendTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/SendTest.php index 47148f7878134..c70c2a1a6a9b6 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/SendTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/SendTest.php @@ -5,7 +5,10 @@ */ namespace Magento\Wishlist\Test\Unit\Controller\Index; +use Magento\Captcha\Helper\Data as CaptchaHelper; +use Magento\Captcha\Model\DefaultModel as CaptchaModel; use Magento\Customer\Model\Data\Customer as CustomerData; +use Magento\Customer\Model\Session; use Magento\Framework\App\Action\Context as ActionContext; use Magento\Framework\App\RequestInterface; use Magento\Framework\Controller\Result\Redirect as ResultRedirect; @@ -14,15 +17,13 @@ use Magento\Framework\Event\ManagerInterface as EventManagerInterface; use Magento\Framework\Mail\TransportInterface; use Magento\Framework\Message\ManagerInterface; +use Magento\Framework\Phrase; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\UrlInterface; use Magento\Framework\View\Result\Layout as ResultLayout; use Magento\Store\Model\Store; use Magento\Wishlist\Controller\Index\Send; use Magento\Wishlist\Controller\WishlistProviderInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Captcha\Helper\Data as CaptchaHelper; -use Magento\Captcha\Model\DefaultModel as CaptchaModel; -use Magento\Customer\Model\Session; /** * @SuppressWarnings(PHPMD.TooManyFields) @@ -212,7 +213,12 @@ protected function setUp() ); } - public function testExecuteNoFormKeyValidated() + /** + * Verify execute method without Form Key validated + * + * @return void + */ + public function testExecuteNoFormKeyValidated(): void { $this->formKeyValidator->expects($this->once()) ->method('validate') @@ -228,8 +234,43 @@ public function testExecuteNoFormKeyValidated() } /** - * @expectedException \Magento\Framework\Exception\NotFoundException - * @expectedExceptionMessage Page not found. + * Verify execute with no emails left + * + * @return void + */ + public function testExecuteWithNoEmailLeft(): void + { + $expectedMessage = new Phrase('Maximum of %1 emails can be sent.', [0]); + + $this->formKeyValidator->expects($this->once()) + ->method('validate') + ->with($this->request) + ->willReturn(true); + + $this->request->expects($this->at(0)) + ->method('getPost') + ->with('emails') + ->willReturn('some.Email@gmail.com', 'some.email2@gmail.com'); + $this->request->expects($this->at(1)) + ->method('getPost') + ->with('message'); + $wishlist = $this->createMock(\Magento\Wishlist\Model\Wishlist::class); + $this->wishlistProvider->expects($this->once()) + ->method('getWishlist') + ->willReturn($wishlist); + $this->resultRedirect->expects($this->once()) + ->method('setPath') + ->with('*/*/share') + ->willReturnSelf(); + $this->messageManager->expects($this->once()) + ->method('addErrorMessage') + ->with($expectedMessage); + + $this->assertEquals($this->resultRedirect, $this->model->execute()); + } + + /** + * Execute method with no wishlist available */ public function testExecuteNoWishlistAvailable() { @@ -241,6 +282,8 @@ public function testExecuteNoWishlistAvailable() $this->wishlistProvider->expects($this->once()) ->method('getWishlist') ->willReturn(null); + $this->expectException(\Magento\Framework\Exception\NotFoundException::class); + $this->expectExceptionMessage('Page not found'); $this->model->execute(); } diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/UpdateItemOptionsTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/UpdateItemOptionsTest.php index 0c2d7765b1ff2..b6fd509214897 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Controller/Index/UpdateItemOptionsTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Index/UpdateItemOptionsTest.php @@ -249,7 +249,7 @@ public function testExecuteWithoutProduct() $this->messageManager ->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('We can\'t specify a product.') ->willReturn(true); $this->resultRedirectMock->expects($this->once()) @@ -294,7 +294,7 @@ public function testExecuteWithoutWishList() $this->messageManager ->expects($this->never()) - ->method('addError') + ->method('addErrorMessage') ->with('We can\'t specify a product.') ->willReturn(true); @@ -433,12 +433,12 @@ public function testExecuteAddSuccessException() $this->messageManager ->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('Test name has been updated in your Wish List.', null) ->willThrowException(new \Magento\Framework\Exception\LocalizedException(__('error-message'))); $this->messageManager ->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('error-message', null) ->willReturn(true); $this->resultRedirectMock->expects($this->once()) @@ -572,12 +572,12 @@ public function testExecuteAddSuccessCriticalException() $this->messageManager ->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with('Test name has been updated in your Wish List.', null) ->willThrowException($exception); $this->messageManager ->expects($this->once()) - ->method('addError') + ->method('addErrorMessage') ->with('We can\'t update your Wish List right now.', null) ->willReturn(true); $this->resultRedirectMock->expects($this->once()) diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Shared/CartTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Shared/CartTest.php index 118c27ae3eee2..c65f166957c5f 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Controller/Shared/CartTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Shared/CartTest.php @@ -266,7 +266,7 @@ public function testExecute( $successMessage = __('You added %1 to your shopping cart.', $productName); $this->messageManager->expects($this->any()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with($successMessage) ->willReturnSelf(); diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/ItemCarrierTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/ItemCarrierTest.php index 43f77e00bdf98..71ae2d182d0e4 100644 --- a/app/code/Magento/Wishlist/Test/Unit/Model/ItemCarrierTest.php +++ b/app/code/Magento/Wishlist/Test/Unit/Model/ItemCarrierTest.php @@ -228,7 +228,7 @@ public function testMoveAllToCart() ->willReturn($productTwoName); $this->managerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('%1 product(s) have been added to shopping cart: %2.', 1, '"' . $productTwoName . '"'), null) ->willReturnSelf(); @@ -431,12 +431,12 @@ public function testMoveAllToCartWithNotSalableAndOptions() ->willReturn($productTwoName); $this->managerMock->expects($this->at(0)) - ->method('addError') + ->method('addErrorMessage') ->with(__('%1 for "%2".', 'Localized Exception', $productTwoName), null) ->willReturnSelf(); $this->managerMock->expects($this->at(1)) - ->method('addError') + ->method('addErrorMessage') ->with( __( 'We couldn\'t add the following product(s) to the shopping cart: %1.', @@ -580,7 +580,7 @@ public function testMoveAllToCartWithException() ->with($exception, []); $this->managerMock->expects($this->at(0)) - ->method('addError') + ->method('addErrorMessage') ->with(__('We can\'t add this item to your shopping cart right now.'), null) ->willReturnSelf(); @@ -603,7 +603,7 @@ public function testMoveAllToCartWithException() ->willThrowException(new \Exception()); $this->managerMock->expects($this->at(1)) - ->method('addError') + ->method('addErrorMessage') ->with(__('We can\'t update the Wish List right now.'), null) ->willReturnSelf(); @@ -615,7 +615,7 @@ public function testMoveAllToCartWithException() ->willReturn($productTwoName); $this->managerMock->expects($this->once()) - ->method('addSuccess') + ->method('addSuccessMessage') ->with(__('%1 product(s) have been added to shopping cart: %2.', 1, '"' . $productOneName . '"'), null) ->willReturnSelf(); diff --git a/app/code/Magento/Wishlist/Test/Unit/ViewModel/AllowedQuantityTest.php b/app/code/Magento/Wishlist/Test/Unit/ViewModel/AllowedQuantityTest.php new file mode 100644 index 0000000000000..23e28abf20959 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Unit/ViewModel/AllowedQuantityTest.php @@ -0,0 +1,139 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Wishlist\Test\Unit\ViewModel; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Configuration\Item\ItemInterface; +use Magento\CatalogInventory\Model\StockRegistry; +use Magento\Store\Model\Store; +use Magento\Wishlist\ViewModel\AllowedQuantity; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Class AllowedQuantityTest + */ +class AllowedQuantityTest extends TestCase +{ + /** + * @var AllowedQuantity + */ + private $sut; + + /** + * @var StockRegistry|MockObject + */ + private $stockRegistryMock; + + /** + * @var ItemInterface|MockObject + */ + private $itemMock; + + /** + * @var Product|MockObject + */ + private $productMock; + + /** + * @var Store|MockObject + */ + private $storeMock; + + /** + * Set Up + */ + protected function setUp() + { + $this->stockRegistryMock = $this->createMock(StockRegistry::class); + $this->itemMock = $this->getMockForAbstractClass( + ItemInterface::class, + [], + '', + false, + true, + true, + ['getMinSaleQty', 'getMaxSaleQty'] + ); + $this->productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + $this->storeMock = $this->getMockBuilder(Store::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->sut = new AllowedQuantity( + $this->stockRegistryMock + ); + $this->sut->setItem($this->itemMock); + } + + /** + * Getting min and max qty test. + * + * @dataProvider saleQuantityDataProvider + * + * @param int $minSaleQty + * @param int $maxSaleQty + * @param array $expectedResult + */ + public function testGettingMinMaxQty(int $minSaleQty, int $maxSaleQty, array $expectedResult) + { + $this->storeMock->expects($this->atLeastOnce()) + ->method('getWebsiteId') + ->willReturn(1); + $this->productMock->expects($this->atLeastOnce()) + ->method('getId') + ->willReturn(1); + $this->productMock->expects($this->atLeastOnce()) + ->method('getStore') + ->willReturn($this->storeMock); + $this->itemMock->expects($this->any()) + ->method('getProduct') + ->willReturn($this->productMock); + $this->itemMock->expects($this->any()) + ->method('getMinSaleQty') + ->willReturn($minSaleQty); + $this->itemMock->expects($this->any()) + ->method('getMaxSaleQty') + ->willReturn($maxSaleQty); + $this->stockRegistryMock->expects($this->any()) + ->method('getStockItem') + ->will($this->returnValue($this->itemMock)); + + $result = $this->sut->getMinMaxQty(); + + $this->assertEquals($result, $expectedResult); + } + + /** + * Sales quantity provider + * + * @return array + */ + public function saleQuantityDataProvider(): array + { + return [ + [ + 1, + 10, + [ + 'minAllowed' => 1, + 'maxAllowed' => 10 + ] + ], [ + 1, + 0, + [ + 'minAllowed' => 1, + 'maxAllowed' => 99999999 + ] + ] + ]; + } +} diff --git a/app/code/Magento/Wishlist/etc/db_schema.xml b/app/code/Magento/Wishlist/etc/db_schema.xml index e430a1ee40ea2..e3f3024df45fd 100644 --- a/app/code/Magento/Wishlist/etc/db_schema.xml +++ b/app/code/Magento/Wishlist/etc/db_schema.xml @@ -77,9 +77,5 @@ <constraint xsi:type="foreign" referenceId="FK_A014B30B04B72DD0EAB3EECD779728D6" table="wishlist_item_option" column="wishlist_item_id" referenceTable="wishlist_item" referenceColumn="wishlist_item_id" onDelete="CASCADE"/> - <constraint xsi:type="foreign" referenceId="WISHLIST_ITEM_OPTION_PRODUCT_ID_CATALOG_PRODUCT_ENTITY_ENTITY_ID" - table="wishlist_item_option" - column="product_id" referenceTable="catalog_product_entity" referenceColumn="entity_id" - onDelete="CASCADE"/> </table> </schema> diff --git a/app/code/Magento/Wishlist/registration.php b/app/code/Magento/Wishlist/registration.php index ea46a972ef449..b4c9f523ab95f 100644 --- a/app/code/Magento/Wishlist/registration.php +++ b/app/code/Magento/Wishlist/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Wishlist', __DIR__); diff --git a/app/code/Magento/Wishlist/view/frontend/email/share_notification.html b/app/code/Magento/Wishlist/view/frontend/email/share_notification.html index 31ea2f72346ea..d5c31ab858de8 100644 --- a/app/code/Magento/Wishlist/view/frontend/email/share_notification.html +++ b/app/code/Magento/Wishlist/view/frontend/email/share_notification.html @@ -6,6 +6,7 @@ --> <!--@subject {{trans "Take a look at %customer_name's Wish List" customer_name=$customerName}} @--> <!--@vars { +"var store.frontend_name":"Store Name", "var customerName":"Customer Name", "var viewOnSiteLink":"View Wish List URL", "var items|raw":"Wish List Items", @@ -14,7 +15,7 @@ {{template config_path="design/email/header_template"}} -<p>{{trans "%customer_name wants to share this Wish List from %store_name with you:" customer_name=$customerName store_name=$store.getFrontendName()}}</p> +<p>{{trans "%customer_name wants to share this Wish List from %store_name with you:" customer_name=$customerName store_name=$store.frontend_name}}</p> {{depend message}} <table class="message-info"> diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/CustomerWishlistResolver.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/CustomerWishlistResolver.php new file mode 100644 index 0000000000000..a84ce0e965b6d --- /dev/null +++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/CustomerWishlistResolver.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\WishlistGraphQl\Model\Resolver; + +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Wishlist\Model\Wishlist; +use Magento\Wishlist\Model\WishlistFactory; + +/** + * Fetches customer wishlist data + */ +class CustomerWishlistResolver implements ResolverInterface +{ + /** + * @var WishlistFactory + */ + private $wishlistFactory; + + /** + * @param WishlistFactory $wishlistFactory + */ + public function __construct(WishlistFactory $wishlistFactory) + { + $this->wishlistFactory = $wishlistFactory; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + if (false === $context->getExtensionAttributes()->getIsCustomer()) { + throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.')); + } + + /** @var Wishlist $wishlist */ + $wishlist = $this->wishlistFactory->create(); + $wishlist->loadByCustomerId($context->getUserId(), true); + + return [ + 'id' => (string)$wishlist->getId(), + 'sharing_code' => $wishlist->getSharingCode(), + 'updated_at' => $wishlist->getUpdatedAt(), + 'items_count' => $wishlist->getItemsCount(), + 'model' => $wishlist, + ]; + } +} diff --git a/app/code/Magento/WishlistGraphQl/Test/Unit/CustomerWishlistResolverTest.php b/app/code/Magento/WishlistGraphQl/Test/Unit/CustomerWishlistResolverTest.php new file mode 100644 index 0000000000000..f5baa5183e558 --- /dev/null +++ b/app/code/Magento/WishlistGraphQl/Test/Unit/CustomerWishlistResolverTest.php @@ -0,0 +1,164 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\WishlistGraphQl\Test\Unit; + +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\GraphQl\Model\Query\ContextExtensionInterface; +use Magento\Wishlist\Model\Wishlist; +use Magento\Wishlist\Model\WishlistFactory; +use Magento\WishlistGraphQl\Model\Resolver\CustomerWishlistResolver; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +class CustomerWishlistResolverTest extends TestCase +{ + private const STUB_CUSTOMER_ID = 1; + + /** + * @var MockObject|ContextInterface + */ + private $contextMock; + + /** + * @var MockObject|ContextExtensionInterface + */ + private $extensionAttributesMock; + + /** + * @var MockObject|WishlistFactory + */ + private $wishlistFactoryMock; + + /** + * @var MockObject|Wishlist + */ + private $wishlistMock; + + /** + * @var CustomerWishlistResolver + */ + private $resolver; + + /** + * Build the Testing Environment + */ + protected function setUp() + { + $this->contextMock = $this->getMockBuilder(ContextInterface::class) + ->setMethods(['getExtensionAttributes', 'getUserId']) + ->getMock(); + + $this->extensionAttributesMock = $this->getMockBuilder(ContextExtensionInterface::class) + ->setMethods(['getIsCustomer']) + ->getMock(); + + $this->contextMock->method('getExtensionAttributes') + ->willReturn($this->extensionAttributesMock); + + $this->wishlistFactoryMock = $this->getMockBuilder(WishlistFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->wishlistMock = $this->getMockBuilder(Wishlist::class) + ->disableOriginalConstructor() + ->setMethods(['loadByCustomerId', 'getId', 'getSharingCode', 'getUpdatedAt', 'getItemsCount']) + ->getMock(); + + $objectManager = new ObjectManager($this); + $this->resolver = $objectManager->getObject(CustomerWishlistResolver::class, [ + 'wishlistFactory' => $this->wishlistFactoryMock + ]); + } + + /** + * Verify if Authorization exception is being thrown when User not logged in + */ + public function testThrowExceptionWhenUserNotAuthorized(): void + { + // Given + $this->extensionAttributesMock->method('getIsCustomer') + ->willReturn(false); + + // Then + $this->expectException(GraphQlAuthorizationException::class); + $this->wishlistFactoryMock->expects($this->never()) + ->method('create'); + + // When + $this->resolver->resolve( + $this->getFieldStub(), + $this->contextMock, + $this->getResolveInfoStub() + ); + } + + /** + * Verify if Wishlist instance is created for currently Authorized user + */ + public function testFactoryCreatesWishlistByAuthorizedCustomerId(): void + { + // Given + $this->extensionAttributesMock->method('getIsCustomer') + ->willReturn(true); + + $this->contextMock->method('getUserId') + ->willReturn(self::STUB_CUSTOMER_ID); + + // Then + $this->wishlistMock->expects($this->once()) + ->method('loadByCustomerId') + ->with(self::STUB_CUSTOMER_ID) + ->willReturnSelf(); + + $this->wishlistFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->wishlistMock); + + // When + $this->resolver->resolve( + $this->getFieldStub(), + $this->contextMock, + $this->getResolveInfoStub() + ); + } + + /** + * Returns stub for Field + * + * @return MockObject|Field + */ + private function getFieldStub(): Field + { + /** @var MockObject|Field $fieldMock */ + $fieldMock = $this->getMockBuilder(Field::class) + ->disableOriginalConstructor() + ->getMock(); + + return $fieldMock; + } + + /** + * Returns stub for ResolveInfo + * + * @return MockObject|ResolveInfo + */ + private function getResolveInfoStub(): ResolveInfo + { + /** @var MockObject|ResolveInfo $resolveInfoMock */ + $resolveInfoMock = $this->getMockBuilder(ResolveInfo::class) + ->disableOriginalConstructor() + ->getMock(); + + return $resolveInfoMock; + } +} diff --git a/app/code/Magento/WishlistGraphQl/etc/module.xml b/app/code/Magento/WishlistGraphQl/etc/module.xml index 337623cc85a92..c2f5b3165b2ab 100644 --- a/app/code/Magento/WishlistGraphQl/etc/module.xml +++ b/app/code/Magento/WishlistGraphQl/etc/module.xml @@ -6,5 +6,10 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_WishlistGraphQl" /> + <module name="Magento_WishlistGraphQl"> + <sequence> + <module name="Magento_Customer"/> + <module name="Magento_CustomerGraphQl"/> + </sequence> + </module> </config> diff --git a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls index 2aa5f03a787d0..deaa66921ba7c 100644 --- a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls +++ b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls @@ -2,13 +2,25 @@ # See COPYING.txt for license details. type Query { - wishlist: WishlistOutput @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistResolver") @doc(description: "The wishlist query returns the contents of a customer's wish list") @cache(cacheable: false) + wishlist: WishlistOutput @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistResolver") @deprecated(reason: "Moved under `Customer` `wishlist`") @doc(description: "The wishlist query returns the contents of a customer's wish list") @cache(cacheable: false) } -type WishlistOutput { +type Customer { + wishlist: Wishlist! @resolver(class:"\\Magento\\WishlistGraphQl\\Model\\Resolver\\CustomerWishlistResolver") @doc(description: "The wishlist query returns the contents of a customer's wish lists") @cache(cacheable: false) +} + +type WishlistOutput @doc(description: "Deprecated: `Wishlist` type should be used instead") { + items: [WishlistItem] @deprecated(reason: "Use field `items` from type `Wishlist` instead") @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistItemsResolver") @doc(description: "An array of items in the customer's wish list"), + items_count: Int @deprecated(reason: "Use field `items_count` from type `Wishlist` instead") @doc(description: "The number of items in the wish list"), + name: String @deprecated(reason: "This field is related to Commerce functionality and is always `null` in Open Source edition") @doc(description: "When multiple wish lists are enabled, the name the customer assigns to the wishlist"), + sharing_code: String @deprecated(reason: "Use field `sharing_code` from type `Wishlist` instead") @doc(description: "An encrypted code that Magento uses to link to the wish list"), + updated_at: String @deprecated(reason: "Use field `updated_at` from type `Wishlist` instead") @doc(description: "The time of the last modification to the wish list") +} + +type Wishlist { + id: ID @doc(description: "Wishlist unique identifier") items: [WishlistItem] @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistItemsResolver") @doc(description: "An array of items in the customer's wish list"), items_count: Int @doc(description: "The number of items in the wish list"), - name: String @doc(description: "When multiple wish lists are enabled, the name the customer assigns to the wishlist"), sharing_code: String @doc(description: "An encrypted code that Magento uses to link to the wish list"), updated_at: String @doc(description: "The time of the last modification to the wish list") } @@ -19,4 +31,4 @@ type WishlistItem { description: String @doc(description: "The customer's comment about this item"), added_at: String @doc(description: "The time when the customer added the item to the wish list"), product: ProductInterface @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\ProductResolver") -} \ No newline at end of file +} diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less index d0b17b3439d66..29a7499ec72f4 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/_menu.less @@ -17,7 +17,7 @@ @menu-logo__padding-bottom: 1.7rem; @menu-logo__outer-size: @menu-logo__padding-top + @menu-logo-img__height + @menu-logo__padding-bottom; -@menu-logo__padding-top: 1.7rem; +@menu-logo__padding-top: 1.7rem; @menu-logo-img__height: 4.1rem; @menu-logo-img__width: 3.5rem; @@ -37,6 +37,7 @@ @submenu__padding-horizontal: 1.5rem; @submenu__padding-vertical: 2rem; @submenu__z-index: @menu__z-index - 2; +@submenu__height: 720px; @submenu-column__width: 23.8rem; @submenu-column__width__l: 19.8rem; @submenu-title__color: @color-white; @@ -252,7 +253,6 @@ background-color: @submenu__background-color; box-shadow: 0 0 3px @color-black; left: 100%; // align all submenus with one Y axis line - min-height: ~'calc(@{menu-logo__outer-size} + 2rem + 100%)'; padding: @submenu__padding-vertical 0 0; position: absolute; top: 0; @@ -266,6 +266,13 @@ .ie11 & { height: 100%; } + + > ul[role='menu'] { + max-width: ~'calc(100vw - @{menu__width})'; + min-height: @submenu__height; + overflow-x: auto; + overflow-y: hidden; + } } &._show { diff --git a/app/code/Magento/Cms/view/adminhtml/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Cms/web/css/source/_module.less similarity index 100% rename from app/code/Magento/Cms/view/adminhtml/web/css/source/_module.less rename to app/design/adminhtml/Magento/backend/Magento_Cms/web/css/source/_module.less diff --git a/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less index c8f2530df22e0..0c7dd7e7cb94c 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less @@ -78,3 +78,33 @@ } } } + +.customer-newsletter-fieldset.admin__fieldset { + &.multi-website { + > .admin__field > .admin__field-control { + width: ~'calc(100% * 0.75 - 30px)'; + + table { + th.subscriber-status { + text-align: center; + } + + td { + &.subscriber-status { + text-align: center; + } + + select.admin__control-select { + width: 100%; + } + } + } + } + } + + > .admin__field > .admin__field-control { + input[type='checkbox'] { + margin: 8px 0 0 0; + } + } +} diff --git a/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_total.less b/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_total.less index f2369ad8f35e1..6e663b15c89cc 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_total.less +++ b/app/design/adminhtml/Magento/backend/Magento_Sales/web/css/source/module/order/_total.less @@ -22,6 +22,10 @@ } } +.totals-actions { + text-align: right; +} + .order-totals-actions { margin-top: @indent__s; .actions { diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module.less index e05e81d737f14..9255accff6950 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/_module.less @@ -4,3 +4,4 @@ // */ @import 'module/_data-grid.less'; +@import 'module/_masonry-grid.less'; diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_data-grid.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_data-grid.less index 946d11db2d1a2..b0c44e127a454 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_data-grid.less +++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_data-grid.less @@ -1075,7 +1075,6 @@ body._in-resize { } .data-grid-checkbox-cell-inner { - display: unset; margin: 0 @data-grid-checkbox-cell-inner__padding-horizontal 0; padding: 0; text-align: center; diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less new file mode 100644 index 0000000000000..9dd42246dac3f --- /dev/null +++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_masonry-grid.less @@ -0,0 +1,118 @@ +// /** +// * Copyright © Magento, Inc. All rights reserved. +// * See COPYING.txt for license details. +// */ +@admin__masonry_grid_image__space: 20px; +@admin__masonry_grid_background_color: #fff; +@admin__masonry_overlay_background_color: #507dc8; +@admin__masonry_grid_active_image_border_color: #558dd6; + +& when (@media-common = true) { + .masonry-image { + &-grid { + margin: @admin__masonry_grid_image__space/2 -(@admin__masonry_grid_image__space/2); + overflow: hidden; + position: relative; + + .no-data-message-container, + .error-message-container { + font-size: @data-grid__no-records__font-size; + padding: @data-grid__no-records__padding; + text-align: center; + } + } + + &-column { + background-color: @admin__masonry_grid_background_color; + float: left; + margin: @admin__masonry_grid_image__space/2; + overflow: hidden; + + .masonry-image-block { + &.active { + img { + border: 2px @admin__masonry_grid_active_image_border_color solid; + padding: 1px; + } + } + } + + img { + cursor: pointer; + height: 100%; + width: 100%; + } + } + + &-overlay { + background-color: @admin__masonry_overlay_background_color; + color: @admin__masonry_grid_background_color; + opacity: 1; + padding: .5rem; + position: absolute; + text-align: center; + width: 80px; + z-index: 10; + } + + &-preview { + background-color: @admin__masonry_grid_background_color; + display: table; + left: 0; + position: absolute; + right: 0; + width: 100%; + + .container { + margin: auto; + max-width: 880px; + padding-top: 10px; + + .action-buttons { + text-align: right; + + .action { + &-close { + padding: 30px; + position: static; + } + + &-previous, + &-next { + background: transparent; + border: none; + margin: 0; + white-space: nowrap; + } + + &-close, + &-previous, + &-next { + font-size: 2rem; + } + } + } + + .preview-row-content { + display: flex; + + &:after { + clear: both; + content: ''; + display: table; + } + + img.preview { + display: block; + flex-basis: 300px; + float: left; + margin-bottom: 20px; + max-height: 500px; + max-width: 60%; + width: auto; + } + } + } + } + } +} diff --git a/app/design/adminhtml/Magento/backend/registration.php b/app/design/adminhtml/Magento/backend/registration.php index 039f3a141e382..e15cc9c18604b 100644 --- a/app/design/adminhtml/Magento/backend/registration.php +++ b/app/design/adminhtml/Magento/backend/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'adminhtml/Magento/backend', __DIR__); diff --git a/app/design/adminhtml/Magento/backend/web/css/source/_actions.less b/app/design/adminhtml/Magento/backend/web/css/source/_actions.less index 886bbcc29a3b9..852c6c1f3799e 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/_actions.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/_actions.less @@ -444,6 +444,18 @@ button { > .action-menu-item { min-width: 100%; } + + &::after { + border-color: transparent transparent transparent @color-black; + border-style: solid; + border-width: .4rem 0 .4rem .5rem; + content: ''; + height: 0; + position: relative; + right: 1.2rem; + top: 1.4rem; + width: 0; + } } } diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_calendar-temp.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_calendar-temp.less index 11b187db3d1e4..3ed4ac91d32a4 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_calendar-temp.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_calendar-temp.less @@ -47,10 +47,6 @@ vertical-align: top; z-index: 1; - &:active { - .scale(.975); - } - + .admin__control-support-text, + .admin__control-label { margin-left: @action__height + .5rem; diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_media-gallery.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_media-gallery.less index 9a5f35e4ede90..6de25c424656d 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_media-gallery.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_media-gallery.less @@ -280,6 +280,7 @@ .item-role { background: @color-gray89; color: @color-brownie; + cursor: pointer; font-size: @font-size__s; line-height: 1; margin: 0 .4rem .4rem 0; diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less index 15cd295885892..6660f780afdaf 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_messages.less @@ -44,6 +44,7 @@ .messages { .message { + .lib-wrap-words(); &:last-child { margin: 0 0 2rem; } diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less index ab4bac919ee5f..c6f39e8e8840d 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less @@ -59,15 +59,15 @@ .admin__control-select { &:extend(.abs-form-control-pattern all); .lib-css(appearance, none, 1); - background-image+: url('../images/arrows-bg.svg') !important; + background-image+: url('../images/arrows-bg.svg'); background-position+: ~'calc(100% - 12px)' -34px; background-size+: auto; - background-image+: linear-gradient(@color-gray89, @color-gray89) !important; + background-image+: linear-gradient(@color-gray89, @color-gray89); background-position+: 100%; background-size+: @field-control__height 100%; - background-image+: linear-gradient(@field-control__border-color,@field-control__border-color) !important; + background-image+: linear-gradient(@field-control__border-color,@field-control__border-color); background-position+: ~'calc(100% - @{field-control__height})' 0; background-size+: 1px 100%; @@ -86,13 +86,13 @@ } &:active { - background-image+: url('../images/arrows-bg.svg') !important; + background-image+: url('../images/arrows-bg.svg'); background-position+: ~'calc(100% - 12px)' 13px; - background-image+: linear-gradient(@color-gray89, @color-gray89) !important; + background-image+: linear-gradient(@color-gray89, @color-gray89); background-position+: 100%; - background-image+: linear-gradient(@field-control__focus__border-color, @field-control__focus__border-color) !important; + background-image+: linear-gradient(@field-control__focus__border-color, @field-control__focus__border-color); background-position+: ~'calc(100% - @{field-control__height})' 0; border-color: @field-control__focus__border-color; } 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 ddc6aa42c23eb..bfb515c700b33 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 @@ -550,7 +550,6 @@ & > .admin__field-label { #mix-grid .column(@field-label-grid__column, @field-grid__columns); cursor: pointer; - background: @color-white; left: 0; position: absolute; top: 0; 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 697d11fb57d67..0a81223525fbd 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 @@ -237,3 +237,22 @@ float: right; } } + +.product_form_product_form_advanced_pricing_modal .admin__fieldset > .admin__field > .admin__field-label, +.product_form_product_form_advanced_pricing_modal [class*='admin__control-grouped'] > .admin__field:first-child > .admin__field-label { + margin-left: 0; +} + +.product_form_product_form_advanced_pricing_modal .admin__control-table td, +.product_form_product_form_advanced_pricing_modal .admin__control-table th { + overflow-y: visible; +} + +.product_form_product_form_advanced_pricing_modal .admin__fieldset { + margin-left: -30px; +} + +.product_form_product_form_advanced_pricing_modal .admin__control-table-wrapper { + overflow-x: visible; + overflow-y: visible; +} diff --git a/app/design/adminhtml/Magento/backend/web/css/styles-old.less b/app/design/adminhtml/Magento/backend/web/css/styles-old.less index b2afde435a627..247316ab0361b 100644 --- a/app/design/adminhtml/Magento/backend/web/css/styles-old.less +++ b/app/design/adminhtml/Magento/backend/web/css/styles-old.less @@ -1827,6 +1827,10 @@ background-image: url(../images/fam_application_form_delete.png); } + .x-tree-node .x-tree-node-el input[type=checkbox] { + margin-left: 3px; + } + // // Styles for "js" tooltip with positionings // -------------------------------------- diff --git a/app/design/adminhtml/Magento/backend/web/js/theme.js b/app/design/adminhtml/Magento/backend/web/js/theme.js index 39b364ea8553f..05d73ac20fcbd 100644 --- a/app/design/adminhtml/Magento/backend/web/js/theme.js +++ b/app/design/adminhtml/Magento/backend/web/js/theme.js @@ -93,6 +93,7 @@ define('globalNavigationScroll', [ } else { // static menu cases checkRemoveClass(menu, fixedClassName); + menu.css('top', 'auto'); } // Save previous window scrollTop diff --git a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less index 299c138832064..44e93087399a1 100644 --- a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/_module.less @@ -457,9 +457,11 @@ .action { &.delete { &:extend(.abs-remove-button-for-blocks all); + line-height: unset; position: absolute; right: 0; - top: 0; + top: -1px; + width: auto; } } diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_cart.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_cart.less index d3d15019f0e87..3142d5de64be1 100644 --- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_cart.less +++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_cart.less @@ -346,6 +346,10 @@ .widget { float: left; + + &.block { + margin-bottom: @indent__base; + } } } diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less index 65f3eeef63b01..c9b1d41857eee 100644 --- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less +++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/_minicart.less @@ -110,7 +110,7 @@ @_dropdown-list-position-right: 0, @_dropdown-list-pointer-position: right, @_dropdown-list-pointer-position-left-right: 26px, - @_dropdown-list-z-index: 101, + @_dropdown-list-z-index: 101, @_dropdown-toggle-icon-content: @icon-cart, @_dropdown-toggle-active-icon-content: @icon-cart, @_dropdown-list-item-padding: false, @@ -263,6 +263,7 @@ ); cursor: pointer; position: relative; + white-space: nowrap; &:after { position: static; @@ -304,7 +305,7 @@ .weee[data-label] { .lib-font-size(11); - + .label { &:extend(.abs-no-display all); } @@ -340,13 +341,13 @@ } .item-qty { - margin-right: @indent__s; text-align: center; width: 45px; } .update-cart-item { .lib-font-size(11); + margin-left: 5px; vertical-align: top; } @@ -390,6 +391,17 @@ .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__s) { .minicart-wrapper { margin-top: @indent__s; + .lib-clearfix(); + .product { + .actions { + float: left; + margin: 10px 0 0 0; + } + } + .update-cart-item { + float: right; + margin-left: 0; + } } } @@ -400,7 +412,7 @@ .media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { .minicart-wrapper { margin-left: 13px; - + .block-minicart { right: -15px; width: 390px; diff --git a/app/code/Magento/Contact/view/frontend/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Contact/web/css/source/_module.less similarity index 100% rename from app/code/Magento/Contact/view/frontend/web/css/source/_module.less rename to app/design/frontend/Magento/blank/Magento_Contact/web/css/source/_module.less 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 213b8131815b3..0c2b1b4db83e6 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 @@ -85,6 +85,7 @@ .box-information, .box-newsletter { .box-content { + .lib-wrap-words(); line-height: 26px; } } diff --git a/app/design/frontend/Magento/blank/Magento_Newsletter/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Newsletter/web/css/source/_module.less index f22a325debc9c..09759d95c4b10 100644 --- a/app/design/frontend/Magento/blank/Magento_Newsletter/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Newsletter/web/css/source/_module.less @@ -44,7 +44,8 @@ } input { - padding-left: 35px; + margin-right: 35px; + padding: 0 0 0 35px; // Reset some default Safari padding values. } .title { @@ -75,7 +76,8 @@ .media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { .block.newsletter { - width: 32%; + max-width: 44%; + width: max-content; .field { margin-right: 5px; diff --git a/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less index b314bcf5b3473..3faa8ca965410 100644 --- a/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less @@ -89,6 +89,7 @@ img { display: block; + height: auto; } .page-print & { diff --git a/app/design/frontend/Magento/blank/media/preview.jpg b/app/design/frontend/Magento/blank/media/preview.jpg index 61be74876a1e9..438a72e108898 100644 Binary files a/app/design/frontend/Magento/blank/media/preview.jpg and b/app/design/frontend/Magento/blank/media/preview.jpg differ diff --git a/app/design/frontend/Magento/blank/registration.php b/app/design/frontend/Magento/blank/registration.php index 211f3fca131cc..297ae6d98ee41 100644 --- a/app/design/frontend/Magento/blank/registration.php +++ b/app/design/frontend/Magento/blank/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Magento/blank', __DIR__); diff --git a/app/design/frontend/Magento/blank/web/css/source/_layout.less b/app/design/frontend/Magento/blank/web/css/source/_layout.less index 62195b5566f8e..287f1b45b8328 100644 --- a/app/design/frontend/Magento/blank/web/css/source/_layout.less +++ b/app/design/frontend/Magento/blank/web/css/source/_layout.less @@ -125,11 +125,14 @@ } .page-layout-2columns-left { + .main { + padding-left: @layout-column__additional-sidebar-offset + } + .sidebar-additional { clear: left; float: left; padding-left: 0; - padding-right: @layout-column__additional-sidebar-offset; } } diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less index 3b4da1d1ae6f5..27533a0eb598f 100644 --- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/_module.less @@ -174,9 +174,9 @@ width: 100%; .price-box { - display: table-cell; + display: inline-block; vertical-align: top; - width: 1px; + width: auto; .price-container { > span { @@ -228,7 +228,8 @@ } .product-info-stock-sku { - display: table-cell; + display: inline-block; + float: right; padding-bottom: @indent__s; padding-left: 10%; text-align: right; @@ -397,7 +398,7 @@ .box-tocart { &:extend(.abs-box-tocart all); - + .field.qty { } @@ -987,6 +988,24 @@ } } } + +.media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__s) { + .sidebar { + .product-items { + .action { + &.delete { + &:extend(.abs-remove-button-for-blocks all); + line-height: unset; + position: absolute; + right: 0; + top: -1px; + width: auto; + } + } + } + } +} + .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { .compare.wrapper, [class*='block-compare'] { diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less index 92945d61e4368..d106fa8886c05 100644 --- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less +++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less @@ -108,6 +108,13 @@ .actions-primary { display: inline-block; vertical-align: middle; + + > .stock.unavailable { + line-height: 1; + padding-bottom: @indent__s; + padding-right: 24px; + padding-top: @indent__s; + } } } @@ -279,6 +286,12 @@ } } } + + .product-item-actions { + .actions-primary { + display: block; + } + } } .media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__s) { @@ -292,7 +305,7 @@ margin: -10px; padding: 9px; position: relative; - z-index: 2; + z-index: 9; .product-item-inner { display: block; @@ -360,13 +373,12 @@ position: absolute; top: -2px; width: 100%; - z-index: 1; + z-index: -1; } } } .product-item-actions { - position: relative; z-index: 1; } } diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less index 460a961830b43..87990c3e48280 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less @@ -267,6 +267,10 @@ .lib-icon-font-symbol( @_icon-font-content: @icon-trash ); + + &:hover { + .lib-css(text-decoration, @link__text-decoration); + } } } @@ -563,6 +567,10 @@ .widget { float: left; + + &.block { + margin-bottom: @indent__base; + } } } diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less index af94dd7b97bbb..14c754623cf03 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less @@ -120,7 +120,7 @@ @_dropdown-list-position-right: -10px, @_dropdown-list-pointer-position: right, @_dropdown-list-pointer-position-left-right: 12px, - @_dropdown-list-z-index: 101, + @_dropdown-list-z-index: 101, @_dropdown-toggle-icon-content: @icon-cart, @_dropdown-toggle-active-icon-content: @icon-cart, @_dropdown-list-item-padding: false, @@ -136,7 +136,7 @@ .block-minicart { .lib-css(padding, 25px @minicart__padding-horizontal); - + .block-title { display: none; } @@ -233,7 +233,7 @@ .minicart-items { .lib-list-reset-styles(); - + .product-item { padding: @indent__base 0; @@ -315,8 +315,9 @@ .toggle { &:extend(.abs-toggling-title all); border: 0; - padding: 0 @indent__xl @indent__xs 0; - + padding: 0 0 @indent__xs 0; + white-space: nowrap; + &:after { .lib-css(color, @color-gray56); margin: 0 0 0 @indent__xs; @@ -349,7 +350,7 @@ @_icon-font-position: after ); } - + > span { &:extend(.abs-visually-hidden-reset all); } @@ -369,13 +370,13 @@ } .item-qty { - margin-right: @indent__s; text-align: center; width: 60px; } .update-cart-item { .lib-font-size(11); + margin-left: 5px; vertical-align: top; } @@ -419,6 +420,17 @@ .media-width(@extremum, @break) when (@extremum = 'max') and (@break = @screen__m) { .minicart-wrapper { margin-top: @indent__s; + .lib-clearfix(); + .product { + .actions { + float: left; + margin: 10px 0 0 0; + } + } + .update-cart-item { + float: right; + margin-left: 0; + } } } diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payments.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payments.less index eb9c069053661..494483ff60dda 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payments.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payments.less @@ -133,6 +133,10 @@ .lib-css(line-height, @checkout-billing-address-details__line-height); .lib-css(padding, @checkout-billing-address-details__padding); } + + input[type="checkbox"] { + vertical-align: top; + } } .payment-method-note { diff --git a/app/design/frontend/Magento/luma/Magento_Customer/email/account_new.html b/app/design/frontend/Magento/luma/Magento_Customer/email/account_new.html index c8b10aa6dffc7..4a897a62a9235 100644 --- a/app/design/frontend/Magento/luma/Magento_Customer/email/account_new.html +++ b/app/design/frontend/Magento/luma/Magento_Customer/email/account_new.html @@ -4,17 +4,19 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Welcome to %store_name" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Welcome to %store_name" store_name=$store.frontend_name}} @--> <!--@vars { -"var this.getUrl($store, 'customer/account/')":"Customer Account URL", +"var store.frontend_name":"Store Name", +"var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL", "var customer.email":"Customer Email", -"var customer.name":"Customer Name" +"var customer.name":"Customer Name", +"var this.getUrl($store,'customer/account/createPassword/',[_query:[id:$customer.id,token:$customer.rp_token],_nosid:1])":"Password Reset URL" } @--> {{template config_path="design/email/header_template"}} <p class="greeting">{{trans "%name," name=$customer.name}}</p> -<p>{{trans "Welcome to %store_name." store_name=$store.getFrontendName()}}</p> +<p>{{trans "Welcome to %store_name." store_name=$store.frontend_name}}</p> <p> {{trans 'To sign in to our site, use these credentials during checkout or on the <a href="%customer_url">My Account</a> page:' 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 6adf4b5b2f86b..6354cc35d32ed 100644 --- a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less @@ -126,6 +126,7 @@ .box-information, .box-newsletter { .box-content { + .lib-wrap-words(); &:extend(.abs-account-block-line-height all); } } diff --git a/app/design/frontend/Magento/luma/Magento_Email/email/footer.html b/app/design/frontend/Magento/luma/Magento_Email/email/footer.html index 0fc8e36a82076..9620e5c446e60 100644 --- a/app/design/frontend/Magento/luma/Magento_Email/email/footer.html +++ b/app/design/frontend/Magento/luma/Magento_Email/email/footer.html @@ -6,7 +6,12 @@ --> <!--@subject {{trans "Footer"}} @--> <!--@vars { -"var store.getFrontendName()":"Store Name" +"var store.frontend_name":"Store Name", +"var url_about_us":"About Us URL", +"var url_customer_service":"Customer Service URL", +"var store_phone":"Store Phone", +"var store_hours":"Store Hours", +"var store.formatted_address|raw":"Store Address" } @--> <!-- End Content --> @@ -42,7 +47,7 @@ </td> <td> <p class="address"> - {{var store.getFormattedAddress()|raw}} + {{var store.formatted_address|raw}} </p> </td> </tr> diff --git a/app/design/frontend/Magento/luma/Magento_Newsletter/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Newsletter/web/css/source/_module.less index d7ee1319c9a43..a72f31d72ce48 100644 --- a/app/design/frontend/Magento/luma/Magento_Newsletter/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Newsletter/web/css/source/_module.less @@ -46,7 +46,8 @@ } input { - padding-left: 35px; + margin-right: 35px; + padding: 0 0 0 35px; // Reset some default Safari padding values. } .title { @@ -78,7 +79,15 @@ .media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { .block.newsletter { - width: 34%; + max-width: 44%; + width: max-content; + + .form.subscribe { + > .field, + > .actions { + float: left; + } + } } } diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new.html b/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new.html index 098a610adaddf..86e3cf01e965e 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new.html @@ -4,28 +4,32 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Credit memo for your %store_name order" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Credit memo for your %store_name order" store_name=$store.frontend_name}} @--> <!--@vars { "var formattedBillingAddress|raw":"Billing Address", -"var comment":"Credit Memo Comment", +"var comment|escape|nl2br":"Credit Memo Comment", "var creditmemo.increment_id":"Credit Memo Id", "layout handle=\"sales_email_order_creditmemo_items\" creditmemo=$creditmemo order=$order":"Credit Memo Items Grid", -"var this.getUrl($store, 'customer/account/')":"Customer Account URL", -"var order.getCustomerName()":"Customer Name", +"var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL", +"var order_data.customer_name":"Customer Name", "var order.increment_id":"Order Id", "var payment_html|raw":"Payment Details", "var formattedShippingAddress|raw":"Shipping Address", -"var order.getShippingDescription()":"Shipping Description", -"var order.shipping_description":"Shipping Description" +"var order.shipping_description":"Shipping Description", +"var store.frontend_name":"Store Frontend Name", +"var store_email":"Store Email", +"var creditmemo":"Credit Memo", +"var order":"Order", +"var order_data.is_not_virtual":"Order Type" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$order.getCustomerName()}}</p> + <p class="greeting">{{trans "%name," name=$order_data.customer_name}}</p> <p> - {{trans "Thank you for your order from %store_name." store_name=$store.getFrontendName()}} + {{trans "Thank you for your order from %store_name." store_name=$store.frontend_name}} {{trans 'You can check the status of your order by <a href="%account_url">logging into your account</a>.' account_url=$this.getUrl($store,'customer/account/',[_nosid:1]) |raw}} </p> <p> @@ -55,7 +59,7 @@ <h1>{{trans "Your Credit Memo #%creditmemo_id for Order #%order_id" creditmemo_i <h3>{{trans "Billing Info"}}</h3> <p>{{var formattedBillingAddress|raw}}</p> </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="address-details"> <h3>{{trans "Shipping Info"}}</h3> <p>{{var formattedShippingAddress|raw}}</p> @@ -67,10 +71,10 @@ <h3>{{trans "Shipping Info"}}</h3> <h3>{{trans "Payment Method"}}</h3> {{var payment_html|raw}} </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="method-info"> <h3>{{trans "Shipping Method"}}</h3> - <p>{{var order.getShippingDescription()}}</p> + <p>{{var order.shipping_description}}</p> </td> {{/depend}} </tr> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new_guest.html index f116e86eeeb06..d0310a8e2c7b6 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new_guest.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_new_guest.html @@ -4,27 +4,31 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Credit memo for your %store_name order" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Credit memo for your %store_name order" store_name=$store.frontend_name}} @--> <!--@vars { "var formattedBillingAddress|raw":"Billing Address", -"var comment":"Credit Memo Comment", +"var comment|escape|nl2br":"Credit Memo Comment", "var creditmemo.increment_id":"Credit Memo Id", "layout handle=\"sales_email_order_creditmemo_items\" creditmemo=$creditmemo order=$order":"Credit Memo Items Grid", -"var billing.getName()":"Guest Customer Name (Billing)", +"var billing.name":"Guest Customer Name (Billing)", "var order.increment_id":"Order Id", "var payment_html|raw":"Payment Details", "var formattedShippingAddress|raw":"Shipping Address", -"var order.getShippingDescription()":"Shipping Description", -"var order.shipping_description":"Shipping Description" +"var order.shipping_description":"Shipping Description", +"var store.frontend_name":"Store Frontend Name", +"var store_email":"Store Email", +"var creditmemo":"Credit Memo", +"var order":"Order", +"var order_data.is_not_virtual":"Order Type" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$billing.getName()}}</p> + <p class="greeting">{{trans "%name," name=$billing.name}}</p> <p> - {{trans "Thank you for your order from %store_name." store_name=$store.getFrontendName()}} + {{trans "Thank you for your order from %store_name." store_name=$store.frontend_name}} </p> <p> {{trans 'If you have questions about your order, you can email us at <a href="mailto:%store_email">%store_email</a>.' store_email=$store_email |raw}} @@ -53,7 +57,7 @@ <h1>{{trans "Your Credit Memo #%creditmemo_id for Order #%order_id" creditmemo_i <h3>{{trans "Billing Info"}}</h3> <p>{{var formattedBillingAddress|raw}}</p> </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="address-details"> <h3>{{trans "Shipping Info"}}</h3> <p>{{var formattedShippingAddress|raw}}</p> @@ -65,10 +69,10 @@ <h3>{{trans "Shipping Info"}}</h3> <h3>{{trans "Payment Method"}}</h3> {{var payment_html|raw}} </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="method-info"> <h3>{{trans "Shipping Method"}}</h3> - <p>{{var order.getShippingDescription()}}</p> + <p>{{var order.shipping_description}}</p> </td> {{/depend}} </tr> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_update.html b/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_update.html index 269e46d752084..352c9ab4fddf4 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_update.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_update.html @@ -4,27 +4,29 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Update to your %store_name credit memo" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Update to your %store_name credit memo" store_name=$store.frontend_name}} @--> <!--@vars { -"var comment":"Credit Memo Comment", +"var comment|escape|nl2br":"Credit Memo Comment", "var creditmemo.increment_id":"Credit Memo Id", -"var this.getUrl($store, 'customer/account/')":"Customer Account URL", -"var order.getCustomerName()":"Customer Name", +"var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL", +"var order_data.customer_name":"Customer Name", "var order.increment_id":"Order Id", -"var order.getFrontendStatusLabel()":"Order Status" +"var order_data.frontend_status_label":"Order Status", +"var store.frontend_name":"Store Frontend Name", +"var store_email":"Store Email" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$order.getCustomerName()}}</p> + <p class="greeting">{{trans "%name," name=$order_data.customer_name}}</p> <p> {{trans "Your order #%increment_id has been updated with a status of <strong>%order_status</strong>." increment_id=$order.increment_id - order_status=$order.getFrontendStatusLabel() + order_status=$order_data.frontend_status_label |raw}} {{trans 'You can check the status of your order by <a href="%account_url">logging into your account</a>.' account_url=$this.getUrl($store,'customer/account/',[_nosid:1]) |raw}} </p> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_update_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_update_guest.html index c8bdae7b08fa5..9b09760c1fa4a 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_update_guest.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/creditmemo_update_guest.html @@ -4,26 +4,28 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Update to your %store_name credit memo" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Update to your %store_name credit memo" store_name=$store.frontend_name}} @--> <!--@vars { -"var comment":"Credit Memo Comment", +"var comment|escape|nl2br":"Credit Memo Comment", "var creditmemo.increment_id":"Credit Memo Id", -"var billing.getName()":"Guest Customer Name", +"var billing.name":"Guest Customer Name", "var order.increment_id":"Order Id", -"var order.getFrontendStatusLabel()":"Order Status" +"var order_data.frontend_status_label":"Order Status", +"var store_email":"Store Email", +"var store.frontend_name":"Store Frontend Name" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$billing.getName()}}</p> + <p class="greeting">{{trans "%name," name=$billing.name}}</p> <p> {{trans "Your order #%increment_id has been updated with a status of <strong>%order_status</strong>." increment_id=$order.increment_id - order_status=$order.getFrontendStatusLabel() + order_status=$order_data.frontend_status_label |raw}} </p> <p> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new.html b/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new.html index 8d7ba99312375..636fa9ac5f425 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new.html @@ -4,28 +4,32 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Invoice for your %store_name order" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Invoice for your %store_name order" store_name=$store.frontend_name}} @--> <!--@vars { "var formattedBillingAddress|raw":"Billing Address", -"var this.getUrl($store, 'customer/account/')":"Customer Account URL", -"var order.getCustomerName()":"Customer Name", -"var comment":"Invoice Comment", +"var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL", +"var order_data.customer_name":"Customer Name", +"var comment|escape|nl2br":"Invoice Comment", "var invoice.increment_id":"Invoice Id", "layout area=\"frontend\" handle=\"sales_email_order_invoice_items\" invoice=$invoice order=$order":"Invoice Items Grid", "var order.increment_id":"Order Id", "var payment_html|raw":"Payment Details", "var formattedShippingAddress|raw":"Shipping Address", "var order.shipping_description":"Shipping Description", -"var order.getShippingDescription()":"Shipping Description" +"var store.frontend_name":"Store Frontend Name", +"var store_email":"Store Email", +"var invoice": "Invoice", +"var order": "Order", +"var order_data.is_not_virtual": "Order Type" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$order.getCustomerName()}}</p> + <p class="greeting">{{trans "%name," name=$order_data.customer_name}}</p> <p> - {{trans "Thank you for your order from %store_name." store_name=$store.getFrontendName()}} + {{trans "Thank you for your order from %store_name." store_name=$store.frontend_name}} {{trans 'You can check the status of your order by <a href="%account_url">logging into your account</a>.' account_url=$this.getUrl($store,'customer/account/',[_nosid:1]) |raw}} </p> <p> @@ -55,7 +59,7 @@ <h1>{{trans "Your Invoice #%invoice_id for Order #%order_id" invoice_id=$invoice <h3>{{trans "Billing Info"}}</h3> <p>{{var formattedBillingAddress|raw}}</p> </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="address-details"> <h3>{{trans "Shipping Info"}}</h3> <p>{{var formattedShippingAddress|raw}}</p> @@ -67,10 +71,10 @@ <h3>{{trans "Shipping Info"}}</h3> <h3>{{trans "Payment Method"}}</h3> {{var payment_html|raw}} </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="method-info"> <h3>{{trans "Shipping Method"}}</h3> - <p>{{var order.getShippingDescription()}}</p> + <p>{{var order.shipping_description}}</p> </td> {{/depend}} </tr> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new_guest.html index b8c604daf824b..7df5ffe5f4ab8 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new_guest.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_new_guest.html @@ -4,27 +4,31 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Invoice for your %store_name order" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Invoice for your %store_name order" store_name=$store.frontend_name}} @--> <!--@vars { "var formattedBillingAddress|raw":"Billing Address", -"var billing.getName()":"Guest Customer Name", -"var comment":"Invoice Comment", +"var billing.name":"Guest Customer Name", +"var comment|escape|nl2br":"Invoice Comment", "var invoice.increment_id":"Invoice Id", "layout handle=\"sales_email_order_invoice_items\" invoice=$invoice order=$order":"Invoice Items Grid", "var order.increment_id":"Order Id", "var payment_html|raw":"Payment Details", "var formattedShippingAddress|raw":"Shipping Address", -"var order.getShippingDescription()":"Shipping Description", -"var order.shipping_description":"Shipping Description" +"var order.shipping_description":"Shipping Description", +"var store.frontend_name":"Store Frontend Name", +"var store_email":"Store Email", +"var invoice": "Invoice", +"var order": "Order", +"var order_data.is_not_virtual": "Order Type" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$billing.getName()}}</p> + <p class="greeting">{{trans "%name," name=$billing.name}}</p> <p> - {{trans "Thank you for your order from %store_name." store_name=$store.getFrontendName()}} + {{trans "Thank you for your order from %store_name." store_name=$store.frontend_name}} </p> <p> {{trans 'If you have questions about your order, you can email us at <a href="mailto:%store_email">%store_email</a>.' store_email=$store_email |raw}} @@ -53,7 +57,7 @@ <h1>{{trans "Your Invoice #%invoice_id for Order #%order_id" invoice_id=$invoice <h3>{{trans "Billing Info"}}</h3> <p>{{var formattedBillingAddress|raw}}</p> </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="address-details"> <h3>{{trans "Shipping Info"}}</h3> <p>{{var formattedShippingAddress|raw}}</p> @@ -65,10 +69,10 @@ <h3>{{trans "Shipping Info"}}</h3> <h3>{{trans "Payment Method"}}</h3> {{var payment_html|raw}} </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="method-info"> <h3>{{trans "Shipping Method"}}</h3> - <p>{{var order.getShippingDescription()}}</p> + <p>{{var order.shipping_description}}</p> </td> {{/depend}} </tr> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_update.html b/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_update.html index 8ec54f1e64d9c..834b512f82fb7 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_update.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_update.html @@ -4,27 +4,29 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Update to your %store_name invoice" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Update to your %store_name invoice" store_name=$store.frontend_name}} @--> <!--@vars { -"var this.getUrl($store, 'customer/account/')":"Customer Account URL", -"var order.getCustomerName()":"Customer Name", -"var comment":"Invoice Comment", +"var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL", +"var order_data.customer_name":"Customer Name", +"var comment|escape|nl2br":"Invoice Comment", "var invoice.increment_id":"Invoice Id", "var order.increment_id":"Order Id", -"var order.getFrontendStatusLabel()":"Order Status" +"var order_data.frontend_status_label":"Order Status", +"var store.frontend_name":"Store Frontend Name", +"var store_email":"Store Email" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$order.getCustomerName()}}</p> + <p class="greeting">{{trans "%name," name=$order_data.customer_name}}</p> <p> {{trans "Your order #%increment_id has been updated with a status of <strong>%order_status</strong>." increment_id=$order.increment_id - order_status=$order.getFrontendStatusLabel() + order_status=$order_data.frontend_status_label |raw}} {{trans 'You can check the status of your order by <a href="%account_url">logging into your account</a>.' account_url=$this.getUrl($store,'customer/account/',[_nosid:1]) |raw}} </p> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_update_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_update_guest.html index 6028db7b97730..f9e1498763cba 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_update_guest.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/invoice_update_guest.html @@ -4,26 +4,28 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Update to your %store_name invoice" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Update to your %store_name invoice" store_name=$store.frontend_name}} @--> <!--@vars { -"var billing.getName()":"Guest Customer Name", -"var comment":"Invoice Comment", +"var billing.name":"Guest Customer Name", +"var comment|escape|nl2br":"Invoice Comment", "var invoice.increment_id":"Invoice Id", "var order.increment_id":"Order Id", -"var order.getFrontendStatusLabel()":"Order Status" +"var order_data.frontend_status_label":"Order Status", +"var store.frontend_name":"Store Frontend Name", +"var store_email":"Store Email" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$billing.getName()}}</p> + <p class="greeting">{{trans "%name," name=$billing.name}}</p> <p> {{trans "Your order #%increment_id has been updated with a status of <strong>%order_status</strong>." increment_id=$order.increment_id - order_status=$order.getFrontendStatusLabel() + order_status=$order_data.frontend_status_label |raw}} </p> <p> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/order_new.html b/app/design/frontend/Magento/luma/Magento_Sales/email/order_new.html index b663dc92d1af8..745bf5c9c2eff 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/order_new.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/order_new.html @@ -4,16 +4,23 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Your %store_name order confirmation" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Your %store_name order confirmation" store_name=$store.frontend_name}} @--> <!--@vars { "var formattedBillingAddress|raw":"Billing Address", -"var order.getEmailCustomerNote()":"Email Order Note", +"var order_data.email_customer_note|escape|nl2br":"Email Order Note", "var order.increment_id":"Order Id", "layout handle=\"sales_email_order_items\" order=$order area=\"frontend\"":"Order Items Grid", "var payment_html|raw":"Payment Details", "var formattedShippingAddress|raw":"Shipping Address", -"var order.getShippingDescription()":"Shipping Description", -"var shipping_msg":"Shipping message" +"var order.shipping_description":"Shipping Description", +"var shipping_msg":"Shipping message", +"var created_at_formatted":"Order Created At (datetime)", +"var store.frontend_name":"Store Frontend Name", +"var store_email":"Store Email", +"var order":"Order", +"var order_data.is_not_virtual":"Order Type", +"var order_data.customer_name":"Customer Name", +"var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL" } @--> {{template config_path="design/email/header_template"}} @@ -21,9 +28,9 @@ <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%customer_name," customer_name=$order.getCustomerName()}}</p> + <p class="greeting">{{trans "%customer_name," customer_name=$order_data.customer_name}}</p> <p> - {{trans "Thank you for your order from %store_name." store_name=$store.getFrontendName()}} + {{trans "Thank you for your order from %store_name." store_name=$store.frontend_name}} {{trans "Once your package ships we will send you a tracking number."}} {{trans 'You can check the status of your order by <a href="%account_url">logging into your account</a>.' account_url=$this.getUrl($store,'customer/account/',[_nosid:1]) |raw}} </p> @@ -35,16 +42,16 @@ <tr class="email-summary"> <td> <h1>{{trans 'Your Order <span class="no-link">#%increment_id</span>' increment_id=$order.increment_id |raw}}</h1> - <p>{{trans 'Placed on <span class="no-link">%created_at</span>' created_at=$order.getCreatedAtFormatted(1) |raw}}</p> + <p>{{trans 'Placed on <span class="no-link">%created_at</span>' created_at=$created_at_formatted |raw}}</p> </td> </tr> <tr class="email-information"> <td> - {{depend order.getEmailCustomerNote()}} + {{depend order_data.email_customer_note}} <table class="message-info"> <tr> <td> - {{var order.getEmailCustomerNote()|escape|nl2br}} + {{var order_data.email_customer_note|escape|nl2br}} </td> </tr> </table> @@ -55,7 +62,7 @@ <h1>{{trans 'Your Order <span class="no-link">#%increment_id</span>' increment_i <h3>{{trans "Billing Info"}}</h3> <p>{{var formattedBillingAddress|raw}}</p> </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="address-details"> <h3>{{trans "Shipping Info"}}</h3> <p>{{var formattedShippingAddress|raw}}</p> @@ -67,10 +74,10 @@ <h3>{{trans "Shipping Info"}}</h3> <h3>{{trans "Payment Method"}}</h3> {{var payment_html|raw}} </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="method-info"> <h3>{{trans "Shipping Method"}}</h3> - <p>{{var order.getShippingDescription()}}</p> + <p>{{var order.shipping_description}}</p> {{if shipping_msg}} <p>{{var shipping_msg}}</p> {{/if}} diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/order_new_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/order_new_guest.html index cc32aa4a12676..907be4d45a6c5 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/order_new_guest.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/order_new_guest.html @@ -4,27 +4,31 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Your %store_name order confirmation" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Your %store_name order confirmation" store_name=$store.frontend_name}} @--> <!--@vars { "var formattedBillingAddress|raw":"Billing Address", -"var order.getEmailCustomerNote()":"Email Order Note", -"var order.getBillingAddress().getName()":"Guest Customer Name", -"var order.getCreatedAtFormatted(1)":"Order Created At (datetime)", +"var order_data.email_customer_note|escape|nl2br":"Email Order Note", +"var order.billing_address.name":"Guest Customer Name", +"var created_at_formatted":"Order Created At (datetime)", "var order.increment_id":"Order Id", "layout handle=\"sales_email_order_items\" order=$order":"Order Items Grid", "var payment_html|raw":"Payment Details", "var formattedShippingAddress|raw":"Shipping Address", -"var order.getShippingDescription()":"Shipping Description", -"var shipping_msg":"Shipping message" +"var order.shipping_description":"Shipping Description", +"var shipping_msg":"Shipping message", +"var store.frontend_name":"Store Frontend Name", +"var store_email":"Store Email", +"var order":"Order", +"var order_data.is_not_virtual":"Order Type" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$order.getBillingAddress().getName()}}</p> + <p class="greeting">{{trans "%name," name=$order.billing_address.name}}</p> <p> - {{trans "Thank you for your order from %store_name." store_name=$store.getFrontendName()}} + {{trans "Thank you for your order from %store_name." store_name=$store.frontend_name}} {{trans "Once your package ships we will send you a tracking number."}} </p> <p> @@ -35,16 +39,16 @@ <tr class="email-summary"> <td> <h1>{{trans 'Your Order <span class="no-link">#%increment_id</span>' increment_id=$order.increment_id |raw}}</h1> - <p>{{trans 'Placed on <span class="no-link">%created_at</span>' created_at=$order.getCreatedAtFormatted(1) |raw}}</p> + <p>{{trans 'Placed on <span class="no-link">%created_at</span>' created_at=$created_at_formatted |raw}}</p> </td> </tr> <tr class="email-information"> <td> - {{depend order.getEmailCustomerNote()}} + {{depend order_data.email_customer_note}} <table class="message-info"> <tr> <td> - {{var order.getEmailCustomerNote()|escape|nl2br}} + {{var order_data.email_customer_note|escape|nl2br}} </td> </tr> </table> @@ -55,7 +59,7 @@ <h1>{{trans 'Your Order <span class="no-link">#%increment_id</span>' increment_i <h3>{{trans "Billing Info"}}</h3> <p>{{var formattedBillingAddress|raw}}</p> </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="address-details"> <h3>{{trans "Shipping Info"}}</h3> <p>{{var formattedShippingAddress|raw}}</p> @@ -67,10 +71,10 @@ <h3>{{trans "Shipping Info"}}</h3> <h3>{{trans "Payment Method"}}</h3> {{var payment_html|raw}} </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="method-info"> <h3>{{trans "Shipping Method"}}</h3> - <p>{{var order.getShippingDescription()}}</p> + <p>{{var order.shipping_description}}</p> {{if shipping_msg}} <p>{{var shipping_msg}}</p> {{/if}} diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/order_update.html b/app/design/frontend/Magento/luma/Magento_Sales/email/order_update.html index fa16ac2196bf4..cec5649755b3d 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/order_update.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/order_update.html @@ -4,26 +4,30 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Update to your %store_name order" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Update to your %store_name order" store_name=$store.frontend_name}} @--> <!--@vars { -"var this.getUrl($store, 'customer/account/')":"Customer Account URL", -"var order.getCustomerName()":"Customer Name", -"var comment":"Order Comment", +"var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL", +"var order_data.customer_name":"Customer Name", +"var comment|escape|nl2br":"Order Comment", "var order.increment_id":"Order Id", -"var order.getFrontendStatusLabel()":"Order Status" +"var order_data.frontend_status_label":"Order Status", +"var store.frontend_name":"Store Frontend Name", +"var store_email":"Store Email", +"var order":"Order", +"var order_data.is_not_virtual":"Order Type" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$order.getCustomerName()}}</p> + <p class="greeting">{{trans "%name," name=$order_data.customer_name}}</p> <p> {{trans "Your order #%increment_id has been updated with a status of <strong>%order_status</strong>." increment_id=$order.increment_id - order_status=$order.getFrontendStatusLabel() + order_status=$order_data.frontend_status_label |raw}} {{trans 'You can check the status of your order by <a href="%account_url">logging into your account</a>.' account_url=$this.getUrl($store,'customer/account/',[_nosid:1]) |raw}} </p> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/order_update_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/order_update_guest.html index 8ead615fe01ca..5f23898b50018 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/order_update_guest.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/order_update_guest.html @@ -4,25 +4,27 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Update to your %store_name order" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Update to your %store_name order" store_name=$store.frontend_name}} @--> <!--@vars { -"var billing.getName()":"Guest Customer Name", -"var comment":"Order Comment", +"var billing.name":"Guest Customer Name", +"var comment|escape|nl2br":"Order Comment", "var order.increment_id":"Order Id", -"var order.getFrontendStatusLabel()":"Order Status" +"var order_data.frontend_status_label":"Order Status", +"var store.frontend_name":"Store Frontend Name", +"var store_email":"Store Email" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$billing.getName()}}</p> + <p class="greeting">{{trans "%name," name=$billing.name}}</p> <p> {{trans "Your order #%increment_id has been updated with a status of <strong>%order_status</strong>." increment_id=$order.increment_id - order_status=$order.getFrontendStatusLabel() + order_status=$order_data.frontend_status_label |raw}} </p> <p> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new.html b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new.html index e467aa843e2f4..4ff9da3a31b27 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new.html @@ -4,29 +4,33 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Your %store_name order has shipped" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Your %store_name order has shipped" store_name=$store.frontend_name}} @--> <!--@vars { "var formattedBillingAddress|raw":"Billing Address", -"var this.getUrl($store, 'customer/account/')":"Customer Account URL", -"var order.getCustomerName()":"Customer Name", +"var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL", +"var order_data.customer_name":"Customer Name", "var order.increment_id":"Order Id", "var payment_html|raw":"Payment Details", -"var comment":"Shipment Comment", +"var comment|escape|nl2br":"Shipment Comment", "var shipment.increment_id":"Shipment Id", "layout handle=\"sales_email_order_shipment_items\" shipment=$shipment order=$order":"Shipment Items Grid", "block class='Magento\\\\Framework\\\\View\\\\Element\\\\Template' area='frontend' template='Magento_Sales::email\/shipment\/track.phtml' shipment=$shipment order=$order":"Shipment Track Details", "var formattedShippingAddress|raw":"Shipping Address", "var order.shipping_description":"Shipping Description", -"var order.getShippingDescription()":"Shipping Description" +"var store.frontend_name":"Store Frontend Name", +"var store_email":"Store Email", +"var order_data.is_not_virtual": "Order Type", +"var shipment": "Shipment", +"var order": "Order" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$order.getCustomerName()}}</p> + <p class="greeting">{{trans "%name," name=$order_data.customer_name}}</p> <p> - {{trans "Thank you for your order from %store_name." store_name=$store.getFrontendName()}} + {{trans "Thank you for your order from %store_name." store_name=$store.frontend_name}} {{trans 'You can check the status of your order by <a href="%account_url">logging into your account</a>.' account_url=$this.getUrl($store,'customer/account/',[_nosid:1]) |raw}} </p> <p> @@ -58,7 +62,7 @@ <h1>{{trans "Your Shipment #%shipment_id for Order #%order_id" shipment_id=$ship <h3>{{trans "Billing Info"}}</h3> <p>{{var formattedBillingAddress|raw}}</p> </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="address-details"> <h3>{{trans "Shipping Info"}}</h3> <p>{{var formattedShippingAddress|raw}}</p> @@ -70,10 +74,10 @@ <h3>{{trans "Shipping Info"}}</h3> <h3>{{trans "Payment Method"}}</h3> {{var payment_html|raw}} </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="method-info"> <h3>{{trans "Shipping Method"}}</h3> - <p>{{var order.getShippingDescription()}}</p> + <p>{{var order.shipping_description}}</p> </td> {{/depend}} </tr> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new_guest.html index 385110f8f037e..ac7eaae6b7ff7 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new_guest.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_new_guest.html @@ -4,28 +4,33 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Your %store_name order has shipped" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Your %store_name order has shipped" store_name=$store.frontend_name}} @--> <!--@vars { "var formattedBillingAddress|raw":"Billing Address", -"var billing.getName()":"Guest Customer Name", +"var billing.name":"Guest Customer Name", "var order.increment_id":"Order Id", "var payment_html|raw":"Payment Details", -"var comment":"Shipment Comment", +"var comment|escape|nl2br":"Shipment Comment", "var shipment.increment_id":"Shipment Id", "layout handle=\"sales_email_order_shipment_items\" shipment=$shipment order=$order":"Shipment Items Grid", "block class='Magento\\\\Framework\\\\View\\\\Element\\\\Template' area='frontend' template='Magento_Sales::email\/shipment\/track.phtml' shipment=$shipment order=$order":"Shipment Track Details", "var formattedShippingAddress|raw":"Shipping Address", "var order.shipping_description":"Shipping Description", -"var order.getShippingDescription()":"Shipping Description" +"var store.frontend_name":"Store Frontend Name", +"var store_email":"Store Email", +"var order_data.is_not_virtual": "Order Type", +"var shipment": "Shipment", +"var order": "Order", +"var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$billing.getName()}}</p> + <p class="greeting">{{trans "%name," name=$billing.name}}</p> <p> - {{trans "Thank you for your order from %store_name." store_name=$store.getFrontendName()}} + {{trans "Thank you for your order from %store_name." store_name=$store.frontend_name}} </p> <p> {{trans 'If you have questions about your order, you can email us at <a href="mailto:%store_email">%store_email</a>.' store_email=$store_email |raw}} @@ -56,7 +61,7 @@ <h1>{{trans "Your Shipment #%shipment_id for Order #%order_id" shipment_id=$ship <h3>{{trans "Billing Info"}}</h3> <p>{{var formattedBillingAddress|raw}}</p> </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="address-details"> <h3>{{trans "Shipping Info"}}</h3> <p>{{var formattedShippingAddress|raw}}</p> @@ -68,10 +73,10 @@ <h3>{{trans "Shipping Info"}}</h3> <h3>{{trans "Payment Method"}}</h3> {{var payment_html|raw}} </td> - {{depend order.getIsNotVirtual()}} + {{depend order_data.is_not_virtual}} <td class="method-info"> <h3>{{trans "Shipping Method"}}</h3> - <p>{{var order.getShippingDescription()}}</p> + <p>{{var order.shipping_description}}</p> </td> {{/depend}} </tr> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_update.html b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_update.html index 4f9b7286f3ae4..f1bd61ebec3dd 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_update.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_update.html @@ -4,27 +4,29 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Update to your %store_name shipment" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Update to your %store_name shipment" store_name=$store.frontend_name}} @--> <!--@vars { -"var this.getUrl($store, 'customer/account/')":"Customer Account URL", -"var order.getCustomerName()":"Customer Name", -"var comment":"Order Comment", +"var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL", +"var order_data.customer_name":"Customer Name", +"var comment|escape|nl2br":"Order Comment", "var order.increment_id":"Order Id", -"var order.getFrontendStatusLabel()":"Order Status", -"var shipment.increment_id":"Shipment Id" +"var order_data.frontend_status_label":"Order Status", +"var shipment.increment_id":"Shipment Id", +"var store.frontend_name":"Store Frontend Name", +"var store_email":"Store Email" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$order.getCustomerName()}}</p> + <p class="greeting">{{trans "%name," name=$order_data.customer_name}}</p> <p> {{trans "Your order #%increment_id has been updated with a status of <strong>%order_status</strong>." increment_id=$order.increment_id - order_status=$order.getFrontendStatusLabel() + order_status=$order_data.frontend_status_label |raw}} {{trans 'You can check the status of your order by <a href="%account_url">logging into your account</a>.' account_url=$this.getUrl($store,'customer/account/',[_nosid:1]) |raw}} </p> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_update_guest.html b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_update_guest.html index 3ef26463ea755..5887ff73c6398 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_update_guest.html +++ b/app/design/frontend/Magento/luma/Magento_Sales/email/shipment_update_guest.html @@ -4,26 +4,28 @@ * See COPYING.txt for license details. */ --> -<!--@subject {{trans "Update to your %store_name shipment" store_name=$store.getFrontendName()}} @--> +<!--@subject {{trans "Update to your %store_name shipment" store_name=$store.frontend_name}} @--> <!--@vars { -"var billing.getName()":"Guest Customer Name", -"var comment":"Order Comment", +"var billing.name":"Guest Customer Name", +"var comment|escape|nl2br":"Order Comment", "var order.increment_id":"Order Id", -"var order.getFrontendStatusLabel()":"Order Status", -"var shipment.increment_id":"Shipment Id" +"var order_data.frontend_status_label":"Order Status", +"var shipment.increment_id":"Shipment Id", +"var store.frontend_name":"Store Frontend Name", +"var store_email":"Store Email" } @--> {{template config_path="design/email/header_template"}} <table> <tr class="email-intro"> <td> - <p class="greeting">{{trans "%name," name=$billing.getName()}}</p> + <p class="greeting">{{trans "%name," name=$billing.name}}</p> <p> {{trans "Your order #%increment_id has been updated with a status of <strong>%order_status</strong>." increment_id=$order.increment_id - order_status=$order.getFrontendStatusLabel() + order_status=$order_data.frontend_status_label |raw}} </p> <p> diff --git a/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less index cc6aba6f3566e..599218f970907 100644 --- a/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Sales/web/css/source/_module.less @@ -66,6 +66,10 @@ &:not(:last-child) { margin-bottom: @indent__l; } + + &.order-items-shipment { + overflow: visible; + } } .table-order-items { diff --git a/app/design/frontend/Magento/luma/Magento_Theme/layout/default.xml b/app/design/frontend/Magento/luma/Magento_Theme/layout/default.xml index 5f0f3b92ab44a..ce397fad64f44 100644 --- a/app/design/frontend/Magento/luma/Magento_Theme/layout/default.xml +++ b/app/design/frontend/Magento/luma/Magento_Theme/layout/default.xml @@ -14,12 +14,6 @@ </arguments> </block> </referenceContainer> - <referenceBlock name="logo"> - <arguments> - <argument name="logo_img_width" xsi:type="number">148</argument> - <argument name="logo_img_height" xsi:type="number">43</argument> - </arguments> - </referenceBlock> <referenceContainer name="footer"> <block class="Magento\Store\Block\Switcher" name="store_switcher" as="store_switcher" after="footer_links" template="Magento_Store::switch/stores.phtml"/> </referenceContainer> 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 b841f2206e1d9..438fb55d32e5c 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 @@ -148,6 +148,7 @@ img { display: block; + height: auto; } .page-print & { diff --git a/app/design/frontend/Magento/luma/media/preview.jpg b/app/design/frontend/Magento/luma/media/preview.jpg index feeda8840e562..fa6356fa02c35 100644 Binary files a/app/design/frontend/Magento/luma/media/preview.jpg and b/app/design/frontend/Magento/luma/media/preview.jpg differ diff --git a/app/design/frontend/Magento/luma/registration.php b/app/design/frontend/Magento/luma/registration.php index 41921c06fd040..f8a2c3bdb8b2e 100644 --- a/app/design/frontend/Magento/luma/registration.php +++ b/app/design/frontend/Magento/luma/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Magento/luma', __DIR__); diff --git a/app/etc/di.xml b/app/etc/di.xml index f8818de2af842..308ec1ef4d6ed 100644 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -1453,6 +1453,7 @@ <item name="primary" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Primary</item> <item name="foreign" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Foreign</item> <item name="index" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Index</item> + <item name="json" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Dto\Factories\Json</item> </argument> </arguments> </type> @@ -1480,6 +1481,7 @@ <item name="varchar" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\StringBinary</item> <item name="binary" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\StringBinary</item> <item name="varbinary" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\StringBinary</item> + <item name="json" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Columns\Json</item> <item name="index" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Index</item> <item name="unique" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Constraints\Internal</item> <item name="primary" xsi:type="object">\Magento\Framework\Setup\Declaration\Schema\Db\MySQL\Definition\Constraints\Internal</item> @@ -1593,6 +1595,7 @@ <item name="datetime" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\TimestampDefinition</item> <item name="date" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\DateDefinition</item> <item name="boolean" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\BooleanDefinition</item> + <item name="json" xsi:type="object">Magento\Framework\Setup\SchemaListenerDefinition\JsonDefinition</item> </argument> </arguments> </type> @@ -1799,4 +1802,5 @@ <plugin name="execute_commit_callbacks" type="Magento\Framework\Model\ExecuteCommitCallbacks" /> </type> <preference for="Magento\Framework\GraphQl\Query\ErrorHandlerInterface" type="Magento\Framework\GraphQl\Query\ErrorHandler"/> + <preference for="Magento\Framework\Filter\VariableResolverInterface" type="Magento\Framework\Filter\VariableResolver\StrategyResolver"/> </config> diff --git a/app/i18n/Magento/de_DE/registration.php b/app/i18n/Magento/de_DE/registration.php index 12f479d30b1e1..fae5a938af236 100644 --- a/app/i18n/Magento/de_DE/registration.php +++ b/app/i18n/Magento/de_DE/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'magento_de_de', __DIR__); diff --git a/app/i18n/Magento/en_US/registration.php b/app/i18n/Magento/en_US/registration.php index a8fd609af0592..25c6ce2b53eda 100644 --- a/app/i18n/Magento/en_US/registration.php +++ b/app/i18n/Magento/en_US/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'magento_en_us', __DIR__); diff --git a/app/i18n/Magento/es_ES/registration.php b/app/i18n/Magento/es_ES/registration.php index b22457a5be4d9..6109afe6b3aa2 100644 --- a/app/i18n/Magento/es_ES/registration.php +++ b/app/i18n/Magento/es_ES/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'magento_es_es', __DIR__); diff --git a/app/i18n/Magento/fr_FR/registration.php b/app/i18n/Magento/fr_FR/registration.php index ed866adf28cd1..bca5547092867 100644 --- a/app/i18n/Magento/fr_FR/registration.php +++ b/app/i18n/Magento/fr_FR/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'magento_fr_fr', __DIR__); diff --git a/app/i18n/Magento/nl_NL/registration.php b/app/i18n/Magento/nl_NL/registration.php index b86c41cd8c76d..27a93b13df5eb 100644 --- a/app/i18n/Magento/nl_NL/registration.php +++ b/app/i18n/Magento/nl_NL/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'magento_nl_nl', __DIR__); diff --git a/app/i18n/Magento/pt_BR/registration.php b/app/i18n/Magento/pt_BR/registration.php index eec9dc7895f29..edfbb40f0a78e 100644 --- a/app/i18n/Magento/pt_BR/registration.php +++ b/app/i18n/Magento/pt_BR/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'magento_pt_br', __DIR__); diff --git a/app/i18n/Magento/zh_Hans_CN/registration.php b/app/i18n/Magento/zh_Hans_CN/registration.php index 675778b201e41..da291a0d1b98b 100644 --- a/app/i18n/Magento/zh_Hans_CN/registration.php +++ b/app/i18n/Magento/zh_Hans_CN/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'magento_zh_hans_cn', __DIR__); diff --git a/composer.json b/composer.json index 34dd9aa5a50e9..ab767fdac286d 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ "colinmollenhour/credis": "1.10.0", "colinmollenhour/php-redis-session-abstract": "~1.4.0", "composer/composer": "^1.6", - "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1", + "elasticsearch/elasticsearch": "~2.0||~5.1||~6.1", "magento/composer": "~1.5.0", "magento/magento-composer-installer": ">=0.1.11", "magento/zendframework1": "~1.14.2", @@ -42,13 +42,13 @@ "wikimedia/less.php": "~1.8.0", "paragonie/sodium_compat": "^1.6", "pelago/emogrifier": "^2.0.0", - "php-amqplib/php-amqplib": "~2.7.0|~2.10.0", + "php-amqplib/php-amqplib": "~2.7.0||~2.10.0", "phpseclib/mcrypt_compat": "1.0.8", "phpseclib/phpseclib": "2.0.*", "ramsey/uuid": "~3.8.0", - "symfony/console": "~4.1.0|~4.2.0|~4.3.0", - "symfony/event-dispatcher": "~4.1.0|~4.2.0|~4.3.0", - "symfony/process": "~4.1.0|~4.2.0|~4.3.0", + "symfony/console": "~4.1.0||~4.2.0||~4.3.0", + "symfony/event-dispatcher": "~4.1.0||~4.2.0||~4.3.0", + "symfony/process": "~4.1.0||~4.2.0||~4.3.0", "tedivm/jshrink": "~1.3.0", "tubalmartin/cssmin": "4.1.1", "webonyx/graphql-php": "^0.13.8", @@ -84,12 +84,15 @@ }, "require-dev": { "allure-framework/allure-phpunit": "~1.2.0", + "dealerdirect/phpcodesniffer-composer-installer": "^0.5.0", "friendsofphp/php-cs-fixer": "~2.14.0", "lusitanian/oauth": "~0.8.10", - "magento/magento-coding-standard": "~4.0.0", - "magento/magento2-functional-testing-framework": "2.5.0", + "magento/magento-coding-standard": "*", + "magento/magento2-functional-testing-framework": "2.5.4", "pdepend/pdepend": "2.5.2", + "phpcompatibility/php-compatibility": "^9.3", "phpmd/phpmd": "@stable", + "phpstan/phpstan": "^0.12.3", "phpunit/phpunit": "~6.5.0", "sebastian/phpcpd": "~3.0.0", "squizlabs/php_codesniffer": "~3.4.0" @@ -191,6 +194,8 @@ "magento/module-instant-purchase": "*", "magento/module-integration": "*", "magento/module-layered-navigation": "*", + "magento/module-media-gallery": "*", + "magento/module-media-gallery-api": "*", "magento/module-media-storage": "*", "magento/module-message-queue": "*", "magento/module-msrp": "*", @@ -285,7 +290,8 @@ "components/jqueryui": "1.10.4", "twbs/bootstrap": "3.1.0", "tinymce/tinymce": "3.4.7", - "magento/module-tinymce-3": "*" + "magento/module-tinymce-3": "*", + "magento/module-csp": "*" }, "conflict": { "gene/bluefoot": "*" @@ -336,7 +342,8 @@ "Magento\\Tools\\": "dev/tools/Magento/Tools/", "Magento\\Tools\\Sanity\\": "dev/build/publication/sanity/Magento/Tools/Sanity/", "Magento\\TestFramework\\Inspection\\": "dev/tests/static/framework/Magento/TestFramework/Inspection/", - "Magento\\TestFramework\\Utility\\": "dev/tests/static/framework/Magento/TestFramework/Utility/" + "Magento\\TestFramework\\Utility\\": "dev/tests/static/framework/Magento/TestFramework/Utility/", + "Magento\\PhpStan\\": "dev/tests/static/framework/Magento/PhpStan/" } }, "prefer-stable": true diff --git a/composer.lock b/composer.lock index a53d81ec69346..b6d834610059a 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#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "86a1369b80e7beabe9ea3dcb38b89ca4", + "content-hash": "8d8e6b87c1f6ac98b3b7331eba9473f3", "packages": [ { "name": "braintree/braintree_php", @@ -257,16 +257,16 @@ }, { "name": "composer/composer", - "version": "1.9.0", + "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "314aa57fdcfc942065996f59fb73a8b3f74f3fa5" + "reference": "bb01f2180df87ce7992b8331a68904f80439dd2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/314aa57fdcfc942065996f59fb73a8b3f74f3fa5", - "reference": "314aa57fdcfc942065996f59fb73a8b3f74f3fa5", + "url": "https://api.github.com/repos/composer/composer/zipball/bb01f2180df87ce7992b8331a68904f80439dd2f", + "reference": "bb01f2180df87ce7992b8331a68904f80439dd2f", "shasum": "" }, "require": { @@ -333,7 +333,7 @@ "dependency", "package" ], - "time": "2019-08-02T18:55:33+00:00" + "time": "2019-11-01T16:20:17+00:00" }, { "name": "composer/semver", @@ -459,24 +459,24 @@ }, { "name": "composer/xdebug-handler", - "version": "1.3.3", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "46867cbf8ca9fb8d60c506895449eb799db1184f" + "reference": "cbe23383749496fe0f373345208b79568e4bc248" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/46867cbf8ca9fb8d60c506895449eb799db1184f", - "reference": "46867cbf8ca9fb8d60c506895449eb799db1184f", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/cbe23383749496fe0f373345208b79568e4bc248", + "reference": "cbe23383749496fe0f373345208b79568e4bc248", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0", + "php": "^5.3.2 || ^7.0 || ^8.0", "psr/log": "^1.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8" }, "type": "library", "autoload": { @@ -494,12 +494,12 @@ "email": "john-stevenson@blueyonder.co.uk" } ], - "description": "Restarts a process without xdebug.", + "description": "Restarts a process without Xdebug.", "keywords": [ "Xdebug", "performance" ], - "time": "2019-05-27T17:52:04+00:00" + "time": "2019-11-06T16:40:04+00:00" }, { "name": "container-interop/container-interop", @@ -530,6 +530,7 @@ ], "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", "homepage": "https://github.com/container-interop/container-interop", + "abandoned": "psr/container", "time": "2017-02-14T19:40:03+00:00" }, { @@ -594,27 +595,28 @@ }, { "name": "guzzlehttp/guzzle", - "version": "6.3.3", + "version": "6.4.1", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" + "reference": "0895c932405407fd3a7368b6910c09a24d26db11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0895c932405407fd3a7368b6910c09a24d26db11", + "reference": "0895c932405407fd3a7368b6910c09a24d26db11", "shasum": "" }, "require": { + "ext-json": "*", "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.4", + "guzzlehttp/psr7": "^1.6.1", "php": ">=5.5" }, "require-dev": { "ext-curl": "*", "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", - "psr/log": "^1.0" + "psr/log": "^1.1" }, "suggest": { "psr/log": "Required for using the Log middleware" @@ -626,12 +628,12 @@ } }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { "GuzzleHttp\\": "src/" - } + }, + "files": [ + "src/functions_include.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -655,7 +657,7 @@ "rest", "web service" ], - "time": "2018-04-22T15:46:56+00:00" + "time": "2019-10-23T15:58:00+00:00" }, { "name": "guzzlehttp/promises", @@ -1110,16 +1112,16 @@ }, { "name": "monolog/monolog", - "version": "1.25.1", + "version": "1.25.2", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "70e65a5470a42cfec1a7da00d30edb6e617e8dcf" + "reference": "d5e2fb341cb44f7e2ab639d12a1e5901091ec287" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/70e65a5470a42cfec1a7da00d30edb6e617e8dcf", - "reference": "70e65a5470a42cfec1a7da00d30edb6e617e8dcf", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/d5e2fb341cb44f7e2ab639d12a1e5901091ec287", + "reference": "d5e2fb341cb44f7e2ab639d12a1e5901091ec287", "shasum": "" }, "require": { @@ -1184,7 +1186,7 @@ "logging", "psr-3" ], - "time": "2019-09-06T13:49:17+00:00" + "time": "2019-11-13T10:00:05+00:00" }, { "name": "paragonie/random_compat", @@ -1233,16 +1235,16 @@ }, { "name": "paragonie/sodium_compat", - "version": "v1.11.4", + "version": "v1.12.1", "source": { "type": "git", "url": "https://github.com/paragonie/sodium_compat.git", - "reference": "b7115d0a80d5f9e8ae4cbfdee59d1d39dcfc90ea" + "reference": "063cae9b3a7323579063e7037720f5b52b56c178" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/b7115d0a80d5f9e8ae4cbfdee59d1d39dcfc90ea", - "reference": "b7115d0a80d5f9e8ae4cbfdee59d1d39dcfc90ea", + "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/063cae9b3a7323579063e7037720f5b52b56c178", + "reference": "063cae9b3a7323579063e7037720f5b52b56c178", "shasum": "" }, "require": { @@ -1250,7 +1252,7 @@ "php": "^5.2.4|^5.3|^5.4|^5.5|^5.6|^7|^8" }, "require-dev": { - "phpunit/phpunit": "^3|^4|^5" + "phpunit/phpunit": "^3|^4|^5|^6|^7" }, "suggest": { "ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.", @@ -1311,7 +1313,7 @@ "secret-key cryptography", "side-channel resistant" ], - "time": "2019-10-18T15:04:07+00:00" + "time": "2019-11-07T17:07:24+00:00" }, { "name": "pelago/emogrifier", @@ -1702,16 +1704,16 @@ }, { "name": "psr/log", - "version": "1.1.0", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd" + "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", - "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", + "url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801", + "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801", "shasum": "" }, "require": { @@ -1720,7 +1722,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.1.x-dev" } }, "autoload": { @@ -1745,7 +1747,7 @@ "psr", "psr-3" ], - "time": "2018-11-20T15:27:04+00:00" + "time": "2019-11-01T11:05:21+00:00" }, { "name": "ralouphie/getallheaders", @@ -1917,16 +1919,16 @@ }, { "name": "seld/jsonlint", - "version": "1.7.1", + "version": "1.7.2", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "d15f59a67ff805a44c50ea0516d2341740f81a38" + "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/d15f59a67ff805a44c50ea0516d2341740f81a38", - "reference": "d15f59a67ff805a44c50ea0516d2341740f81a38", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/e2e5d290e4d2a4f0eb449f510071392e00e10d19", + "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19", "shasum": "" }, "require": { @@ -1962,7 +1964,7 @@ "parser", "validator" ], - "time": "2018-01-24T12:46:19+00:00" + "time": "2019-10-24T14:27:39+00:00" }, { "name": "seld/phar-utils", @@ -2081,7 +2083,7 @@ }, { "name": "symfony/css-selector", - "version": "v4.3.5", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -2134,16 +2136,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v4.3.5", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "6229f58993e5a157f6096fc7145c0717d0be8807" + "reference": "0df002fd4f500392eabd243c2947061a50937287" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/6229f58993e5a157f6096fc7145c0717d0be8807", - "reference": "6229f58993e5a157f6096fc7145c0717d0be8807", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0df002fd4f500392eabd243c2947061a50937287", + "reference": "0df002fd4f500392eabd243c2947061a50937287", "shasum": "" }, "require": { @@ -2200,7 +2202,7 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2019-10-01T16:40:32+00:00" + "time": "2019-11-03T09:04:05+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -2262,7 +2264,7 @@ }, { "name": "symfony/filesystem", - "version": "v4.3.5", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", @@ -2312,16 +2314,16 @@ }, { "name": "symfony/finder", - "version": "v4.3.5", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "5e575faa95548d0586f6bedaeabec259714e44d1" + "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/5e575faa95548d0586f6bedaeabec259714e44d1", - "reference": "5e575faa95548d0586f6bedaeabec259714e44d1", + "url": "https://api.github.com/repos/symfony/finder/zipball/72a068f77e317ae77c0a0495236ad292cfb5ce6f", + "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f", "shasum": "" }, "require": { @@ -2357,7 +2359,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2019-09-16T11:29:48+00:00" + "time": "2019-10-30T12:53:54+00:00" }, { "name": "symfony/polyfill-ctype", @@ -2478,16 +2480,16 @@ }, { "name": "symfony/process", - "version": "v4.3.5", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "50556892f3cc47d4200bfd1075314139c4c9ff4b" + "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/50556892f3cc47d4200bfd1075314139c4c9ff4b", - "reference": "50556892f3cc47d4200bfd1075314139c4c9ff4b", + "url": "https://api.github.com/repos/symfony/process/zipball/3b2e0cb029afbb0395034509291f21191d1a4db0", + "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0", "shasum": "" }, "require": { @@ -2523,7 +2525,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2019-09-26T21:17:10+00:00" + "time": "2019-10-28T17:07:32+00:00" }, { "name": "tedivm/jshrink", @@ -2724,23 +2726,23 @@ }, { "name": "wikimedia/less.php", - "version": "1.8.1", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/wikimedia/less.php.git", - "reference": "f0f7768f6fa8a9d2ac6a0274f6f477c72159bf9b" + "reference": "e238ad228d74b6ffd38209c799b34e9826909266" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wikimedia/less.php/zipball/f0f7768f6fa8a9d2ac6a0274f6f477c72159bf9b", - "reference": "f0f7768f6fa8a9d2ac6a0274f6f477c72159bf9b", + "url": "https://api.github.com/repos/wikimedia/less.php/zipball/e238ad228d74b6ffd38209c799b34e9826909266", + "reference": "e238ad228d74b6ffd38209c799b34e9826909266", "shasum": "" }, "require": { - "php": ">=5.3" + "php": ">=7.2.9" }, "require-dev": { - "phpunit/phpunit": "~4.8.24" + "phpunit/phpunit": "7.5.14" }, "bin": [ "bin/lessc" @@ -2759,6 +2761,10 @@ "Apache-2.0" ], "authors": [ + { + "name": "Josh Schmidt", + "homepage": "https://github.com/oyejorge" + }, { "name": "Matt Agar", "homepage": "https://github.com/agar" @@ -2766,10 +2772,6 @@ { "name": "Martin Jantošovič", "homepage": "https://github.com/Mordred" - }, - { - "name": "Josh Schmidt", - "homepage": "https://github.com/oyejorge" } ], "description": "PHP port of the Javascript version of LESS http://lesscss.org (Originally maintained by Josh Schmidt)", @@ -2781,7 +2783,7 @@ "php", "stylesheet" ], - "time": "2019-01-19T01:01:33+00:00" + "time": "2019-11-06T18:30:11+00:00" }, { "name": "zendframework/zend-captcha", @@ -4099,16 +4101,16 @@ }, { "name": "zendframework/zend-modulemanager", - "version": "2.8.2", + "version": "2.8.4", "source": { "type": "git", "url": "https://github.com/zendframework/zend-modulemanager.git", - "reference": "394df6e12248ac430a312d4693f793ee7120baa6" + "reference": "b2596d24b9a4e36a3cd114d35d3ad0918db9a243" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/394df6e12248ac430a312d4693f793ee7120baa6", - "reference": "394df6e12248ac430a312d4693f793ee7120baa6", + "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/b2596d24b9a4e36a3cd114d35d3ad0918db9a243", + "reference": "b2596d24b9a4e36a3cd114d35d3ad0918db9a243", "shasum": "" }, "require": { @@ -4118,7 +4120,7 @@ "zendframework/zend-stdlib": "^3.1 || ^2.7" }, "require-dev": { - "phpunit/phpunit": "^6.0.8 || ^5.7.15", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-console": "^2.6", "zendframework/zend-di": "^2.6", @@ -4135,8 +4137,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev", - "dev-develop": "2.8-dev" + "dev-master": "2.8.x-dev", + "dev-develop": "2.9.x-dev" } }, "autoload": { @@ -4149,13 +4151,12 @@ "BSD-3-Clause" ], "description": "Modular application system for zend-mvc applications", - "homepage": "https://github.com/zendframework/zend-modulemanager", "keywords": [ "ZendFramework", "modulemanager", "zf" ], - "time": "2017-12-02T06:11:18+00:00" + "time": "2019-10-28T13:29:38+00:00" }, { "name": "zendframework/zend-mvc", @@ -4303,16 +4304,16 @@ }, { "name": "zendframework/zend-serializer", - "version": "2.9.0", + "version": "2.9.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-serializer.git", - "reference": "0172690db48d8935edaf625c4cba38b79719892c" + "reference": "6fb7ae016cfdf0cfcdfa2b989e6a65f351170e21" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/0172690db48d8935edaf625c4cba38b79719892c", - "reference": "0172690db48d8935edaf625c4cba38b79719892c", + "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/6fb7ae016cfdf0cfcdfa2b989e6a65f351170e21", + "reference": "6fb7ae016cfdf0cfcdfa2b989e6a65f351170e21", "shasum": "" }, "require": { @@ -4321,7 +4322,7 @@ "zendframework/zend-stdlib": "^2.7 || ^3.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.25 || ^6.4.4", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-math": "^2.6 || ^3.0", "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" @@ -4350,13 +4351,13 @@ "license": [ "BSD-3-Clause" ], - "description": "provides an adapter based interface to simply generate storable representation of PHP types by different facilities, and recover", + "description": "Serialize and deserialize PHP structures to a variety of representations", "keywords": [ "ZendFramework", "serializer", "zf" ], - "time": "2018-05-14T18:45:18+00:00" + "time": "2019-10-19T08:06:30+00:00" }, { "name": "zendframework/zend-server", @@ -4459,16 +4460,16 @@ }, { "name": "zendframework/zend-session", - "version": "2.9.0", + "version": "2.9.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-session.git", - "reference": "0a0c7ae4d8be608e30ecff714c86164ccca19ca3" + "reference": "c289c4d733ec23a389e25c7c451f4d062088511f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-session/zipball/0a0c7ae4d8be608e30ecff714c86164ccca19ca3", - "reference": "0a0c7ae4d8be608e30ecff714c86164ccca19ca3", + "url": "https://api.github.com/repos/zendframework/zend-session/zipball/c289c4d733ec23a389e25c7c451f4d062088511f", + "reference": "c289c4d733ec23a389e25c7c451f4d062088511f", "shasum": "" }, "require": { @@ -4522,7 +4523,7 @@ "session", "zf" ], - "time": "2019-09-20T12:50:51+00:00" + "time": "2019-10-28T19:40:43+00:00" }, { "name": "zendframework/zend-soap", @@ -4720,16 +4721,16 @@ }, { "name": "zendframework/zend-validator", - "version": "2.12.1", + "version": "2.12.2", "source": { "type": "git", "url": "https://github.com/zendframework/zend-validator.git", - "reference": "7b870a7515f3a35afbecc39d63f34a861f40f58b" + "reference": "fd24920c2afcf2a70d11f67c3457f8f509453a62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/7b870a7515f3a35afbecc39d63f34a861f40f58b", - "reference": "7b870a7515f3a35afbecc39d63f34a861f40f58b", + "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/fd24920c2afcf2a70d11f67c3457f8f509453a62", + "reference": "fd24920c2afcf2a70d11f67c3457f8f509453a62", "shasum": "" }, "require": { @@ -4789,7 +4790,7 @@ "validator", "zf" ], - "time": "2019-10-12T12:17:57+00:00" + "time": "2019-10-29T08:33:25+00:00" }, { "name": "zendframework/zend-view", @@ -5723,20 +5724,20 @@ }, { "name": "consolidation/robo", - "version": "1.4.10", + "version": "1.4.11", "source": { "type": "git", "url": "https://github.com/consolidation/Robo.git", - "reference": "e5a6ca64cf1324151873672e484aceb21f365681" + "reference": "5fa1d901776a628167a325baa9db95d8edf13a80" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/Robo/zipball/e5a6ca64cf1324151873672e484aceb21f365681", - "reference": "e5a6ca64cf1324151873672e484aceb21f365681", + "url": "https://api.github.com/repos/consolidation/Robo/zipball/5fa1d901776a628167a325baa9db95d8edf13a80", + "reference": "5fa1d901776a628167a325baa9db95d8edf13a80", "shasum": "" }, "require": { - "consolidation/annotated-command": "^2.10.2", + "consolidation/annotated-command": "^2.11.0", "consolidation/config": "^1.2", "consolidation/log": "~1", "consolidation/output-formatters": "^3.1.13", @@ -5766,6 +5767,7 @@ "pear/archive_tar": "^1.4.4", "php-coveralls/php-coveralls": "^1", "phpunit/php-code-coverage": "~2|~4", + "sebastian/comparator": "^1.2.4", "squizlabs/php_codesniffer": "^2.8" }, "suggest": { @@ -5827,7 +5829,7 @@ } ], "description": "Modern task runner", - "time": "2019-07-29T15:40:50+00:00" + "time": "2019-10-29T15:50:02+00:00" }, { "name": "consolidation/self-update", @@ -5965,6 +5967,72 @@ "description": "Guzzle6 transport for Vault PHP client", "time": "2019-03-10T06:17:37+00:00" }, + { + "name": "dealerdirect/phpcodesniffer-composer-installer", + "version": "v0.5.0", + "source": { + "type": "git", + "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", + "reference": "e749410375ff6fb7a040a68878c656c2e610b132" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/e749410375ff6fb7a040a68878c656c2e610b132", + "reference": "e749410375ff6fb7a040a68878c656c2e610b132", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0", + "php": "^5.3|^7", + "squizlabs/php_codesniffer": "^2|^3" + }, + "require-dev": { + "composer/composer": "*", + "phpcompatibility/php-compatibility": "^9.0", + "sensiolabs/security-checker": "^4.1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + }, + "autoload": { + "psr-4": { + "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Franck Nijhof", + "email": "franck.nijhof@dealerdirect.com", + "homepage": "http://www.frenck.nl", + "role": "Developer / IT Manager" + } + ], + "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "homepage": "http://www.dealerdirect.com", + "keywords": [ + "PHPCodeSniffer", + "PHP_CodeSniffer", + "code quality", + "codesniffer", + "composer", + "installer", + "phpcs", + "plugin", + "qa", + "quality", + "standard", + "standards", + "style guide", + "stylecheck", + "tests" + ], + "time": "2018-10-26T13:21:45+00:00" + }, { "name": "dflydev/dot-access-data", "version": "v1.1.0", @@ -6094,16 +6162,16 @@ }, { "name": "doctrine/cache", - "version": "v1.8.0", + "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "d768d58baee9a4862ca783840eca1b9add7a7f57" + "reference": "89a5c76c39c292f7798f964ab3c836c3f8192a55" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/d768d58baee9a4862ca783840eca1b9add7a7f57", - "reference": "d768d58baee9a4862ca783840eca1b9add7a7f57", + "url": "https://api.github.com/repos/doctrine/cache/zipball/89a5c76c39c292f7798f964ab3c836c3f8192a55", + "reference": "89a5c76c39c292f7798f964ab3c836c3f8192a55", "shasum": "" }, "require": { @@ -6114,7 +6182,7 @@ }, "require-dev": { "alcaeus/mongo-php-adapter": "^1.1", - "doctrine/coding-standard": "^4.0", + "doctrine/coding-standard": "^6.0", "mongodb/mongodb": "^1.1", "phpunit/phpunit": "^7.0", "predis/predis": "~1.0" @@ -6125,7 +6193,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8.x-dev" + "dev-master": "1.9.x-dev" } }, "autoload": { @@ -6138,6 +6206,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -6146,10 +6218,6 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -6159,13 +6227,21 @@ "email": "schmittjoh@gmail.com" } ], - "description": "Caching library offering an object-oriented API for many cache backends", - "homepage": "https://www.doctrine-project.org", + "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.", + "homepage": "https://www.doctrine-project.org/projects/cache.html", "keywords": [ + "abstraction", + "apcu", "cache", - "caching" + "caching", + "couchdb", + "memcached", + "php", + "redis", + "riak", + "xcache" ], - "time": "2018-08-21T18:01:43+00:00" + "time": "2019-11-15T14:31:57+00:00" }, { "name": "doctrine/inflector", @@ -6236,16 +6312,16 @@ }, { "name": "doctrine/instantiator", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "a2c590166b2133a4633738648b6b064edae0814a" + "reference": "ae466f726242e637cebdd526a7d991b9433bacf1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", - "reference": "a2c590166b2133a4633738648b6b064edae0814a", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1", + "reference": "ae466f726242e637cebdd526a7d991b9433bacf1", "shasum": "" }, "require": { @@ -6288,32 +6364,34 @@ "constructor", "instantiate" ], - "time": "2019-03-17T17:37:11+00:00" + "time": "2019-10-21T16:45:58+00:00" }, { "name": "doctrine/lexer", - "version": "1.0.2", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8" + "reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/1febd6c3ef84253d7c815bed85fc622ad207a9f8", - "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6", + "reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": "^7.2" }, "require-dev": { - "phpunit/phpunit": "^4.5" + "doctrine/coding-standard": "^6.0", + "phpstan/phpstan": "^0.11.8", + "phpunit/phpunit": "^8.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.2.x-dev" } }, "autoload": { @@ -6326,14 +6404,14 @@ "MIT" ], "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, { "name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com" }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, { "name": "Johannes Schmitt", "email": "schmittjoh@gmail.com" @@ -6348,7 +6426,7 @@ "parser", "php" ], - "time": "2019-06-08T11:03:04+00:00" + "time": "2019-10-30T14:39:59+00:00" }, { "name": "facebook/webdriver", @@ -6542,16 +6620,16 @@ }, { "name": "fzaninotto/faker", - "version": "v1.8.0", + "version": "v1.9.0", "source": { "type": "git", "url": "https://github.com/fzaninotto/Faker.git", - "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de" + "reference": "27a216cbe72327b2d6369fab721a5843be71e57d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/f72816b43e74063c8b10357394b6bba8cb1c10de", - "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de", + "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/27a216cbe72327b2d6369fab721a5843be71e57d", + "reference": "27a216cbe72327b2d6369fab721a5843be71e57d", "shasum": "" }, "require": { @@ -6560,13 +6638,11 @@ "require-dev": { "ext-intl": "*", "phpunit/phpunit": "^4.8.35 || ^5.7", - "squizlabs/php_codesniffer": "^1.5" + "squizlabs/php_codesniffer": "^2.9.2" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "1.8-dev" - } + "branch-alias": [] }, "autoload": { "psr-4": { @@ -6588,7 +6664,7 @@ "faker", "fixtures" ], - "time": "2018-07-12T10:23:15+00:00" + "time": "2019-11-14T13:13:06+00:00" }, { "name": "grasmash/expander", @@ -7105,51 +7181,61 @@ }, { "name": "magento/magento-coding-standard", - "version": "4", + "version": "5", "source": { "type": "git", "url": "https://github.com/magento/magento-coding-standard.git", - "reference": "d24e0230a532e19941ed264f57db38fad5b1008a" + "reference": "da46c5d57a43c950dfa364edc7f1f0436d5353a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento-coding-standard/zipball/d24e0230a532e19941ed264f57db38fad5b1008a", - "reference": "d24e0230a532e19941ed264f57db38fad5b1008a", + "url": "https://api.github.com/repos/magento/magento-coding-standard/zipball/da46c5d57a43c950dfa364edc7f1f0436d5353a5", + "reference": "da46c5d57a43c950dfa364edc7f1f0436d5353a5", "shasum": "" }, "require": { "php": ">=5.6.0", - "squizlabs/php_codesniffer": "^3.4" + "squizlabs/php_codesniffer": "^3.4", + "webonyx/graphql-php": ">=0.12.6 <1.0" }, "require-dev": { "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" }, "type": "phpcodesniffer-standard", + "autoload": { + "classmap": [ + "PHP_CodeSniffer/Tokenizers/" + ], + "psr-4": { + "Magento2\\": "Magento2/" + } + }, "notification-url": "https://packagist.org/downloads/", "license": [ "OSL-3.0", "AFL-3.0" ], "description": "A set of Magento specific PHP CodeSniffer rules.", - "time": "2019-08-06T15:58:37+00:00" + "time": "2019-11-04T22:08:27+00:00" }, { "name": "magento/magento2-functional-testing-framework", - "version": "2.5.0", + "version": "2.5.4", "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "5aa379674def88d1efc180d936dae1e4654c238a" + "reference": "4f482ce22a755a812b76f81020ae71d502f9d043" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/5aa379674def88d1efc180d936dae1e4654c238a", - "reference": "5aa379674def88d1efc180d936dae1e4654c238a", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/4f482ce22a755a812b76f81020ae71d502f9d043", + "reference": "4f482ce22a755a812b76f81020ae71d502f9d043", "shasum": "" }, "require": { "allure-framework/allure-codeception": "~1.3.0", - "codeception/codeception": "~2.3.4 || ~2.4.0 ", + "codeception/codeception": "~2.4.5", + "composer/composer": "^1.4", "consolidation/robo": "^1.0.0", "csharpru/vault-php": "~3.5.3", "csharpru/vault-php-guzzle6-transport": "^2.0", @@ -7208,20 +7294,20 @@ "magento", "testing" ], - "time": "2019-09-18T14:52:11+00:00" + "time": "2019-12-12T20:14:00+00:00" }, { "name": "mikey179/vfsstream", - "version": "v1.6.7", + "version": "v1.6.8", "source": { "type": "git", "url": "https://github.com/bovigo/vfsStream.git", - "reference": "2b544ac3a21bcc4dde5d90c4ae8d06f4319055fb" + "reference": "231c73783ebb7dd9ec77916c10037eff5a2b6efe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bovigo/vfsStream/zipball/2b544ac3a21bcc4dde5d90c4ae8d06f4319055fb", - "reference": "2b544ac3a21bcc4dde5d90c4ae8d06f4319055fb", + "url": "https://api.github.com/repos/bovigo/vfsStream/zipball/231c73783ebb7dd9ec77916c10037eff5a2b6efe", + "reference": "231c73783ebb7dd9ec77916c10037eff5a2b6efe", "shasum": "" }, "require": { @@ -7254,7 +7340,7 @@ ], "description": "Virtual file system to mock the real file system in unit tests.", "homepage": "http://vfs.bovigo.org/", - "time": "2019-08-01T01:38:37+00:00" + "time": "2019-10-30T15:31:00+00:00" }, { "name": "mustache/mustache", @@ -7350,6 +7436,58 @@ ], "time": "2019-08-09T12:45:53+00:00" }, + { + "name": "nikic/php-parser", + "version": "v4.3.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "9a9981c347c5c49d6dfe5cf826bb882b824080dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/9a9981c347c5c49d6dfe5cf826bb882b824080dc", + "reference": "9a9981c347c5c49d6dfe5cf826bb882b824080dc", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "0.0.5", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.3-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "time": "2019-11-08T13:50:10+00:00" + }, { "name": "pdepend/pdepend", "version": "2.5.2", @@ -7591,6 +7729,64 @@ ], "time": "2015-05-17T12:39:23+00:00" }, + { + "name": "phpcompatibility/php-compatibility", + "version": "9.3.4", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", + "reference": "1f37659196e4f3113ea506a7efba201c52303bf1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/1f37659196e4f3113ea506a7efba201c52303bf1", + "reference": "1f37659196e4f3113ea506a7efba201c52303bf1", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "squizlabs/php_codesniffer": "^2.3 || ^3.0.2" + }, + "conflict": { + "squizlabs/php_codesniffer": "2.6.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "homepage": "https://github.com/wimg", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors" + } + ], + "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.", + "homepage": "http://techblog.wimgodden.be/tag/codesniffer/", + "keywords": [ + "compatibility", + "phpcs", + "standards" + ], + "time": "2019-11-15T04:12:02+00:00" + }, { "name": "phpdocumentor/reflection-common", "version": "2.0.0", @@ -7782,20 +7978,20 @@ "authors": [ { "name": "Manuel Pichler", - "role": "Project Founder", "email": "github@manuel-pichler.de", - "homepage": "https://github.com/manuelpichler" + "homepage": "https://github.com/manuelpichler", + "role": "Project Founder" }, { "name": "Marc Würth", - "role": "Project Maintainer", "email": "ravage@bluewin.ch", - "homepage": "https://github.com/ravage84" + "homepage": "https://github.com/ravage84", + "role": "Project Maintainer" }, { "name": "Other contributors", - "role": "Contributors", - "homepage": "https://github.com/phpmd/phpmd/graphs/contributors" + "homepage": "https://github.com/phpmd/phpmd/graphs/contributors", + "role": "Contributors" } ], "description": "PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD.", @@ -7811,28 +8007,28 @@ }, { "name": "phpoption/phpoption", - "version": "1.5.0", + "version": "1.5.2", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed" + "reference": "2ba2586380f8d2b44ad1b9feb61c371020b27793" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/94e644f7d2051a5f0fcf77d81605f152eecff0ed", - "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/2ba2586380f8d2b44ad1b9feb61c371020b27793", + "reference": "2ba2586380f8d2b44ad1b9feb61c371020b27793", "shasum": "" }, "require": { "php": ">=5.3.0" }, "require-dev": { - "phpunit/phpunit": "4.7.*" + "phpunit/phpunit": "^4.7|^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-master": "1.5-dev" } }, "autoload": { @@ -7842,7 +8038,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache2" + "Apache-2.0" ], "authors": [ { @@ -7857,7 +8053,7 @@ "php", "type" ], - "time": "2015-07-25T16:39:46+00:00" + "time": "2019-11-06T22:27:00+00:00" }, { "name": "phpspec/prophecy", @@ -7922,6 +8118,46 @@ ], "time": "2019-10-03T11:07:50+00:00" }, + { + "name": "phpstan/phpstan", + "version": "0.12.3", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "c15a6ea55da71d8133399306f560cfe4d30301b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c15a6ea55da71d8133399306f560cfe4d30301b7", + "reference": "c15a6ea55da71d8133399306f560cfe4d30301b7", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.3.0", + "php": "^7.1" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.12-dev" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "time": "2019-12-14T13:41:17+00:00" + }, { "name": "phpunit/php-code-coverage", "version": "5.3.2", @@ -9110,16 +9346,16 @@ }, { "name": "symfony/browser-kit", - "version": "v4.3.5", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "78b7611c45039e8ce81698be319851529bf040b1" + "reference": "b14fa08508afd152257d5dcc7adb5f278654d972" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/78b7611c45039e8ce81698be319851529bf040b1", - "reference": "78b7611c45039e8ce81698be319851529bf040b1", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/b14fa08508afd152257d5dcc7adb5f278654d972", + "reference": "b14fa08508afd152257d5dcc7adb5f278654d972", "shasum": "" }, "require": { @@ -9165,20 +9401,20 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2019-09-10T11:25:17+00:00" + "time": "2019-10-28T17:07:32+00:00" }, { "name": "symfony/config", - "version": "v4.3.5", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "0acb26407a9e1a64a275142f0ae5e36436342720" + "reference": "8267214841c44d315a55242ea867684eb43c42ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/0acb26407a9e1a64a275142f0ae5e36436342720", - "reference": "0acb26407a9e1a64a275142f0ae5e36436342720", + "url": "https://api.github.com/repos/symfony/config/zipball/8267214841c44d315a55242ea867684eb43c42ce", + "reference": "8267214841c44d315a55242ea867684eb43c42ce", "shasum": "" }, "require": { @@ -9229,20 +9465,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2019-09-19T15:51:53+00:00" + "time": "2019-11-08T08:31:27+00:00" }, { "name": "symfony/dependency-injection", - "version": "v4.3.5", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "e1e0762a814b957a1092bff75a550db49724d05b" + "reference": "80c6d9e19467dfbba14f830ed478eb592ce51b64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/e1e0762a814b957a1092bff75a550db49724d05b", - "reference": "e1e0762a814b957a1092bff75a550db49724d05b", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/80c6d9e19467dfbba14f830ed478eb592ce51b64", + "reference": "80c6d9e19467dfbba14f830ed478eb592ce51b64", "shasum": "" }, "require": { @@ -9302,20 +9538,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2019-10-02T12:58:58+00:00" + "time": "2019-11-08T16:22:27+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.3.5", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "e9f7b4d19d69b133bd638eeddcdc757723b4211f" + "reference": "4b9efd5708c3a38593e19b6a33e40867f4f89d72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/e9f7b4d19d69b133bd638eeddcdc757723b4211f", - "reference": "e9f7b4d19d69b133bd638eeddcdc757723b4211f", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/4b9efd5708c3a38593e19b6a33e40867f4f89d72", + "reference": "4b9efd5708c3a38593e19b6a33e40867f4f89d72", "shasum": "" }, "require": { @@ -9363,20 +9599,20 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2019-09-28T21:25:05+00:00" + "time": "2019-10-28T17:07:32+00:00" }, { "name": "symfony/http-foundation", - "version": "v2.8.50", + "version": "v2.8.52", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "746f8d3638bf46ee8b202e62f2b214c3d61fb06a" + "reference": "3929d9fe8148d17819ad0178c748b8d339420709" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/746f8d3638bf46ee8b202e62f2b214c3d61fb06a", - "reference": "746f8d3638bf46ee8b202e62f2b214c3d61fb06a", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3929d9fe8148d17819ad0178c748b8d339420709", + "reference": "3929d9fe8148d17819ad0178c748b8d339420709", "shasum": "" }, "require": { @@ -9418,20 +9654,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2019-04-16T10:00:53+00:00" + "time": "2019-11-12T12:34:41+00:00" }, { "name": "symfony/options-resolver", - "version": "v4.3.5", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "81c2e120522a42f623233968244baebd6b36cb6a" + "reference": "f46c7fc8e207bd8a2188f54f8738f232533765a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/81c2e120522a42f623233968244baebd6b36cb6a", - "reference": "81c2e120522a42f623233968244baebd6b36cb6a", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/f46c7fc8e207bd8a2188f54f8738f232533765a4", + "reference": "f46c7fc8e207bd8a2188f54f8738f232533765a4", "shasum": "" }, "require": { @@ -9472,7 +9708,7 @@ "configuration", "options" ], - "time": "2019-08-08T09:29:19+00:00" + "time": "2019-10-28T20:59:01+00:00" }, { "name": "symfony/polyfill-php54", @@ -9704,16 +9940,16 @@ }, { "name": "symfony/service-contracts", - "version": "v1.1.7", + "version": "v1.1.8", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "ffcde9615dc5bb4825b9f6aed07716f1f57faae0" + "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ffcde9615dc5bb4825b9f6aed07716f1f57faae0", - "reference": "ffcde9615dc5bb4825b9f6aed07716f1f57faae0", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ffc7f5692092df31515df2a5ecf3b7302b3ddacf", + "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf", "shasum": "" }, "require": { @@ -9758,20 +9994,20 @@ "interoperability", "standards" ], - "time": "2019-09-17T11:12:18+00:00" + "time": "2019-10-14T12:27:06+00:00" }, { "name": "symfony/stopwatch", - "version": "v4.3.5", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "1e4ff456bd625be5032fac9be4294e60442e9b71" + "reference": "e96c259de6abcd0cead71f0bf4d730d53ee464d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/1e4ff456bd625be5032fac9be4294e60442e9b71", - "reference": "1e4ff456bd625be5032fac9be4294e60442e9b71", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/e96c259de6abcd0cead71f0bf4d730d53ee464d0", + "reference": "e96c259de6abcd0cead71f0bf4d730d53ee464d0", "shasum": "" }, "require": { @@ -9808,20 +10044,20 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2019-08-07T11:52:19+00:00" + "time": "2019-11-05T14:48:09+00:00" }, { "name": "symfony/yaml", - "version": "v4.3.5", + "version": "v4.3.8", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "41e16350a2a1c7383c4735aa2f9fce74cf3d1178" + "reference": "324cf4b19c345465fad14f3602050519e09e361d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/41e16350a2a1c7383c4735aa2f9fce74cf3d1178", - "reference": "41e16350a2a1c7383c4735aa2f9fce74cf3d1178", + "url": "https://api.github.com/repos/symfony/yaml/zipball/324cf4b19c345465fad14f3602050519e09e361d", + "reference": "324cf4b19c345465fad14f3602050519e09e361d", "shasum": "" }, "require": { @@ -9867,7 +10103,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2019-09-11T15:41:19+00:00" + "time": "2019-10-30T12:58:49+00:00" }, { "name": "theseer/fdomdocument", diff --git a/dev/tests/acceptance/tests/_data/importSpecChars.csv b/dev/tests/acceptance/tests/_data/importSpecChars.csv new file mode 100644 index 0000000000000..5d62286ccf2ee --- /dev/null +++ b/dev/tests/acceptance/tests/_data/importSpecChars.csv @@ -0,0 +1,2 @@ +sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_label,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,deferred_stock_update,use_config_deferred_stock_update,related_skus,related_position,crosssell_skus,crosssell_position,upsell_skus,upsell_position,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,bundle_shipment_type,configurable_variations,configurable_variation_labels,associated_skus +Mug,,Default,simple,Default Category/C1,base,Mug,<p>this is a mug</p>,,,1,Taxable Goods,"Catalog, Search",30,,,,mug,Mug,Mug,Mug ,,,,,,,,,"10/1/18, 9:21 PM","10/1/18, 11:30 PM",,,Block after Info Column,,,,Use config,,,,,,,,,gift_wrapping_available=Use config,99,0,1,0,0,1,1,1,10000,1,1,1,1,1,1,1,1,1,0,0,0,1,1,,,,,,,,,,,,,,,,,,, diff --git a/dev/tests/acceptance/tests/_data/import_productsoftwostoresdata.csv b/dev/tests/acceptance/tests/_data/import_productsoftwostoresdata.csv new file mode 100644 index 0000000000000..5cb120e7e2b2b --- /dev/null +++ b/dev/tests/acceptance/tests/_data/import_productsoftwostoresdata.csv @@ -0,0 +1,7 @@ +sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,msrp_price,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id +name4,,Default,simple,,base,name4,name4,name4,0,1,None,Catalog,39,1,7/8/2015 8:00,,name4,,,,12/16/2015 6:33,7/7/2016 13:01,,,Product Info Column,,,,,,,,Use config,,,1,0,1,0,0,0,1,1,10000,1,1,1,1,1,1,1,0,1,0,0,1 +name4,english,Default,simple,,base,, ,,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +name4,chinese,Default,simple,,base,白瓷奶勺110厘米, ,白瓷奶勺110厘米,,,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +name5,,Default,simple,,base,name5,name5,name5,0,1,,Catalog,229,111.75,7/15/2015 0:00,,name5,,,,12/16/2015 6:33,7/7/2016 13:01,,,Product Info Column,,,,,,,,Use config,,,0,0,1,0,2,2,1,1,10000,1,1,1,1,1,1,1,0,1,0,0,1 +name5,chinese,Default,simple,,base,盐磨瓶18厘米,,盐磨瓶18厘米,,2,None,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +name5,english,Default,simple,,base,,,,,2,None,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, diff --git a/dev/tests/acceptance/tests/_data/import_simple_product.csv b/dev/tests/acceptance/tests/_data/import_simple_product.csv new file mode 100644 index 0000000000000..b7eea288db4df --- /dev/null +++ b/dev/tests/acceptance/tests/_data/import_simple_product.csv @@ -0,0 +1,2 @@ +sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_label,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,deferred_stock_update,use_config_deferred_stock_update,related_skus,related_position,crosssell_skus,crosssell_position,upsell_skus,upsell_position,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,bundle_shipment_type,use_config_is_redeemable,use_config_lifetime,use_config_allow_message,use_config_email_template,associated_skus,configurable_variations,configurable_variation_labels +test_sku,,Default,simple,Default Category/simpleCategory5d53a993b7ccb2,base,Test_Product,,,,1,Taxable Goods,"Catalog, Search",560,,,,test-product,Test_Product,Test_Product,Test_Product ,,,,,,,,,"8/14/19, 6:27 AM","8/14/19, 6:27 AM",,,Block after Info Column,,,,Use config,,,,,,,Use config,,,25,0,1,0,0,1,1,1,10000,1,1,1,1,1,1,1,1,1,0,0,0,1,1,,,,,,,,,,,,,,,,,,,,,,, diff --git a/dev/tests/acceptance/tests/_data/simpleProductUpdate.csv b/dev/tests/acceptance/tests/_data/simpleProductUpdate.csv new file mode 100644 index 0000000000000..147d6f8ade275 --- /dev/null +++ b/dev/tests/acceptance/tests/_data/simpleProductUpdate.csv @@ -0,0 +1,2 @@ +sku,url_key +simpleProduct,simpleProd diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml index cac9d0c3cb55f..d38bf90a3a16d 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CGuestUserTest.xml @@ -11,7 +11,7 @@ <test name="EndToEndB2CGuestUserTest"> <!-- Search configurable product --> <comment userInput="Search configurable product" stepKey="commentSearchConfigurableProduct" after="searchAssertSimpleProduct2ImageNotDefault" /> - <actionGroup ref="StorefrontCheckCategoryConfigurableProduct" stepKey="searchAssertFilterCategoryConfigProduct" after="commentSearchConfigurableProduct"> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="searchAssertFilterCategoryConfigProduct" after="commentSearchConfigurableProduct"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -19,7 +19,7 @@ <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="searchGrabConfigProductImageSrc" after="searchAssertFilterCategoryConfigProduct"/> <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabConfigProductImageSrc" stepKey="searchAssertConfigProductImageNotDefault" after="searchGrabConfigProductImageSrc"/> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="searchClickConfigProductView" after="searchAssertConfigProductImageNotDefault"/> - <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="searchAssertConfigProductPage" after="searchClickConfigProductView"> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="searchAssertConfigProductPage" after="searchClickConfigProductView"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -30,7 +30,7 @@ <test name="EndToEndB2CGuestUserMysqlTest"> <!-- Search configurable product --> <comment userInput="Search configurable product" stepKey="commentSearchConfigurableProduct" after="searchAssertSimpleProduct2ImageNotDefault" /> - <actionGroup ref="StorefrontCheckCategoryConfigurableProduct" stepKey="searchAssertFilterCategoryConfigProduct" after="commentSearchConfigurableProduct"> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="searchAssertFilterCategoryConfigProduct" after="commentSearchConfigurableProduct"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -38,7 +38,7 @@ <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="searchGrabConfigProductImageSrc" after="searchAssertFilterCategoryConfigProduct"/> <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabConfigProductImageSrc" stepKey="searchAssertConfigProductImageNotDefault" after="searchGrabConfigProductImageSrc"/> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="searchClickConfigProductView" after="searchAssertConfigProductImageNotDefault"/> - <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="searchAssertConfigProductPage" after="searchClickConfigProductView"> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="searchAssertConfigProductPage" after="searchClickConfigProductView"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> diff --git a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml index 9fe70c8b4dd3b..f616e46d6f692 100644 --- a/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml +++ b/dev/tests/acceptance/tests/functional/Magento/FunctionalTest/ConfigurableProductCatalogSearch/Test/EndToEndB2CLoggedInUserTest.xml @@ -11,7 +11,7 @@ <test name="EndToEndB2CLoggedInUserTest"> <!-- Search configurable product --> <comment userInput="Search configurable product" stepKey="commentSearchConfigurableProduct" after="searchAssertSimpleProduct2ImageNotDefault" /> - <actionGroup ref="StorefrontCheckCategoryConfigurableProduct" stepKey="searchAssertFilterCategoryConfigProduct" after="commentSearchConfigurableProduct"> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductActionGroup" stepKey="searchAssertFilterCategoryConfigProduct" after="commentSearchConfigurableProduct"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> @@ -19,7 +19,7 @@ <grabAttributeFrom selector="{{StorefrontCategoryProductSection.ProductImageByName($$createConfigProduct.name$$)}}" userInput="src" stepKey="searchGrabConfigProductImageSrc" after="searchAssertFilterCategoryConfigProduct"/> <assertNotRegExp expected="'/placeholder\/small_image\.jpg/'" actual="$searchGrabConfigProductImageSrc" stepKey="searchAssertConfigProductImageNotDefault" after="searchGrabConfigProductImageSrc"/> <click selector="{{StorefrontCategoryProductSection.ProductTitleByName($$createConfigProduct.name$$)}}" stepKey="searchClickConfigProductView" after="searchAssertConfigProductImageNotDefault"/> - <actionGroup ref="StorefrontCheckConfigurableProduct" stepKey="searchAssertConfigProductPage" after="searchClickConfigProductView"> + <actionGroup ref="StorefrontCheckConfigurableProductActionGroup" stepKey="searchAssertConfigProductPage" after="searchClickConfigProductView"> <argument name="product" value="$$createConfigProduct$$"/> <argument name="optionProduct" value="$$createConfigChildProduct1$$"/> </actionGroup> diff --git a/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/BulkStatusInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/BulkStatusInterfaceTest.php new file mode 100644 index 0000000000000..46ffb39878c0d --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/BulkStatusInterfaceTest.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\AsynchronousOperations\Api; + +use Magento\TestFramework\TestCase\WebapiAbstract; +use Magento\Framework\Bulk\OperationInterface; + +class BulkStatusInterfaceTest extends WebapiAbstract +{ + const RESOURCE_PATH = '/V1/bulk/'; + const SERVICE_NAME = 'asynchronousOperationsBulkStatusV1'; + const GET_COUNT_OPERATION_NAME = "GetOperationsCountByBulkIdAndStatus"; + const TEST_UUID = "bulk-uuid-searchable-6"; + + /** + * @magentoApiDataFixture Magento/AsynchronousOperations/_files/operation_searchable.php + */ + public function testGetListByBulkStartTime() + { + $resourcePath = self::RESOURCE_PATH + . self::TEST_UUID + . "/operation-status/" + . OperationInterface::STATUS_TYPE_OPEN; + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => $resourcePath, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => 'V1', + 'operation' => self::SERVICE_NAME . self::GET_COUNT_OPERATION_NAME + ], + ]; + $qty = $this->_webApiCall( + $serviceInfo, + ['bulkUuid' => self::TEST_UUID, 'status' => OperationInterface::STATUS_TYPE_OPEN] + ); + $this->assertEquals(2, $qty); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/OperationRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/OperationRepositoryInterfaceTest.php index 8eab6c9fd8676..81ed561a9803e 100644 --- a/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/OperationRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/AsynchronousOperations/Api/OperationRepositoryInterfaceTest.php @@ -57,8 +57,8 @@ public function testGetListByBulkStartTime() $this->assertArrayHasKey('items', $response); $this->assertEquals($searchCriteria['searchCriteria'], $response['search_criteria']); - $this->assertEquals(3, $response['total_count']); - $this->assertEquals(3, count($response['items'])); + $this->assertEquals(5, $response['total_count']); + $this->assertEquals(5, count($response['items'])); foreach ($response['items'] as $item) { $this->assertEquals('bulk-uuid-searchable-6', $item['bulk_uuid']); diff --git a/dev/tests/api-functional/testsuite/Magento/Bundle/Api/ProductServiceTest.php b/dev/tests/api-functional/testsuite/Magento/Bundle/Api/ProductServiceTest.php index 0293f44615080..6388684466d10 100644 --- a/dev/tests/api-functional/testsuite/Magento/Bundle/Api/ProductServiceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Bundle/Api/ProductServiceTest.php @@ -282,7 +282,6 @@ protected function getBundleProductOptions($product) protected function setBundleProductOptions(&$product, $bundleProductOptions) { $product["extension_attributes"]["bundle_product_options"] = $bundleProductOptions; - return; } /** @@ -499,7 +498,8 @@ protected function deleteProductBySku($productSku) protected function saveProduct($product) { if (isset($product['custom_attributes'])) { - for ($i=0; $i<sizeof($product['custom_attributes']); $i++) { + $count = count($product['custom_attributes']); + for ($i=0; $i < $count; $i++) { if ($product['custom_attributes'][$i]['attribute_code'] == 'category_ids' && !is_array($product['custom_attributes'][$i]['value']) ) { diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php index 332e509d550ac..d614f6e913dc5 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php @@ -6,11 +6,21 @@ */ namespace Magento\Catalog\Api; +use Magento\Authorization\Model\Role; +use Magento\Authorization\Model\Rules; +use Magento\Integration\Api\AdminTokenServiceInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\WebapiAbstract; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; +use Magento\Authorization\Model\RoleFactory; +use Magento\Authorization\Model\RulesFactory; +/** + * Test repository web API. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class CategoryRepositoryTest extends WebapiAbstract { const RESOURCE_PATH = '/V1/categories'; @@ -18,6 +28,33 @@ class CategoryRepositoryTest extends WebapiAbstract private $modelId = 333; + /** + * @var RoleFactory + */ + private $roleFactory; + + /** + * @var RulesFactory + */ + private $rulesFactory; + + /** + * @var AdminTokenServiceInterface + */ + private $adminTokens; + + /** + * @inheritDoc + */ + protected function setUp() + { + parent::setUp(); + + $this->roleFactory = Bootstrap::getObjectManager()->get(RoleFactory::class); + $this->rulesFactory = Bootstrap::getObjectManager()->get(RulesFactory::class); + $this->adminTokens = Bootstrap::getObjectManager()->get(AdminTokenServiceInterface::class); + } + /** * @magentoApiDataFixture Magento/Catalog/_files/category_backend.php */ @@ -57,8 +94,10 @@ public function testInfoNoSuchEntityException() } /** + * Load category data. + * * @param int $id - * @return string + * @return array */ protected function getInfoCategory($id) { @@ -209,10 +248,11 @@ protected function getSimpleCategoryData($categoryData = []) /** * Create category process * - * @param $category - * @return int + * @param array $category + * @param string|null $token + * @return array */ - protected function createCategory($category) + protected function createCategory(array $category, ?string $token = null) { $serviceInfo = [ 'rest' => [ @@ -225,6 +265,9 @@ protected function createCategory($category) 'operation' => self::SERVICE_NAME . 'Save', ], ]; + if ($token) { + $serviceInfo['rest']['token'] = $serviceInfo['soap']['token'] = $token; + } $requestData = ['category' => $category]; return $this->_webApiCall($serviceInfo, $requestData); } @@ -251,7 +294,15 @@ protected function deleteCategory($id) return $this->_webApiCall($serviceInfo, ['categoryId' => $id]); } - protected function updateCategory($id, $data) + /** + * Update given category via web API. + * + * @param int $id + * @param array $data + * @param string|null $token + * @return array + */ + protected function updateCategory($id, $data, ?string $token = null) { $serviceInfo = [ @@ -265,6 +316,9 @@ protected function updateCategory($id, $data) 'operation' => self::SERVICE_NAME . 'Save', ], ]; + if ($token) { + $serviceInfo['rest']['token'] = $serviceInfo['soap']['token'] = $token; + } if (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) { $data['id'] = $id; @@ -272,7 +326,125 @@ protected function updateCategory($id, $data) } else { $data['id'] = $id; return $this->_webApiCall($serviceInfo, ['id' => $id, 'category' => $data]); - return $this->_webApiCall($serviceInfo, ['category' => $data]); } } + + /** + * Update admin role resources list. + * + * @param string $roleName + * @param string[] $resources + * @return void + */ + private function updateRoleResources(string $roleName, array $resources): void + { + /** @var Role $role */ + $role = $this->roleFactory->create(); + $role->load($roleName, 'role_name'); + /** @var Rules $rules */ + $rules = $this->rulesFactory->create(); + $rules->setRoleId($role->getId()); + $rules->setResources($resources); + $rules->saveRel(); + } + + /** + * Extract error returned by the server. + * + * @param \Throwable $exception + * @return string + */ + private function extractCallExceptionMessage(\Throwable $exception): string + { + if ($restResponse = json_decode($exception->getMessage(), true)) { + //REST + return $restResponse['message']; + } else { + //SOAP + return $exception->getMessage(); + } + } + + /** + * Test design settings authorization + * + * @magentoApiDataFixture Magento/User/_files/user_with_custom_role.php + * @throws \Throwable + * @return void + */ + public function testSaveDesign(): void + { + //Updating our admin user's role to allow saving categories but not their design settings. + $roleName = 'test_custom_role'; + $this->updateRoleResources($roleName, ['Magento_Catalog::categories']); + //Using the admin user with custom role. + $token = $this->adminTokens->createAdminAccessToken( + 'customRoleUser', + \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD + ); + + $categoryData = $this->getSimpleCategoryData(); + $categoryData['custom_attributes'][] = ['attribute_code' => 'custom_layout_update_file', 'value' => 'test']; + + //Creating new category with design settings. + $exceptionMessage = null; + try { + $this->createCategory($categoryData, $token); + } catch (\Throwable $exception) { + $exceptionMessage = $this->extractCallExceptionMessage($exception); + } + //We don't have the permissions. + $this->assertEquals('Not allowed to edit the category\'s design attributes', $exceptionMessage); + + //Updating the user role to allow access to design properties. + $this->updateRoleResources($roleName, ['Magento_Catalog::categories', 'Magento_Catalog::edit_category_design']); + //Making the same request with design settings. + $categoryData = $this->getSimpleCategoryData(); + foreach ($categoryData['custom_attributes'] as &$attribute) { + if ($attribute['attribute_code'] === 'custom_design') { + $attribute['value'] = 'test'; + break; + } + } + $result = $this->createCategory($categoryData, $token); + $this->assertArrayHasKey('id', $result); + //Category must be saved. + $categorySaved = $this->getInfoCategory($result['id']); + $savedCustomDesign = null; + foreach ($categorySaved['custom_attributes'] as $customAttribute) { + if ($customAttribute['attribute_code'] === 'custom_design') { + $savedCustomDesign = $customAttribute['value']; + break; + } + } + $this->assertEquals('test', $savedCustomDesign); + $categoryData = $categorySaved; + + //Updating our role to remove design properties access. + $this->updateRoleResources($roleName, ['Magento_Catalog::categories']); + //Updating the category but with the same design properties values. + //Omitting existing design attribute and keeping it's existing value + $attributes = $categoryData['custom_attributes']; + foreach ($attributes as $index => $attrData) { + if ($attrData['attribute_code'] === 'custom_design') { + unset($categoryData['custom_attributes'][$index]); + break; + } + } + unset($attributes, $index, $attrData); + $result = $this->updateCategory($categoryData['id'], $categoryData, $token); + //We haven't changed the design so operation is successful. + $this->assertArrayHasKey('id', $result); + + //Changing a design property. + $categoryData['custom_attributes'][] = ['attribute_code' => 'custom_design', 'value' => 'test2']; + $exceptionMessage = null; + try { + $this->updateCategory($categoryData['id'], $categoryData, $token); + } catch (\Throwable $exception) { + $exceptionMessage = $this->extractCallExceptionMessage($exception); + } + //We don't have permissions to do that. + $this->assertEquals('Not allowed to edit the category\'s design attributes', $exceptionMessage); + } } diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php index 1cd299149507c..a192936aeaccf 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php @@ -11,11 +11,11 @@ use Magento\Framework\Api\Data\ImageContentInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\Catalog\Model\ProductFactory; -use Magento\TestFramework\ObjectManager; use Magento\Catalog\Model\Product\Attribute\Backend\Media\ImageEntryConverter; use Magento\Catalog\Model\ProductRepository; use Magento\Framework\Webapi\Rest\Request; use Magento\TestFramework\TestCase\WebapiAbstract; +use Magento\Framework\ObjectManagerInterface; /** * Class ProductAttributeMediaGalleryManagementInterfaceTest @@ -48,11 +48,18 @@ class ProductAttributeMediaGalleryManagementInterfaceTest extends WebapiAbstract */ protected $testImagePath; + /** + * @var ObjectManagerInterface + */ + private $objectManager; + /** * @inheritDoc */ protected function setUp() { + $this->objectManager = Bootstrap::getObjectManager(); + $this->createServiceInfo = [ 'rest' => [ 'resourcePath' => '/V1/products/simple/media', @@ -98,9 +105,7 @@ protected function setUp() */ protected function getTargetSimpleProduct() { - $objectManager = Bootstrap::getObjectManager(); - - return $objectManager->get(ProductFactory::class)->create()->load(1); + return $this->objectManager->get(ProductFactory::class)->create()->load(1); } /** @@ -241,6 +246,10 @@ public function testCreateWithNotDefaultStoreId() */ public function testUpdate() { + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ + $product = $productRepository->get('simple'); + $imageId = (int)$product->getMediaGalleryImages()->getFirstItem()->getValueId(); $requestData = [ 'sku' => 'simple', 'entry' => [ @@ -257,19 +266,48 @@ public function testUpdate() . '/' . $this->getTargetGalleryEntryId(); $this->assertTrue($this->_webApiCall($this->updateServiceInfo, $requestData, null, 'all')); + $updatedImage = $this->assertMediaGalleryData($imageId, '/m/a/magento_image.jpg', 'Updated Image Text'); + $this->assertEquals(10, $updatedImage['position_default']); + $this->assertEquals(1, $updatedImage['disabled_default']); + } - $targetProduct = $this->getTargetSimpleProduct(); - $this->assertEquals('/m/a/magento_image.jpg', $targetProduct->getData('thumbnail')); - $this->assertEquals('no_selection', $targetProduct->getData('image')); - $this->assertEquals('no_selection', $targetProduct->getData('small_image')); - $mediaGallery = $targetProduct->getData('media_gallery'); - $this->assertCount(1, $mediaGallery['images']); - $updatedImage = array_shift($mediaGallery['images']); - $this->assertEquals('Updated Image Text', $updatedImage['label']); - $this->assertEquals('/m/a/magento_image.jpg', $updatedImage['file']); - $this->assertEquals(10, $updatedImage['position']); - $this->assertEquals(1, $updatedImage['disabled']); - $this->assertEquals('Updated Image Text', $updatedImage['label_default']); + /** + * Update media gallery entity with new image. + * + * @magentoApiDataFixture Magento/Catalog/_files/product_with_image.php + * @return void + */ + public function testUpdateWithNewImage(): void + { + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ + $product = $productRepository->get('simple'); + $imageId = (int)$product->getMediaGalleryImages()->getFirstItem()->getValueId(); + + $requestData = [ + 'sku' => 'simple', + 'entry' => [ + 'id' => $this->getTargetGalleryEntryId(), + 'label' => 'Updated Image Text', + 'position' => 10, + 'types' => ['thumbnail'], + 'disabled' => true, + 'media_type' => 'image', + 'content' => [ + 'base64_encoded_data' => 'iVBORw0KGgoAAAANSUhEUgAAAP8AAADGCAMAAAAqo6adAAAAA1BMVEUAAP79f' + . '+LBAAAASElEQVR4nO3BMQEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + . 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAA+BsYAAAF7hZJ0AAAAAElFTkSuQmCC', + 'type' => 'image/png', + 'name' => 'testname_updated.png', + ], + ], + ]; + + $this->updateServiceInfo['rest']['resourcePath'] = $this->updateServiceInfo['rest']['resourcePath'] + . '/' . $this->getTargetGalleryEntryId(); + + $this->assertTrue($this->_webApiCall($this->updateServiceInfo, $requestData, null, 'all')); + $updatedImage = $this->assertMediaGalleryData($imageId, '/t/e/testname_updated.png', 'Updated Image Text'); $this->assertEquals(10, $updatedImage['position_default']); $this->assertEquals(1, $updatedImage['disabled_default']); } @@ -281,6 +319,11 @@ public function testUpdate() */ public function testUpdateWithNotDefaultStoreId() { + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + /** @var \Magento\Catalog\Api\Data\ProductInterface $product */ + $product = $productRepository->get('simple'); + $imageId = (int)$product->getMediaGalleryImages()->getFirstItem()->getValueId(); + $requestData = [ 'sku' => 'simple', 'entry' => [ @@ -297,21 +340,36 @@ public function testUpdateWithNotDefaultStoreId() . '/' . $this->getTargetGalleryEntryId(); $this->assertTrue($this->_webApiCall($this->updateServiceInfo, $requestData, null, 'default')); + $updatedImage = $this->assertMediaGalleryData($imageId, '/m/a/magento_image.jpg', 'Image Alt Text'); + $this->assertEquals(1, $updatedImage['position_default']); + $this->assertEquals(0, $updatedImage['disabled_default']); + } + /** + * Check that Media Gallery data is correct. + * + * @param int $imageId + * @param string $file + * @param string $label + * @return array + */ + private function assertMediaGalleryData(int $imageId, string $file, string $label): array + { $targetProduct = $this->getTargetSimpleProduct(); - $this->assertEquals('/m/a/magento_image.jpg', $targetProduct->getData('thumbnail')); + $this->assertEquals($file, $targetProduct->getData('thumbnail')); + $this->assertEquals('no_selection', $targetProduct->getData('image')); + $this->assertEquals('no_selection', $targetProduct->getData('small_image')); $mediaGallery = $targetProduct->getData('media_gallery'); $this->assertCount(1, $mediaGallery['images']); $updatedImage = array_shift($mediaGallery['images']); - // Not default store view values were updated + $this->assertEquals($imageId, $updatedImage['value_id']); $this->assertEquals('Updated Image Text', $updatedImage['label']); - $this->assertEquals('/m/a/magento_image.jpg', $updatedImage['file']); + $this->assertEquals($file, $updatedImage['file']); $this->assertEquals(10, $updatedImage['position']); $this->assertEquals(1, $updatedImage['disabled']); - // Default store view values were not updated - $this->assertEquals('Image Alt Text', $updatedImage['label_default']); - $this->assertEquals(1, $updatedImage['position_default']); - $this->assertEquals(0, $updatedImage['disabled_default']); + $this->assertEquals($label, $updatedImage['label_default']); + + return $updatedImage; } /** @@ -564,9 +622,8 @@ public function testGet() { $productSku = 'simple'; - $objectManager = ObjectManager::getInstance(); /** @var ProductRepository $repository */ - $repository = $objectManager->create(ProductRepository::class); + $repository = $this->objectManager->create(ProductRepository::class); $product = $repository->get($productSku); $image = current($product->getMediaGallery('images')); $imageId = $image['value_id']; diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductLinkManagementInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductLinkManagementInterfaceTest.php index 011a1e40407ac..1ac61bc860759 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductLinkManagementInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductLinkManagementInterfaceTest.php @@ -81,6 +81,7 @@ protected function assertLinkedProducts($productSku, $linkType) $actual = $this->_webApiCall($serviceInfo, ['sku' => $productSku, 'type' => $linkType]); + $this->assertArrayHasKey(0, $actual); $this->assertEquals('simple', $actual[0]['linked_product_type']); $this->assertEquals('simple', $actual[0]['linked_product_sku']); $this->assertEquals(1, $actual[0]['position']); @@ -122,9 +123,13 @@ public function testAssign() $this->_webApiCall($serviceInfo, $arguments); $actual = $this->getLinkedProducts($productSku, 'related'); - array_walk($actual, function (&$item) { - $item = $item->__toArray(); - }); + array_walk( + $actual, + function (&$item) { + /** @var \Magento\Catalog\Api\Data\ProductLinkInterface $item */ + $item = $item->__toArray(); + } + ); $this->assertEquals([$linkData], $actual); } diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php index ed67f41a78234..76107ebc6a13a 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php @@ -7,10 +7,15 @@ namespace Magento\Catalog\Api; +use Magento\Authorization\Model\Role; +use Magento\Authorization\Model\Rules; +use Magento\Authorization\Model\RoleFactory; +use Magento\Authorization\Model\RulesFactory; use Magento\Catalog\Api\Data\ProductInterface; use Magento\CatalogInventory\Api\Data\StockItemInterface; use Magento\Downloadable\Api\DomainManagerInterface; use Magento\Downloadable\Model\Link; +use Magento\Integration\Api\AdminTokenServiceInterface; use Magento\Store\Model\Store; use Magento\Store\Model\Website; use Magento\Store\Model\WebsiteRepository; @@ -58,6 +63,21 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract ], ]; + /** + * @var RoleFactory + */ + private $roleFactory; + + /** + * @var RulesFactory + */ + private $rulesFactory; + + /** + * @var AdminTokenServiceInterface + */ + private $adminTokens; + /** * @inheritDoc */ @@ -65,9 +85,11 @@ protected function setUp() { parent::setUp(); - $objectManager = Bootstrap::getObjectManager(); + $this->roleFactory = Bootstrap::getObjectManager()->get(RoleFactory::class); + $this->rulesFactory = Bootstrap::getObjectManager()->get(RulesFactory::class); + $this->adminTokens = Bootstrap::getObjectManager()->get(AdminTokenServiceInterface::class); /** @var DomainManagerInterface $domainManager */ - $domainManager = $objectManager->get(DomainManagerInterface::class); + $domainManager = Bootstrap::getObjectManager()->get(DomainManagerInterface::class); $domainManager->addDomains(['example.com']); } @@ -789,16 +811,15 @@ public function testUpdateWithExtensionAttributes(): void * Update product * * @param array $product + * @param string|null $token * @return array|bool|float|int|string */ - protected function updateProduct($product) + protected function updateProduct($product, ?string $token = null) { if (isset($product['custom_attributes'])) { - foreach ($product['custom_attributes'] as &$customAttribute) { - if ($customAttribute['attribute_code'] == 'category_ids' - && !is_array($customAttribute['value']) - ) { - $customAttribute['value'] = [""]; + foreach ($product['custom_attributes'] as &$attribute) { + if ($attribute['attribute_code'] == 'category_ids' && !is_array($attribute['value'])) { + $attribute['value'] = [""]; } } } @@ -818,6 +839,9 @@ protected function updateProduct($product) 'operation' => self::SERVICE_NAME . 'Save', ], ]; + if ($token) { + $serviceInfo['rest']['token'] = $serviceInfo['soap']['token'] = $token; + } $requestData = ['product' => $product]; $response = $this->_webApiCall($serviceInfo, $requestData); return $response; @@ -1234,16 +1258,17 @@ protected function getSimpleProductData($productData = []) * * @param $product * @param string|null $storeCode + * @param string|null $token * @return mixed */ - protected function saveProduct($product, $storeCode = null) + protected function saveProduct($product, $storeCode = null, ?string $token = null) { if (isset($product['custom_attributes'])) { - foreach ($product['custom_attributes'] as &$customAttribute) { - if ($customAttribute['attribute_code'] == 'category_ids' - && !is_array($customAttribute['value']) + foreach ($product['custom_attributes'] as &$attribute) { + if ($attribute['attribute_code'] == 'category_ids' + && !is_array($attribute['value']) ) { - $customAttribute['value'] = [""]; + $attribute['value'] = [""]; } } } @@ -1258,6 +1283,9 @@ protected function saveProduct($product, $storeCode = null) 'operation' => self::SERVICE_NAME . 'Save', ], ]; + if ($token) { + $serviceInfo['rest']['token'] = $serviceInfo['soap']['token'] = $token; + } $requestData = ['product' => $product]; return $this->_webApiCall($serviceInfo, $requestData, null, $storeCode); @@ -1695,4 +1723,106 @@ private function assertMultiselectValue($productSku, $multiselectAttributeCode, } $this->assertEquals($expectedMultiselectValue, $multiselectValue); } + + /** + * Test design settings authorization + * + * @magentoApiDataFixture Magento/User/_files/user_with_custom_role.php + * @throws \Throwable + * @return void + */ + public function testSaveDesign(): void + { + //Updating our admin user's role to allow saving products but not their design settings. + /** @var Role $role */ + $role = $this->roleFactory->create(); + $role->load('test_custom_role', 'role_name'); + /** @var Rules $rules */ + $rules = $this->rulesFactory->create(); + $rules->setRoleId($role->getId()); + $rules->setResources(['Magento_Catalog::products']); + $rules->saveRel(); + //Using the admin user with custom role. + $token = $this->adminTokens->createAdminAccessToken( + 'customRoleUser', + \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD + ); + + $productData = $this->getSimpleProductData(); + $productData['custom_attributes'][] = ['attribute_code' => 'custom_design', 'value' => '1']; + + //Creating new product with design settings. + $exceptionMessage = null; + try { + $this->saveProduct($productData, null, $token); + } catch (\Throwable $exception) { + if ($restResponse = json_decode($exception->getMessage(), true)) { + //REST + $exceptionMessage = $restResponse['message']; + } else { + //SOAP + $exceptionMessage = $exception->getMessage(); + } + } + //We don't have the permissions. + $this->assertEquals('Not allowed to edit the product\'s design attributes', $exceptionMessage); + + //Updating the user role to allow access to design properties. + /** @var Rules $rules */ + $rules = Bootstrap::getObjectManager()->create(Rules::class); + $rules->setRoleId($role->getId()); + $rules->setResources(['Magento_Catalog::products', 'Magento_Catalog::edit_product_design']); + $rules->saveRel(); + //Making the same request with design settings. + $result = $this->saveProduct($productData, null, $token); + $this->assertArrayHasKey('id', $result); + //Product must be saved. + $productSaved = $this->getProduct($productData[ProductInterface::SKU]); + $savedCustomDesign = null; + foreach ($productSaved['custom_attributes'] as $customAttribute) { + if ($customAttribute['attribute_code'] === 'custom_design') { + $savedCustomDesign = $customAttribute['value']; + break; + } + } + $this->assertEquals('1', $savedCustomDesign); + $productData = $productSaved; + + //Updating our role to remove design properties access. + /** @var Rules $rules */ + $rules = Bootstrap::getObjectManager()->create(Rules::class); + $rules->setRoleId($role->getId()); + $rules->setResources(['Magento_Catalog::products']); + $rules->saveRel(); + //Updating the product but with the same design properties values. + //Removing the design attribute and keeping existing value. + $attributes = $productData['custom_attributes']; + foreach ($attributes as $i => $attribute) { + if ($attribute['attribute_code'] === 'custom_design') { + unset($productData['custom_attributes'][$i]); + break; + } + } + unset($attributes, $attribute, $i); + $result = $this->updateProduct($productData, $token); + //We haven't changed the design so operation is successful. + $this->assertArrayHasKey('id', $result); + + //Changing a design property. + $productData['custom_attributes'][] = ['attribute_code' => 'custom_design', 'value' => '2']; + $exceptionMessage = null; + try { + $this->updateProduct($productData, $token); + } catch (\Throwable $exception) { + if ($restResponse = json_decode($exception->getMessage(), true)) { + //REST + $exceptionMessage = $restResponse['message']; + } else { + //SOAP + $exceptionMessage = $exception->getMessage(); + } + } + //We don't have permissions to do that. + $this->assertEquals('Not allowed to edit the product\'s design attributes', $exceptionMessage); + } } diff --git a/dev/tests/api-functional/testsuite/Magento/CatalogInventory/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/CatalogInventory/Api/ProductRepositoryInterfaceTest.php index 5422362afd73c..f1d6949408f5b 100644 --- a/dev/tests/api-functional/testsuite/Magento/CatalogInventory/Api/ProductRepositoryInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/CatalogInventory/Api/ProductRepositoryInterfaceTest.php @@ -306,7 +306,8 @@ protected function getProduct($sku) protected function saveProduct($product) { if (isset($product['custom_attributes'])) { - for ($i=0; $i<sizeof($product['custom_attributes']); $i++) { + $count = count($product['custom_attributes']); + for ($i=0; $i < $count; $i++) { if ($product['custom_attributes'][$i]['attribute_code'] == 'category_ids' && !is_array($product['custom_attributes'][$i]['value']) ) { @@ -339,7 +340,8 @@ protected function saveProduct($product) protected function updateProduct($product) { if (isset($product['custom_attributes'])) { - for ($i=0; $i<sizeof($product['custom_attributes']); $i++) { + $count = count($product['custom_attributes']); + for ($i=0; $i < $count; $i++) { if ($product['custom_attributes'][$i]['attribute_code'] == 'category_ids' && !is_array($product['custom_attributes'][$i]['value']) ) { diff --git a/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php index c40d1918cca67..015eb067e4c8e 100644 --- a/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Cms/Api/PageRepositoryTest.php @@ -5,16 +5,23 @@ */ namespace Magento\Cms\Api; +use Magento\Authorization\Model\Role; +use Magento\Authorization\Model\Rules; +use Magento\Authorization\Model\RoleFactory; +use Magento\Authorization\Model\RulesFactory; use Magento\Cms\Api\Data\PageInterface; use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Api\SortOrder; use Magento\Framework\Api\SortOrderBuilder; +use Magento\Integration\Api\AdminTokenServiceInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\WebapiAbstract; /** * Tests for cms page service. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class PageRepositoryTest extends WebapiAbstract { @@ -47,6 +54,21 @@ class PageRepositoryTest extends WebapiAbstract */ protected $currentPage; + /** + * @var RoleFactory + */ + private $roleFactory; + + /** + * @var RulesFactory + */ + private $rulesFactory; + + /** + * @var AdminTokenServiceInterface + */ + private $adminTokens; + /** * Execute per test initialization. */ @@ -57,6 +79,9 @@ public function setUp() $this->dataObjectHelper = Bootstrap::getObjectManager()->create(\Magento\Framework\Api\DataObjectHelper::class); $this->dataObjectProcessor = Bootstrap::getObjectManager() ->create(\Magento\Framework\Reflection\DataObjectProcessor::class); + $this->roleFactory = Bootstrap::getObjectManager()->get(RoleFactory::class); + $this->rulesFactory = Bootstrap::getObjectManager()->get(RulesFactory::class); + $this->adminTokens = Bootstrap::getObjectManager()->get(AdminTokenServiceInterface::class); } /** @@ -379,4 +404,109 @@ private function deletePageByIdentifier($pageId) $this->_webApiCall($serviceInfo, [PageInterface::PAGE_ID => $pageId]); } + + /** + * Check that extra authorization is required for the design properties. + * + * @magentoApiDataFixture Magento/User/_files/user_with_custom_role.php + * @throws \Throwable + * @return void + */ + public function testSaveDesign(): void + { + //Updating our admin user's role to allow saving pages but not their design settings. + /** @var Role $role */ + $role = $this->roleFactory->create(); + $role->load('test_custom_role', 'role_name'); + /** @var Rules $rules */ + $rules = $this->rulesFactory->create(); + $rules->setRoleId($role->getId()); + $rules->setResources(['Magento_Cms::page']); + $rules->saveRel(); + //Using the admin user with custom role. + $token = $this->adminTokens->createAdminAccessToken( + 'customRoleUser', + \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD + ); + + $id = 'test-cms-page'; + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + 'token' => $token, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Save', + 'token' => $token + ], + ]; + $requestData = [ + 'page' => [ + PageInterface::IDENTIFIER => $id, + PageInterface::TITLE => 'Page title', + PageInterface::CUSTOM_THEME => 1 + ], + ]; + + //Creating new page with design settings. + $exceptionMessage = null; + try { + $this->_webApiCall($serviceInfo, $requestData); + } catch (\Throwable $exception) { + if ($restResponse = json_decode($exception->getMessage(), true)) { + //REST + $exceptionMessage = $restResponse['message']; + } else { + //SOAP + $exceptionMessage = $exception->getMessage(); + } + } + //We don't have the permissions. + $this->assertEquals('You are not allowed to change CMS pages design settings', $exceptionMessage); + + //Updating the user role to allow access to design properties. + /** @var Rules $rules */ + $rules = Bootstrap::getObjectManager()->create(Rules::class); + $rules->setRoleId($role->getId()); + $rules->setResources(['Magento_Cms::page', 'Magento_Cms::save_design']); + $rules->saveRel(); + //Making the same request with design settings. + $result = $this->_webApiCall($serviceInfo, $requestData); + $this->assertArrayHasKey('id', $result); + //Page must be saved. + $this->currentPage = $this->pageRepository->getById($result['id']); + $this->assertEquals($id, $this->currentPage->getIdentifier()); + $this->assertEquals(1, $this->currentPage->getCustomTheme()); + $requestData['page']['id'] = $this->currentPage->getId(); + + //Updating our role to remove design properties access. + /** @var Rules $rules */ + $rules = Bootstrap::getObjectManager()->create(Rules::class); + $rules->setRoleId($role->getId()); + $rules->setResources(['Magento_Cms::page']); + $rules->saveRel(); + //Updating the page but with the same design properties values. + $result = $this->_webApiCall($serviceInfo, $requestData); + //We haven't changed the design so operation is successful. + $this->assertArrayHasKey('id', $result); + //Changing a design property. + $requestData['page'][PageInterface::CUSTOM_THEME] = 2; + $exceptionMessage = null; + try { + $this->_webApiCall($serviceInfo, $requestData); + } catch (\Throwable $exception) { + if ($restResponse = json_decode($exception->getMessage(), true)) { + //REST + $exceptionMessage = $restResponse['message']; + } else { + //SOAP + $exceptionMessage = $exception->getMessage(); + } + } + //We don't have permissions to do that. + $this->assertEquals('You are not allowed to change CMS pages design settings', $exceptionMessage); + } } diff --git a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php index dc32bb2fc129a..1dc7ca1ad44a6 100644 --- a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php @@ -462,7 +462,8 @@ protected function deleteProductBySku($productSku) protected function saveProduct($product) { if (isset($product['custom_attributes'])) { - for ($i=0; $i<sizeof($product['custom_attributes']); $i++) { + $count = count($product['custom_attributes']); + for ($i=0; $i < $count; $i++) { if ($product['custom_attributes'][$i]['attribute_code'] == 'category_ids' && !is_array($product['custom_attributes'][$i]['value']) ) { diff --git a/dev/tests/api-functional/testsuite/Magento/Customer/Api/CustomerRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Customer/Api/CustomerRepositoryTest.php index 709abbbb8fbf9..7a02e2f843719 100644 --- a/dev/tests/api-functional/testsuite/Magento/Customer/Api/CustomerRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Customer/Api/CustomerRepositoryTest.php @@ -10,6 +10,9 @@ use Magento\Customer\Api\Data\AddressInterface as Address; use Magento\Framework\Api\SortOrder; use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Webapi\Rest\Request; +use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Helper\Customer as CustomerHelper; use Magento\TestFramework\TestCase\WebapiAbstract; @@ -780,6 +783,66 @@ public function testSearchCustomersMultipleFilterGroups() $this->assertEquals(0, $searchResults['total_count']); } + /** + * Test revoking all access Tokens for customer + */ + public function testRevokeAllAccessTokensForCustomer() + { + $customerData = $this->_createCustomer(); + + /** @var CustomerTokenServiceInterface $customerTokenService */ + $customerTokenService = Bootstrap::getObjectManager()->create(CustomerTokenServiceInterface::class); + $token = $customerTokenService->createCustomerAccessToken( + $customerData[Customer::EMAIL], + CustomerHelper::PASSWORD + ); + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . '/me', + 'httpMethod' => Request::HTTP_METHOD_GET, + 'token' => $token, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'GetSelf', + 'token' => $token, + ], + ]; + + $customerLoadedData = $this->_webApiCall($serviceInfo, ['customerId' => $customerData[Customer::ID]]); + self::assertGreaterThanOrEqual($customerData[Customer::UPDATED_AT], $customerLoadedData[Customer::UPDATED_AT]); + unset($customerData[Customer::UPDATED_AT]); + self::assertArraySubset($customerData, $customerLoadedData); + + $revokeToken = $customerTokenService->revokeCustomerAccessToken($customerData[Customer::ID]); + self::assertTrue($revokeToken); + + try { + $customerTokenService->revokeCustomerAccessToken($customerData[Customer::ID]); + } catch (\Throwable $exception) { + $this->assertInstanceOf(LocalizedException::class, $exception); + $this->assertEquals('This customer has no tokens.', $exception->getMessage()); + } + + $expectedMessage = 'The consumer isn\'t authorized to access %resources.'; + + try { + $this->_webApiCall($serviceInfo, ['customerId' => $customerData[Customer::ID]]); + } catch (\SoapFault $e) { + $this->assertContains( + $expectedMessage, + $e->getMessage(), + 'SoapFault does not contain expected message.' + ); + } catch (\Throwable $e) { + $errorObj = $this->processRestExceptionResult($e); + $this->assertEquals($expectedMessage, $errorObj['message']); + $this->assertEquals(['resources' => 'self'], $errorObj['parameters']); + $this->assertEquals(HTTPExceptionCodes::HTTP_UNAUTHORIZED, $e->getCode()); + } + } + /** * Retrieve customer data by Id * diff --git a/dev/tests/api-functional/testsuite/Magento/GiftMessage/Api/OrderGetRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/GiftMessage/Api/OrderGetRepositoryTest.php new file mode 100644 index 0000000000000..91d1954581da7 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GiftMessage/Api/OrderGetRepositoryTest.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GiftMessage\Api; + +use Magento\TestFramework\TestCase\WebapiAbstract; + +class OrderGetRepositoryTest extends WebapiAbstract +{ + const SERVICE_VERSION = 'V1'; + const SERVICE_NAME = 'salesOrderRepositoryV1'; + const RESOURCE_PATH = '/V1/orders/'; + + /** + * @magentoDataFixture Magento/GiftMessage/_files/order_with_message.php + * @magentoConfigFixture default_store sales/gift_options/allow_order 1 + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ + public function testGet() + { + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /** @var \Magento\Sales\Model\Order $order */ + $order = $objectManager->create(\Magento\Sales\Model\Order::class)->loadByIncrementId('100000001'); + $orderId = $order->getId(); + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . $orderId, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Get', + ], + ]; + $expectedMessage = [ + 'recipient' => 'Mercutio', + 'sender' => 'Romeo', + 'message' => 'I thought all for the best.', + ]; + $requestData = ['id' => $orderId]; + $result = $this->_webApiCall($serviceInfo, $requestData); + $resultMessage = $result['extension_attributes']['gift_message']; + static::assertCount(5, $resultMessage); + unset($resultMessage['gift_message_id']); + unset($resultMessage['customer_id']); + static::assertEquals($expectedMessage, $resultMessage); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GiftMessage/Api/OrderItemGetRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/GiftMessage/Api/OrderItemGetRepositoryTest.php new file mode 100644 index 0000000000000..074133835f6da --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GiftMessage/Api/OrderItemGetRepositoryTest.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GiftMessage\Api; + +use Magento\TestFramework\TestCase\WebapiAbstract; + +class OrderItemGetRepositoryTest extends WebapiAbstract +{ + const SERVICE_VERSION = 'V1'; + const SERVICE_NAME = 'salesOrderItemRepositoryV1'; + const RESOURCE_PATH = '/V1/orders/items/'; + + /** + * @magentoDataFixture Magento/GiftMessage/_files/order_with_message.php + * @magentoConfigFixture default_store sales/gift_options/allow_items 1 + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ + public function testGet() + { + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /** @var \Magento\Sales\Model\Order $order */ + $order = $objectManager->create(\Magento\Sales\Model\Order::class)->loadByIncrementId('100000001'); + $items = $order->getItems(); + /** @var \Magento\Sales\Api\Data\OrderItemInterface $orderItem */ + $orderItem = array_shift($items); + $itemId = $orderItem->getItemId(); + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . $itemId, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Get', + ], + ]; + $expectedMessage = [ + 'recipient' => 'Mercutio', + 'sender' => 'Romeo', + 'message' => 'I thought all for the best.', + ]; + $requestData = ['id' => $itemId]; + $result = $this->_webApiCall($serviceInfo, $requestData); + $resultMessage = $result['extension_attributes']['gift_message']; + static::assertCount(5, $resultMessage); + unset($resultMessage['gift_message_id']); + unset($resultMessage['customer_id']); + static::assertEquals($expectedMessage, $resultMessage); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Customer/SetPaymentMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Customer/SetPaymentMethodTest.php index 0ca1be775258d..d6954c249f209 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Customer/SetPaymentMethodTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/AuthorizenetAcceptjs/Customer/SetPaymentMethodTest.php @@ -126,17 +126,19 @@ public function dataProviderTestPlaceOrder(): array * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php * @dataProvider dataProviderSetPaymentInvalidInput * @param \Closure $getMutationClosure - * @param string $expectedMessage + * @param array $expectedMessages * @expectedException \Exception */ - public function testSetPaymentInvalidInput(\Closure $getMutationClosure, string $expectedMessage) + public function testSetPaymentInvalidInput(\Closure $getMutationClosure, array $expectedMessages) { $reservedOrderId = 'test_quote'; $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId); $setPaymentMutation = $getMutationClosure($maskedQuoteId); - $this->expectExceptionMessage($expectedMessage); + foreach ($expectedMessages as $expectedMessage) { + $this->expectExceptionMessage($expectedMessage); + } $this->graphQlMutation($setPaymentMutation, [], '', $this->getHeaderMap()); } @@ -152,30 +154,10 @@ public function dataProviderSetPaymentInvalidInput(): array function (string $maskedQuoteId) { return $this->getInvalidSetPaymentMutation($maskedQuoteId); }, - 'Required parameter "authorizenet_acceptjs" for "payment_method" is missing.', - ], - [ - function (string $maskedQuoteId) { - return $this->getEmptyAcceptJsInput($maskedQuoteId); - }, - 'for "authorizenet_acceptjs" is missing.', - ], - [ - function (string $maskedQuoteId) { - return $this->getMissingCcLastFourAcceptJsInput( - $maskedQuoteId, - static::VALID_DESCRIPTOR, - static::VALID_NONCE - ); - }, - 'parameter "cc_last_4" for "authorizenet_acceptjs" is missing', - ], - [ - function (string $maskedQuoteId) { - return $this->getMissingOpaqueDataValueAcceptJsInput($maskedQuoteId, static::VALID_DESCRIPTOR); - }, - 'parameter "opaque_data_value" for "authorizenet_acceptjs" is missing', - ], + [ + 'Required parameter "authorizenet_acceptjs" for "payment_method" is missing.' + ] + ] ]; } @@ -205,93 +187,6 @@ private function getInvalidSetPaymentMutation(string $maskedQuoteId): string QUERY; } - /** - * Get setPaymentMethodOnCart missing required additional data properties - * - * @param string $maskedQuoteId - * @return string - */ - private function getEmptyAcceptJsInput(string $maskedQuoteId): string - { - return <<<QUERY -mutation { - setPaymentMethodOnCart(input:{ - cart_id:"{$maskedQuoteId}" - payment_method:{ - code:"authorizenet_acceptjs" - authorizenet_acceptjs: {} - } - }) { - cart { - selected_payment_method { - code - } - } - } -} -QUERY; - } - - /** - * Get setPaymentMethodOnCart missing required additional data properties - * - * @param string $maskedQuoteId - * @return string - */ - private function getMissingCcLastFourAcceptJsInput(string $maskedQuoteId, string $descriptor, string $nonce): string - { - return <<<QUERY -mutation { - setPaymentMethodOnCart(input:{ - cart_id:"{$maskedQuoteId}" - payment_method:{ - code:"authorizenet_acceptjs" - authorizenet_acceptjs:{ - opaque_data_descriptor: "{$descriptor}" - opaque_data_value: "{$nonce}" - } - } - }) { - cart { - selected_payment_method { - code - } - } - } -} -QUERY; - } - - /** - * Get setPaymentMethodOnCart missing required additional data properties - * - * @param string $maskedQuoteId - * @return string - */ - private function getMissingOpaqueDataValueAcceptJsInput(string $maskedQuoteId, string $descriptor): string - { - return <<<QUERY -mutation { - setPaymentMethodOnCart(input:{ - cart_id:"{$maskedQuoteId}" - payment_method:{ - code:"authorizenet_acceptjs" - authorizenet_acceptjs:{ - opaque_data_descriptor: "{$descriptor}" - cc_last_4: 1111 - } - } - }) { - cart { - selected_payment_method { - code - } - } - } -} -QUERY; - } - private function assertPlaceOrderResponse(array $response, string $reservedOrderId): void { self::assertArrayHasKey('placeOrder', $response); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Customer/SetPaymentMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Customer/SetPaymentMethodTest.php index ad756dfdd2e44..a36a4f5d38223 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Customer/SetPaymentMethodTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Customer/SetPaymentMethodTest.php @@ -259,34 +259,14 @@ public function testSetPaymentMethodInvalidMethodInput(string $methodCode) $methodCode ); $this->expectExceptionMessage("for \"$methodCode\" is missing."); - $this->graphQlMutation($setPaymentQuery, [], '', $this->getHeaderMap()); - } - - /** - * @magentoConfigFixture default_store carriers/flatrate/active 1 - * @magentoConfigFixture default_store payment/braintree/active 1 - * @magentoConfigFixture default_store payment/braintree_cc_vault/active 1 - * @magentoConfigFixture default_store payment/braintree/environment sandbox - * @magentoConfigFixture default_store payment/braintree/merchant_id def_merchant_id - * @magentoConfigFixture default_store payment/braintree/public_key def_public_key - * @magentoConfigFixture default_store payment/braintree/private_key def_private_key - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php - * @dataProvider dataProviderTestSetPaymentMethodInvalidInput - * @expectedException \Exception - */ - public function testSetPaymentMethodWithoutRequiredPaymentMethodInput() - { - $reservedOrderId = 'test_quote'; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId); + $expectedExceptionMessages = [ + 'braintree' => + 'Field BraintreeInput.is_active_payment_token_enabler of required type Boolean! was not provided.', + 'braintree_cc_vault' => + 'Field BraintreeCcVaultInput.public_hash of required type String! was not provided.' + ]; - $setPaymentQuery = $this->getSetPaymentBraintreeQueryInvalidPaymentMethodInput($maskedQuoteId); - $this->expectExceptionMessage("for \"braintree\" is missing."); + $this->expectExceptionMessage($expectedExceptionMessages[$methodCode]); $this->graphQlMutation($setPaymentQuery, [], '', $this->getHeaderMap()); } @@ -400,33 +380,6 @@ private function getSetPaymentBraintreeQueryInvalidInput(string $maskedQuoteId, QUERY; } - /** - * @param string $maskedQuoteId - * @return string - */ - private function getSetPaymentBraintreeQueryInvalidPaymentMethodInput(string $maskedQuoteId): string - { - return <<<QUERY -mutation { - setPaymentMethodOnCart(input:{ - cart_id:"{$maskedQuoteId}" - payment_method:{ - code:"braintree" - braintree:{ - payment_method_nonce:"fake-valid-nonce" - } - } - }) { - cart { - selected_payment_method { - code - } - } - } -} -QUERY; - } - /** * @param string $maskedQuoteId * @param string $methodCode diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Guest/SetPaymentMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Guest/SetPaymentMethodTest.php index 5ee7dd457657c..5376634c05146 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Guest/SetPaymentMethodTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Guest/SetPaymentMethodTest.php @@ -129,32 +129,6 @@ public function testSetPaymentMethodInvalidInput() $this->graphQlMutation($setPaymentQuery); } - /** - * @magentoConfigFixture default_store carriers/flatrate/active 1 - * @magentoConfigFixture default_store payment/braintree/active 1 - * @magentoConfigFixture default_store payment/braintree/environment sandbox - * @magentoConfigFixture default_store payment/braintree/merchant_id def_merchant_id - * @magentoConfigFixture default_store payment/braintree/public_key def_public_key - * @magentoConfigFixture default_store payment/braintree/private_key def_private_key - * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php - * @expectedException \Exception - */ - public function testSetPaymentMethodInvalidMethodInput() - { - $reservedOrderId = 'test_quote'; - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId); - - $setPaymentQuery = $this->getSetPaymentBraintreeQueryInvalidMethodInput($maskedQuoteId); - $this->expectExceptionMessage("for \"braintree\" is missing."); - $this->graphQlMutation($setPaymentQuery); - } - private function assertPlaceOrderResponse(array $response, string $reservedOrderId): void { self::assertArrayHasKey('placeOrder', $response); @@ -224,31 +198,6 @@ private function getSetPaymentBraintreeQueryInvalidInput(string $maskedQuoteId): QUERY; } - /** - * @param string $maskedQuoteId - * @return string - */ - private function getSetPaymentBraintreeQueryInvalidMethodInput(string $maskedQuoteId): string - { - return <<<QUERY -mutation { - setPaymentMethodOnCart(input:{ - cart_id:"{$maskedQuoteId}" - payment_method:{ - code:"braintree" - braintree: {} - } - }) { - cart { - selected_payment_method { - code - } - } - } -} -QUERY; - } - /** * @param string $maskedQuoteId * @return string diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php new file mode 100644 index 0000000000000..826083b0b3378 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartTest.php @@ -0,0 +1,274 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Bundle; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\TestFramework\TestCase\GraphQlAbstract; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; + +/** + * Test adding bundled products to cart + */ +class AddBundleProductToCartTest extends GraphQlAbstract +{ + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var Quote + */ + private $quote; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @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); + $this->productRepository = $objectManager->get(ProductRepositoryInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Bundle/_files/product_1.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + */ + public function testAddBundleProductToCart() + { + $sku = 'bundle-product'; + + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + + $product = $this->productRepository->get($sku); + + /** @var $typeInstance \Magento\Bundle\Model\Product\Type */ + $typeInstance = $product->getTypeInstance(); + $typeInstance->setStoreFilter($product->getStoreId(), $product); + /** @var $option \Magento\Bundle\Model\Option */ + $option = $typeInstance->getOptionsCollection($product)->getFirstItem(); + /** @var \Magento\Catalog\Model\Product $selection */ + $selection = $typeInstance->getSelectionsCollection([$option->getId()], $product)->getFirstItem(); + $optionId = $option->getId(); + $selectionId = $selection->getSelectionId(); + + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = <<<QUERY +mutation { + addBundleProductsToCart(input:{ + cart_id:"{$maskedQuoteId}" + cart_items:[ + { + data:{ + sku:"{$sku}" + quantity:1 + } + bundle_options:[ + { + id:{$optionId} + quantity:1 + value:[ + "{$selectionId}" + ] + } + ] + } + ] + }) { + cart { + items { + id + quantity + product { + sku + } + ... on BundleCartItem { + bundle_options { + id + label + type + values { + id + label + price + quantity + } + } + } + } + } + } +} +QUERY; + + $response = $this->graphQlMutation($query); + + $this->assertArrayHasKey('addBundleProductsToCart', $response); + $this->assertArrayHasKey('cart', $response['addBundleProductsToCart']); + $cart = $response['addBundleProductsToCart']['cart']; + $bundleItem = current($cart['items']); + $this->assertEquals($sku, $bundleItem['product']['sku']); + $bundleItemOption = current($bundleItem['bundle_options']); + $this->assertEquals($optionId, $bundleItemOption['id']); + $this->assertEquals($option->getTitle(), $bundleItemOption['label']); + $this->assertEquals($option->getType(), $bundleItemOption['type']); + $value = current($bundleItemOption['values']); + $this->assertEquals($selection->getSelectionId(), $value['id']); + $this->assertEquals((float) $selection->getSelectionPriceValue(), $value['price']); + $this->assertEquals(1, $value['quantity']); + } + + /** + * @magentoApiDataFixture Magento/Bundle/_files/quote_with_bundle_and_options.php + * @dataProvider dataProviderTestUpdateBundleItemQuantity + */ + public function testUpdateBundleItemQuantity(int $quantity) + { + $this->quoteResource->load( + $this->quote, + 'test_cart_with_bundle_and_options', + 'reserved_order_id' + ); + + $item = current($this->quote->getAllVisibleItems()); + + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $mutation = <<<QUERY +mutation { + updateCartItems( + input: { + cart_id: "{$maskedQuoteId}" + cart_items: { + cart_item_id: {$item->getId()} + quantity: {$quantity} + } + } + ) { + cart { + items { + id + quantity + product { + sku + } + } + } + } +} +QUERY; + + $response = $this->graphQlMutation($mutation); + + $this->assertArrayHasKey('updateCartItems', $response); + $this->assertArrayHasKey('cart', $response['updateCartItems']); + $cart = $response['updateCartItems']['cart']; + if ($quantity === 0) { + $this->assertCount(0, $cart['items']); + return; + } + + $bundleItem = current($cart['items']); + $this->assertEquals($quantity, $bundleItem['quantity']); + } + + public function dataProviderTestUpdateBundleItemQuantity(): array + { + return [ + [2], + [0], + ]; + } + + /** + * @magentoApiDataFixture Magento/Bundle/_files/product_1.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + * @expectedException \Exception + * @expectedExceptionMessage Please select all required options + */ + public function testAddBundleToCartWithoutOptions() + { + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = <<<QUERY +mutation { + addBundleProductsToCart(input:{ + cart_id:"{$maskedQuoteId}" + cart_items:[ + { + data:{ + sku:"bundle-product" + quantity:1 + } + bundle_options:[ + { + id:555 + quantity:1 + value:[ + "555" + ] + } + ] + } + ] + }) { + cart { + items { + id + quantity + product { + sku + } + ... on BundleCartItem { + bundle_options { + id + label + type + values { + id + label + price + quantity + } + } + } + } + } + } +} +QUERY; + + $this->graphQlMutation($query); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductViewTest.php index 5d08078cf7646..4c58241590540 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductViewTest.php @@ -10,13 +10,12 @@ use Magento\Bundle\Model\Product\OptionList; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\TestFramework\ObjectManager; use Magento\TestFramework\TestCase\GraphQlAbstract; /** - * Bundle product view test + * Test querying Bundle products */ class BundleProductViewTest extends GraphQlAbstract { @@ -83,12 +82,7 @@ public function testAllFieldsBundleProducts() /** @var ProductRepositoryInterface $productRepository */ $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); - /** @var MetadataPool $metadataPool */ - $metadataPool = ObjectManager::getInstance()->get(MetadataPool::class); $bundleProduct = $productRepository->get($productSku, false, null, true); - $bundleProduct->setId( - $bundleProduct->getData($metadataPool->getMetadata(ProductInterface::class)->getLinkField()) - ); if ((bool)$bundleProduct->getShipmentType()) { $this->assertEquals('SEPARATELY', $response['products']['items'][0]['ship_bundle_items']); } else { @@ -182,12 +176,7 @@ public function testBundleProductWithNotVisibleChildren() /** @var ProductRepositoryInterface $productRepository */ $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); - /** @var MetadataPool $metadataPool */ - $metadataPool = ObjectManager::getInstance()->get(MetadataPool::class); $bundleProduct = $productRepository->get($productSku, false, null, true); - $bundleProduct->setId( - $bundleProduct->getData($metadataPool->getMetadata(ProductInterface::class)->getLinkField()) - ); if ((bool)$bundleProduct->getShipmentType()) { $this->assertEquals('SEPARATELY', $response['products']['items'][0]['ship_bundle_items']); } else { @@ -238,7 +227,6 @@ private function assertBundleProductOptions($product, $actualResponse) $actualResponse['items'], "Precondition failed: 'bundle product items' must not be empty" ); - $metadataPool = ObjectManager::getInstance()->get(MetadataPool::class); /** @var OptionList $optionList */ $optionList = ObjectManager::getInstance()->get(\Magento\Bundle\Model\Product\OptionList::class); $options = $optionList->getItems($product); @@ -249,10 +237,6 @@ private function assertBundleProductOptions($product, $actualResponse) $childProductSku = $bundleProductLink->getSku(); $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); $childProduct = $productRepository->get($childProductSku); - /** @var MetadataPool $metadataPool */ - $childProduct->setId( - $childProduct->getData($metadataPool->getMetadata(ProductInterface::class)->getLinkField()) - ); $this->assertEquals(1, count($options)); $this->assertResponseFields( $actualResponse['items'][0], diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryCanonicalUrlTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryCanonicalUrlTest.php new file mode 100644 index 0000000000000..794df3a8b6f44 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryCanonicalUrlTest.php @@ -0,0 +1,89 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Catalog; + +use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use Magento\TestFramework\TestCase\GraphQlAbstract; +use Magento\Catalog\Api\Data\CategoryInterface; + +/** + * Test for getting canonical url data from category + */ +class CategoryCanonicalUrlTest extends GraphQlAbstract +{ + /** @var ObjectManager $objectManager */ + private $objectManager; + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @magentoConfigFixture default_store catalog/seo/category_canonical_tag 1 + */ + public function testCategoryWithCanonicalLinksMetaTagSettingsEnabled() + { + $this->objectManager = Bootstrap::getObjectManager(); + /** @var CategoryCollection $categoryCollection */ + $categoryCollection = $this->objectManager->create(CategoryCollection::class); + $categoryCollection->addFieldToFilter('name', 'Category 1.1.1'); + /** @var CategoryInterface $category */ + $category = $categoryCollection->getFirstItem(); + $categoryId = $category->getId(); + $query = <<<QUERY + { +categoryList(filters: {ids: {in: ["$categoryId"]}}) { + id + name + url_key + url_suffix + canonical_url + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertNotEmpty($response['categoryList'], 'Category list should not be empty'); + $this->assertEquals('.html', $response['categoryList'][0]['url_suffix']); + $this->assertEquals( + 'category-1/category-1-1/category-1-1-1.html', + $response['categoryList'][0]['canonical_url'] + ); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories.php + * @magentoConfigFixture default_store catalog/seo/category_canonical_tag 0 + */ + public function testCategoryWithCanonicalLinksMetaTagSettingsDisabled() + { + $this->objectManager = Bootstrap::getObjectManager(); + /** @var CategoryCollection $categoryCollection */ + $categoryCollection = $this->objectManager->create(CategoryCollection::class); + $categoryCollection->addFieldToFilter('name', 'Category 1.1'); + /** @var CategoryInterface $category */ + $category = $categoryCollection->getFirstItem(); + $categoryId = $category->getId(); + $query = <<<QUERY + { +categoryList(filters: {ids: {in: ["$categoryId"]}}) { + id + name + url_key + canonical_url + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertNotEmpty($response['categoryList'], 'Category list should not be empty'); + $this->assertNull( + $response['categoryList'][0]['canonical_url'] + ); + $this->assertEquals('category-1-1', $response['categoryList'][0]['url_key']); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php index 0e88af2fcb22e..96e8ae79b612e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoryListTest.php @@ -209,6 +209,86 @@ public function testQueryChildCategoriesWithProducts() $this->assertCategoryChildren($secondChildCategory, $firstChildCategoryChildren); } + /** + * @magentoApiDataFixture Magento/Catalog/_files/categories_disabled.php + */ + public function testQueryCategoryWithDisabledChildren() + { + $query = <<<QUERY +{ + categoryList(filters: {ids: {in: ["3"]}}){ + id + name + image + url_key + url_path + description + products{ + total_count + items{ + name + sku + } + } + children{ + name + image + url_key + description + products{ + total_count + items{ + name + sku + } + } + children{ + name + image + children{ + name + image + } + } + } + } +} +QUERY; + $result = $this->graphQlQuery($query); + + $this->assertArrayNotHasKey('errors', $result); + $this->assertCount(1, $result['categoryList']); + $baseCategory = $result['categoryList'][0]; + + $this->assertEquals('Category 1', $baseCategory['name']); + $this->assertArrayHasKey('products', $baseCategory); + //Check base category products + $expectedBaseCategoryProducts = [ + ['sku' => 'simple', 'name' => 'Simple Product'], + ['sku' => '12345', 'name' => 'Simple Product Two'], + ['sku' => 'simple-4', 'name' => 'Simple Product Three'] + ]; + $this->assertCategoryProducts($baseCategory, $expectedBaseCategoryProducts); + //Check base category children + $expectedBaseCategoryChildren = [ + ['name' => 'Category 1.2', 'description' => 'Its a description of Test Category 1.2'] + ]; + $this->assertCategoryChildren($baseCategory, $expectedBaseCategoryChildren); + + //Check first child category + $firstChildCategory = $baseCategory['children'][0]; + $this->assertEquals('Category 1.2', $firstChildCategory['name']); + $this->assertEquals('Its a description of Test Category 1.2', $firstChildCategory['description']); + + $firstChildCategoryExpectedProducts = [ + ['sku' => 'simple', 'name' => 'Simple Product'], + ['sku' => 'simple-4', 'name' => 'Simple Product Three'] + ]; + $this->assertCategoryProducts($firstChildCategory, $firstChildCategoryExpectedProducts); + $firstChildCategoryChildren = []; + $this->assertCategoryChildren($firstChildCategory, $firstChildCategoryChildren); + } + /** * @magentoApiDataFixture Magento/Catalog/_files/categories.php */ diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductCanonicalUrlTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductCanonicalUrlTest.php new file mode 100644 index 0000000000000..308e159b0dd77 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductCanonicalUrlTest.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Catalog; + +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test for getting canonical_url for products + */ +class ProductCanonicalUrlTest extends GraphQlAbstract +{ + /** + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoConfigFixture default_store catalog/seo/product_canonical_tag 1 + * + */ + public function testProductWithCanonicalLinksMetaTagSettingsEnabled() + { + $productSku = 'simple'; + $query + = <<<QUERY +{ + products (filter: {sku: {eq: "{$productSku}"}}) { + items { + name + sku + canonical_url + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertNotEmpty($response['products']['items']); + + $this->assertEquals( + 'simple-product.html', + $response['products']['items'][0]['canonical_url'] + ); + $this->assertEquals('simple', $response['products']['items'][0]['sku']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoConfigFixture default_store catalog/seo/product_canonical_tag 0 + */ + public function testProductWithCanonicalLinksMetaTagSettingsDisabled() + { + $productSku = 'simple'; + $query + = <<<QUERY +{ + products (filter: {sku: {eq: "{$productSku}"}}) { + items { + name + sku + canonical_url + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertNull( + $response['products']['items'][0]['canonical_url'] + ); + $this->assertEquals('simple', $response['products']['items'][0]['sku']); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductFragmentTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductFragmentTest.php new file mode 100644 index 0000000000000..32a2f8f763572 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductFragmentTest.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Catalog; + +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test for simple product fragment. + */ +class ProductFragmentTest extends GraphQlAbstract +{ + /** + * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + */ + public function testSimpleProductFragment() + { + $sku = 'simple'; + $name = 'Simple Product'; + $price = 10; + + $query = <<<QUERY +query GetProduct { + products(filter: { sku: { eq: "$sku" } }) { + items { + sku + ...BasicProductInformation + } + } +} + +fragment BasicProductInformation on ProductInterface { + sku + name + price { + regularPrice { + amount { + value + } + } + } +} +QUERY; + $result = $this->graphQlQuery($query); + $actualProductData = $result['products']['items'][0]; + $this->assertNotEmpty($actualProductData); + $this->assertEquals($name, $actualProductData['name']); + $this->assertEquals($price, $actualProductData['price']['regularPrice']['amount']['value']); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchAggregationsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchAggregationsTest.php new file mode 100644 index 0000000000000..f647dc74ea55f --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchAggregationsTest.php @@ -0,0 +1,81 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Catalog; + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +class ProductSearchAggregationsTest extends GraphQlAbstract +{ + /** + * @magentoApiDataFixture Magento/Catalog/_files/products_with_boolean_attribute.php + */ + public function testAggregationBooleanAttribute() + { + $this->reindex(); + + $skus= '"search_product_1", "search_product_2", "search_product_3", "search_product_4" ,"search_product_5"'; + $query = <<<QUERY +{ + products(filter: {sku: {in: [{$skus}]}}){ + items{ + id + sku + name + } + aggregations{ + label + attribute_code + count + options{ + label + value + count + } + } + } +} +QUERY; + + $result = $this->graphQlQuery($query); + + $this->assertArrayNotHasKey('errors', $result); + $this->assertArrayHasKey('items', $result['products']); + $this->assertCount(5, $result['products']['items']); + $this->assertArrayHasKey('aggregations', $result['products']); + + $booleanAggregation = array_filter( + $result['products']['aggregations'], + function ($a) { + return $a['attribute_code'] == 'boolean_attribute'; + } + ); + $this->assertNotEmpty($booleanAggregation); + $booleanAggregation = reset($booleanAggregation); + $this->assertEquals('Boolean Attribute', $booleanAggregation['label']); + $this->assertEquals('boolean_attribute', $booleanAggregation['attribute_code']); + $this->assertContains(['label' => '1', 'value'=> '1', 'count' => '3'], $booleanAggregation['options']); + + $this->markTestIncomplete('MC-22184: Elasticsearch returns incorrect aggregation options for booleans'); + $this->assertEquals(2, $booleanAggregation['count']); + $this->assertCount(2, $booleanAggregation['options']); + $this->assertContains(['label' => '0', 'value'=> '0', 'count' => '2'], $booleanAggregation['options']); + } + + /** + * Reindex + * + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function reindex() + { + $appDir = dirname(Bootstrap::getInstance()->getAppTempDir()); + // phpcs:ignore Magento2.Security.InsecureFunction + exec("php -f {$appDir}/bin/magento indexer:reindex"); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php index e1615eb9a667e..9ee3b3baa5fc2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php @@ -31,6 +31,37 @@ */ class ProductSearchTest extends GraphQlAbstract { + /** + * Verify that filters for non-existing category are empty + * + * @throws \Exception + */ + public function testFilterForNonExistingCategory() + { + $query = <<<QUERY +{ + products(filter: {category_id: {eq: "99999999"}}) { + filters { + name + } + } +} +QUERY; + + $response = $this->graphQlQuery($query); + + $this->assertArrayHasKey( + 'filters', + $response['products'], + 'Filters are missing in product query result.' + ); + + $this->assertEmpty( + $response['products']['filters'], + 'Returned filters data set does not empty' + ); + } + /** * Verify that layered navigation filters and aggregations are correct for product query * @@ -41,6 +72,7 @@ class ProductSearchTest extends GraphQlAbstract */ public function testFilterLn() { + $this->reIndexAndCleanCache(); $query = <<<QUERY { products ( diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php index dae71c1767caf..9d6a5e6d414e0 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php @@ -13,7 +13,6 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Category; use Magento\Framework\DataObject; -use Magento\Framework\EntityManager\MetadataPool; use Magento\TestFramework\ObjectManager; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -270,11 +269,6 @@ public function testQueryAllFieldsSimpleProduct() /** @var ProductRepositoryInterface $productRepository */ $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); $product = $productRepository->get($productSku, false, null, true); - /** @var MetadataPool $metadataPool */ - $metadataPool = ObjectManager::getInstance()->get(MetadataPool::class); - $product->setId( - $product->getData($metadataPool->getMetadata(ProductInterface::class)->getLinkField()) - ); $this->assertArrayHasKey('products', $response); $this->assertArrayHasKey('items', $response['products']); $this->assertEquals(1, count($response['products']['items'])); @@ -292,11 +286,8 @@ public function testQueryAllFieldsSimpleProduct() 'Filter category', $responseObject->getData('products/items/0/categories/1/name') ); - $storeManager = ObjectManager::getInstance()->get(\Magento\Store\Model\StoreManagerInterface::class); - self::assertEquals( - $storeManager->getStore()->getBaseUrl() . 'simple-product.html', - $responseObject->getData('products/items/0/canonical_url') - ); + //canonical_url will be null unless the admin setting catalog/seo/product_canonical_tag is turned ON + self::assertNull($responseObject->getData('products/items/0/canonical_url')); } /** @@ -659,15 +650,7 @@ public function testProductPrices() */ $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); $firstProduct = $productRepository->get($firstProductSku, false, null, true); - /** @var MetadataPool $metadataPool */ - $metadataPool = ObjectManager::getInstance()->get(MetadataPool::class); - $firstProduct->setId( - $firstProduct->getData($metadataPool->getMetadata(ProductInterface::class)->getLinkField()) - ); $secondProduct = $productRepository->get($secondProductSku, false, null, true); - $secondProduct->setId( - $secondProduct->getData($metadataPool->getMetadata(ProductInterface::class)->getLinkField()) - ); self::assertNotNull($response['products']['items'][0]['price'], "price must be not null"); self::assertCount(2, $response['products']['items']); $this->assertBaseFields($firstProduct, $response['products']['items'][0]); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php index 43796d780646c..0d808c6dd0696 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php @@ -12,6 +12,8 @@ use Magento\TestFramework\TestCase\GraphQlAbstract; use Magento\UrlRewrite\Model\UrlFinderInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite as UrlRewriteDTO; +use Magento\Eav\Model\Config as EavConfig; +use Magento\Store\Model\StoreManagerInterface; /** * Test of getting URL rewrites data from products @@ -54,9 +56,22 @@ public function testProductWithNoCategoriesAssigned() $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); $product = $productRepository->get('virtual-product', false, null, true); + $storeId = ObjectManager::getInstance()->get(StoreManagerInterface::class)->getStore()->getId(); $urlFinder = ObjectManager::getInstance()->get(UrlFinderInterface::class); + $entityType = ObjectManager::getInstance()->create(EavConfig::class)->getEntityType('catalog_product'); - $rewritesCollection = $urlFinder->findAllByData([UrlRewriteDTO::ENTITY_ID => $product->getId()]); + $entityTypeCode = $entityType->getEntityTypeCode(); + if ($entityTypeCode === 'catalog_product') { + $entityTypeCode = 'product'; + } + + $rewritesCollection = $urlFinder->findAllByData( + [ + UrlRewriteDTO::ENTITY_ID => $product->getId(), + UrlRewriteDTO::ENTITY_TYPE => $entityTypeCode, + UrlRewriteDTO::STORE_ID => $storeId + ] + ); /* There should be only one rewrite */ /** @var UrlRewriteDTO $urlRewrite */ @@ -110,18 +125,32 @@ public function testProductWithOneCategoryAssigned() $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); $product = $productRepository->get('simple', false, null, true); + $storeId = ObjectManager::getInstance()->get(StoreManagerInterface::class)->getStore()->getId(); $urlFinder = ObjectManager::getInstance()->get(UrlFinderInterface::class); + $entityType = ObjectManager::getInstance()->create(EavConfig::class)->getEntityType('catalog_product'); - $rewritesCollection = $urlFinder->findAllByData([UrlRewriteDTO::ENTITY_ID => $product->getId()]); - $rewritesCount = count($rewritesCollection); + $entityTypeCode = $entityType->getEntityTypeCode(); + if ($entityTypeCode === 'catalog_product') { + $entityTypeCode = 'product'; + } + $rewritesCollection = $urlFinder->findAllByData( + [ + UrlRewriteDTO::ENTITY_ID => $product->getId(), + UrlRewriteDTO::ENTITY_TYPE => $entityTypeCode, + UrlRewriteDTO::STORE_ID => $storeId + ] + ); + + $rewritesCount = count($rewritesCollection); $this->assertArrayHasKey('url_rewrites', $response['products']['items'][0]); + $this->assertCount(1, $response['products']['items'][0]['url_rewrites']); $this->assertCount($rewritesCount, $response['products']['items'][0]['url_rewrites']); - for ($i = 0; $i < $rewritesCount; $i++) { - $urlRewrite = $rewritesCollection[$i]; + for ($index = 0; $index < $rewritesCount; $index++) { + $urlRewrite = $rewritesCollection[$index]; $this->assertResponseFields( - $response['products']['items'][0]['url_rewrites'][$i], + $response['products']['items'][0]['url_rewrites'][$index], [ "url" => $urlRewrite->getRequestPath(), "parameters" => $this->getUrlParameters($urlRewrite->getTargetPath()) @@ -140,11 +169,12 @@ private function getUrlParameters(string $targetPath): array { $urlParameters = []; $targetPathParts = explode('/', trim($targetPath, '/')); - - for ($i = 3; ($i < sizeof($targetPathParts) - 1); $i += 2) { + $count = count($targetPathParts) - 1; + //phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall + for ($index = 3; $index < $count; $index += 2) { $urlParameters[] = [ - 'name' => $targetPathParts[$i], - 'value' => $targetPathParts[$i + 1] + 'name' => $targetPathParts[$index], + 'value' => $targetPathParts[$index + 1] ]; } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/VirtualProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/VirtualProductViewTest.php index 58b6d4f0e4ea2..80206b232585f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/VirtualProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/VirtualProductViewTest.php @@ -9,7 +9,6 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Framework\EntityManager\MetadataPool; use Magento\TestFramework\ObjectManager; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -57,11 +56,6 @@ public function testQueryAllFieldsVirtualProduct() /** @var ProductRepositoryInterface $productRepository */ $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class); $product = $productRepository->get($productSku, false, null, true); - /** @var MetadataPool $metadataPool */ - $metadataPool = ObjectManager::getInstance()->get(MetadataPool::class); - $product->setId( - $product->getData($metadataPool->getMetadata(ProductInterface::class)->getLinkField()) - ); $this->assertArrayHasKey('products', $response); $this->assertArrayHasKey('items', $response['products']); $this->assertEquals(1, count($response['products']['items'])); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartTest.php index b1858e843bf0f..8e6400a9a3b93 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/AddConfigurableProductToCartTest.php @@ -199,7 +199,7 @@ public function testAddVariationFromAnotherConfigurableProductWithDifferentSuper /** * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable_sku.php * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php - * @expectedException \Exception + * @expectedException Exception * @expectedExceptionMessage The requested qty is not available */ public function testAddProductIfQuantityIsNotAvailable() @@ -224,7 +224,7 @@ public function testAddProductIfQuantityIsNotAvailable() /** * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable_sku.php * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php - * @expectedException \Exception + * @expectedException Exception * @expectedExceptionMessage Could not find a product with SKU "configurable_no_exist" */ public function testAddNonExistentConfigurableProductParentToCart() @@ -263,7 +263,7 @@ public function testAddNonExistentConfigurableProductVariationToCart() 2000 ); - $this->expectException(\Exception::class); + $this->expectException(Exception::class); $this->expectExceptionMessage( 'Could not add the product with SKU configurable to the shopping cart: The product that was requested ' . 'doesn\'t exist. Verify the product and try again.' @@ -272,6 +272,58 @@ public function testAddNonExistentConfigurableProductVariationToCart() $this->graphQlMutation($query); } + /** + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable_disable_first_child.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + */ + public function testAddDisabledVariationToCart() + { + $searchResponse = $this->graphQlQuery($this->getFetchProductQuery('configurable')); + $product = current($searchResponse['products']['items']); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + $parentSku = $product['sku']; + $sku = 'simple_10'; + $query = $this->getQuery( + $maskedQuoteId, + $parentSku, + $sku, + 1 + ); + + $this->expectException(Exception::class); + $this->expectExceptionMessage( + 'Could not add the product with SKU configurable to the shopping cart' + ); + + $this->graphQlMutation($query); + } + + /** + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable_zero_qty_first_child.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + */ + public function testOutOfStockVariationToCart() + { + $searchResponse = $this->graphQlQuery($this->getFetchProductQuery('configurable')); + $product = current($searchResponse['products']['items']); + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1'); + $parentSku = $product['sku']; + $sku = 'simple_10'; + $query = $this->getQuery( + $maskedQuoteId, + $parentSku, + $sku, + 1 + ); + + $this->expectException(Exception::class); + $this->expectExceptionMessage( + 'Could not add the product with SKU configurable to the shopping cart' + ); + + $this->graphQlMutation($query); + } + /** * @param string $maskedQuoteId * @param string $parentSku diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/ConfigurableProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/ConfigurableProductViewTest.php index 4729cae92717d..4837e2c6ec98a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/ConfigurableProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/ConfigurableProduct/ConfigurableProductViewTest.php @@ -243,7 +243,7 @@ private function assertBaseFields($product, $actualResponse) 'expected_value' => $product->getData( $metadataPool->getMetadata( ProductInterface::class - )->getLinkField() + )->getIdentifierField() ) ], ['response_field' => 'name', 'expected_value' => $product->getName()], diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ChangeCustomerPasswordTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ChangeCustomerPasswordTest.php index f36200c8e9218..bf01ad4b37218 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ChangeCustomerPasswordTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ChangeCustomerPasswordTest.php @@ -48,7 +48,7 @@ class ChangeCustomerPasswordTest extends GraphQlAbstract */ private $customerRepository; - protected function setUp() + protected function setUp(): void { $this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class); $this->accountManagement = Bootstrap::getObjectManager()->get(AccountManagementInterface::class); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php index 15da8443b1787..81300e967f6a2 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerAddressTest.php @@ -29,7 +29,7 @@ class CreateCustomerAddressTest extends GraphQlAbstract */ private $addressRepository; - protected function setUp() + protected function setUp(): void { parent::setUp(); @@ -49,7 +49,7 @@ public function testCreateCustomerAddress() 'region_id' => 4, 'region_code' => 'AZ' ], - 'country_id' => 'US', + 'country_code' => 'US', 'street' => ['Line 1 Street', 'Line 2'], 'company' => 'Company name', 'telephone' => '123456789', @@ -75,7 +75,7 @@ public function testCreateCustomerAddress() region_id: {$newAddress['region']['region_id']} region_code: "{$newAddress['region']['region_code']}" } - country_id: {$newAddress['country_id']} + country_code: {$newAddress['country_code']} street: ["{$newAddress['street'][0]}","{$newAddress['street'][1]}"] company: "{$newAddress['company']}" telephone: "{$newAddress['telephone']}" @@ -98,7 +98,7 @@ public function testCreateCustomerAddress() region_id region_code } - country_id + country_code street company telephone @@ -133,6 +133,75 @@ public function testCreateCustomerAddress() $this->assertCustomerAddressesFields($address, $newAddress); } + /** + * Test case for deprecated `country_id` field. + * + * @magentoApiDataFixture Magento/Customer/_files/customer_without_addresses.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testCreateCustomerAddressWithCountryId() + { + $newAddress = [ + 'region' => [ + 'region' => 'Arizona', + 'region_id' => 4, + 'region_code' => 'AZ' + ], + 'country_id' => 'US', + 'street' => ['Line 1 Street', 'Line 2'], + 'company' => 'Company name', + 'telephone' => '123456789', + 'fax' => '123123123', + 'postcode' => '7777', + 'city' => 'City Name', + 'firstname' => 'Adam', + 'lastname' => 'Phillis', + 'middlename' => 'A', + 'prefix' => 'Mr.', + 'suffix' => 'Jr.', + 'vat_id' => '1', + 'default_shipping' => true, + 'default_billing' => false + ]; + + $mutation + = <<<MUTATION +mutation { + createCustomerAddress(input: { + region: { + region: "{$newAddress['region']['region']}" + region_id: {$newAddress['region']['region_id']} + region_code: "{$newAddress['region']['region_code']}" + } + country_id: {$newAddress['country_id']} + street: ["{$newAddress['street'][0]}","{$newAddress['street'][1]}"] + company: "{$newAddress['company']}" + telephone: "{$newAddress['telephone']}" + fax: "{$newAddress['fax']}" + postcode: "{$newAddress['postcode']}" + city: "{$newAddress['city']}" + firstname: "{$newAddress['firstname']}" + lastname: "{$newAddress['lastname']}" + middlename: "{$newAddress['middlename']}" + prefix: "{$newAddress['prefix']}" + suffix: "{$newAddress['suffix']}" + vat_id: "{$newAddress['vat_id']}" + default_shipping: true + default_billing: false + }) { + country_id + } +} +MUTATION; + + $userName = 'customer@example.com'; + $password = 'password'; + + $response = $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); + $this->assertArrayHasKey('createCustomerAddress', $response); + $this->assertEquals($newAddress['country_id'], $response['createCustomerAddress']['country_id']); + } + /** * @expectedException Exception * @expectedExceptionMessage The current customer isn't authorized. @@ -153,7 +222,7 @@ public function testCreateCustomerAddressIfUserIsNotAuthorized() region: { region_id: 1 } - country_id: US + country_code: US postcode: "9999" default_shipping: true default_billing: false @@ -182,7 +251,7 @@ public function testCreateCustomerAddressWithMissingAttribute() region: { region_id: 1 } - country_id: US + country_code: US street: ["Line 1 Street","Line 2"] company: "Company name" telephone: "123456789" @@ -235,7 +304,7 @@ public function testCreateCustomerAddressWithRedundantStreetLine() 'region_id' => 4, 'region_code' => 'AZ' ], - 'country_id' => 'US', + 'country_code' => 'US', 'street' => ['Line 1 Street', 'Line 2', 'Line 3'], 'company' => 'Company name', 'telephone' => '123456789', @@ -261,7 +330,7 @@ public function testCreateCustomerAddressWithRedundantStreetLine() region_id: {$newAddress['region']['region_id']} region_code: "{$newAddress['region']['region_code']}" } - country_id: {$newAddress['country_id']} + country_code: {$newAddress['country_code']} street: ["{$newAddress['street'][0]}","{$newAddress['street'][1]}","{$newAddress['street'][2]}"] company: "{$newAddress['company']}" telephone: "{$newAddress['telephone']}" @@ -289,6 +358,67 @@ public function testCreateCustomerAddressWithRedundantStreetLine() $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer_without_addresses.php + * @magentoConfigFixture default_store general/country/optional_zip_countries UA + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testCreateCustomerAddressWithOptionalZipCode() + { + $newAddress = [ + 'country_code' => 'UA', + 'street' => ['Line 1 Street', 'Line 2'], + 'company' => 'Company name', + 'telephone' => '123456789', + 'fax' => '123123123', + 'city' => 'City Name', + 'firstname' => 'Adam', + 'lastname' => 'Phillis', + 'middlename' => 'A', + 'prefix' => 'Mr.', + 'suffix' => 'Jr.', + 'vat_id' => '1', + 'default_shipping' => true, + 'default_billing' => false + ]; + + $mutation + = <<<MUTATION +mutation { + createCustomerAddress(input: { + country_code: {$newAddress['country_code']} + street: ["{$newAddress['street'][0]}","{$newAddress['street'][1]}"] + company: "{$newAddress['company']}" + telephone: "{$newAddress['telephone']}" + fax: "{$newAddress['fax']}" + city: "{$newAddress['city']}" + firstname: "{$newAddress['firstname']}" + lastname: "{$newAddress['lastname']}" + middlename: "{$newAddress['middlename']}" + prefix: "{$newAddress['prefix']}" + suffix: "{$newAddress['suffix']}" + vat_id: "{$newAddress['vat_id']}" + default_shipping: true + default_billing: false + }) { + id + } +} +MUTATION; + + $userName = 'customer@example.com'; + $password = 'password'; + + $response = $this->graphQlMutation( + $mutation, + [], + '', + $this->getCustomerAuthHeaders($userName, $password) + ); + $this->assertNotEmpty($response['createCustomerAddress']['id']); + } + /** * Create new address with invalid input * @@ -334,12 +464,15 @@ public function invalidInputDataProvider() * * @param AddressInterface $address * @param array $actualResponse + * @param string $countryFieldName */ - private function assertCustomerAddressesFields(AddressInterface $address, array $actualResponse): void - { + private function assertCustomerAddressesFields( + AddressInterface $address, + array $actualResponse + ): void { /** @var $addresses */ $assertionMap = [ - ['response_field' => 'country_id', 'expected_value' => $address->getCountryId()], + ['response_field' => 'country_code', 'expected_value' => $address->getCountryId()], ['response_field' => 'street', 'expected_value' => $address->getStreet()], ['response_field' => 'company', 'expected_value' => $address->getCompany()], ['response_field' => 'telephone', 'expected_value' => $address->getTelephone()], diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php index 0be968d6d340d..3da51088f0af6 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CreateCustomerTest.php @@ -13,7 +13,7 @@ use Magento\TestFramework\TestCase\GraphQlAbstract; /** - * Test for create customer functionallity + * Test for create customer functionality */ class CreateCustomerTest extends GraphQlAbstract { @@ -27,7 +27,7 @@ class CreateCustomerTest extends GraphQlAbstract */ private $customerRepository; - protected function setUp() + protected function setUp(): void { parent::setUp(); @@ -114,7 +114,7 @@ public function testCreateCustomerAccountWithoutPassword() /** * @expectedException \Exception - * @expectedExceptionMessage Field CustomerInput.email of required type String! was not provided + * @expectedExceptionMessage "input" value should be specified */ public function testCreateCustomerIfInputDataIsEmpty() { @@ -140,7 +140,7 @@ public function testCreateCustomerIfInputDataIsEmpty() /** * @expectedException \Exception - * @expectedExceptionMessage Field CustomerInput.email of required type String! was not provided + * @expectedExceptionMessage Required parameters are missing: Email */ public function testCreateCustomerIfEmailMissed() { @@ -172,24 +172,25 @@ public function testCreateCustomerIfEmailMissed() } /** - * @expectedException \Exception - * @expectedExceptionMessage "Email" is not a valid email address. + * @dataProvider invalidEmailAddressDataProvider + * + * @param string $email + * @throws \Exception */ - public function testCreateCustomerIfEmailIsNotValid() + public function testCreateCustomerIfEmailIsNotValid(string $email) { - $newFirstname = 'Richard'; - $newLastname = 'Rowe'; - $currentPassword = 'test123#'; - $newEmail = 'email'; + $firstname = 'Richard'; + $lastname = 'Rowe'; + $password = 'test123#'; $query = <<<QUERY mutation { createCustomer( input: { - firstname: "{$newFirstname}" - lastname: "{$newLastname}" - email: "{$newEmail}" - password: "{$currentPassword}" + firstname: "{$firstname}" + lastname: "{$lastname}" + email: "{$email}" + password: "{$password}" is_subscribed: true } ) { @@ -203,9 +204,29 @@ public function testCreateCustomerIfEmailIsNotValid() } } QUERY; + $this->expectExceptionMessage('"' . $email . '" is not a valid email address.'); $this->graphQlMutation($query); } + /** + * @return array + */ + public function invalidEmailAddressDataProvider(): array + { + return [ + ['plainaddress'], + ['jØrgen@somedomain.com'], + ['#@%^%#$@#$@#.com'], + ['@example.com'], + ['Joe Smith <email@example.com>'], + ['email.example.com'], + ['email@example@example.com'], + ['email@example.com (Joe Smith)'], + ['email@example'], + ['“email”@example.com'], + ]; + } + /** * @expectedException \Exception * @expectedExceptionMessage Field "test123" is not defined by type CustomerInput. @@ -308,7 +329,40 @@ public function testCreateCustomerSubscribed() $this->assertEquals(false, $response['createCustomer']['customer']['is_subscribed']); } - public function tearDown() + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @expectedException \Exception + * @expectedExceptionMessage A customer with the same email address already exists in an associated website. + */ + public function testCreateCustomerIfCustomerWithProvidedEmailAlreadyExists() + { + $existedEmail = 'customer@example.com'; + $password = 'test123#'; + $firstname = 'John'; + $lastname = 'Smith'; + + $query = <<<QUERY +mutation { + createCustomer( + input: { + email: "{$existedEmail}" + password: "{$password}" + firstname: "{$firstname}" + lastname: "{$lastname}" + } + ) { + customer { + firstname + lastname + email + } + } +} +QUERY; + $this->graphQlMutation($query); + } + + public function tearDown(): void { $newEmail = 'new_customer@example.com'; try { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php index 443b9d7ec53e5..31065f3f6f98b 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/DeleteCustomerAddressTest.php @@ -39,7 +39,7 @@ class DeleteCustomerAddressTest extends GraphQlAbstract */ private $lockCustomer; - protected function setUp() + protected function setUp(): void { parent::setUp(); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/GetAddressesTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/GetAddressesTest.php index c1573d7dbd8af..ed360919d8320 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/GetAddressesTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/GetAddressesTest.php @@ -31,7 +31,7 @@ class GetAddressesTest extends GraphQlAbstract */ private $lockCustomer; - protected function setUp() + protected function setUp(): void { parent::setUp(); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/GetCustomerTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/GetCustomerTest.php index 56e950c45fae0..c645d8953981a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/GetCustomerTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/GetCustomerTest.php @@ -36,7 +36,7 @@ class GetCustomerTest extends GraphQlAbstract */ private $customerRepository; - protected function setUp() + protected function setUp(): void { parent::setUp(); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/SubscriptionStatusTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/SubscriptionStatusTest.php index bbb7e245c91fd..4cba8323793dc 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/SubscriptionStatusTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/SubscriptionStatusTest.php @@ -29,7 +29,7 @@ class SubscriptionStatusTest extends GraphQlAbstract */ private $subscriberFactory; - protected function setUp() + protected function setUp(): void { parent::setUp(); @@ -162,7 +162,7 @@ private function getHeaderMap(string $email, string $password): array return ['Authorization' => 'Bearer ' . $customerToken]; } - protected function tearDown() + protected function tearDown(): void { parent::tearDown(); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php index 625d027f58d24..da67900994940 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerAddressTest.php @@ -40,7 +40,7 @@ class UpdateCustomerAddressTest extends GraphQlAbstract */ private $lockCustomer; - protected function setUp() + protected function setUp(): void { parent::setUp(); @@ -76,6 +76,56 @@ public function testUpdateCustomerAddress() $this->assertCustomerAddressesFields($address, $updateAddress); } + /** + * Test case for deprecated `country_id` field. + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_address.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testUpdateCustomerAddressWithCountryId() + { + $userName = 'customer@example.com'; + $password = 'password'; + $addressId = 1; + + $updateAddress = $this->getAddressData(); + + $mutation = $mutation + = <<<MUTATION +mutation { + updateCustomerAddress(id: {$addressId}, input: { + region: { + region: "{$updateAddress['region']['region']}" + region_id: {$updateAddress['region']['region_id']} + region_code: "{$updateAddress['region']['region_code']}" + } + country_id: {$updateAddress['country_code']} + street: ["{$updateAddress['street'][0]}","{$updateAddress['street'][1]}"] + company: "{$updateAddress['company']}" + telephone: "{$updateAddress['telephone']}" + fax: "{$updateAddress['fax']}" + postcode: "{$updateAddress['postcode']}" + city: "{$updateAddress['city']}" + firstname: "{$updateAddress['firstname']}" + lastname: "{$updateAddress['lastname']}" + middlename: "{$updateAddress['middlename']}" + prefix: "{$updateAddress['prefix']}" + suffix: "{$updateAddress['suffix']}" + vat_id: "{$updateAddress['vat_id']}" + default_shipping: true + default_billing: true + }) { + country_id + } +} +MUTATION; + + $response = $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); + $this->assertArrayHasKey('updateCustomerAddress', $response); + $this->assertEquals($updateAddress['country_code'], $response['updateCustomerAddress']['country_id']); + } + /** * @expectedException Exception * @expectedExceptionMessage The current customer isn't authorized. @@ -126,17 +176,63 @@ public function testUpdateCustomerAddressWithMissingAttribute() $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); } + /** + * Test custom attributes of the customer's address + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_address.php + * @magentoApiDataFixture Magento/Customer/_files/attribute_user_defined_address_custom_attribute.php + */ + public function testUpdateCustomerAddressHasCustomAttributes() + { + $userName = 'customer@example.com'; + $password = 'password'; + $addressId = 1; + $attributes = [ + [ + 'attribute_code' => 'custom_attribute1', + 'value'=> '[new-value1,new-value2]' + ], + [ + 'attribute_code' => 'custom_attribute2', + 'value'=> '"new-value3"' + ] + ]; + $attributesFragment = preg_replace('/"([^"]+)"\s*:\s*/', '$1:', json_encode($attributes)); + $mutation + = <<<MUTATION +mutation { + updateCustomerAddress( + id: {$addressId} + input: { + custom_attributes: {$attributesFragment} + } + ) { + custom_attributes { + attribute_code + value + } + } +} +MUTATION; + + $response = $this->graphQlMutation($mutation, [], '', $this->getCustomerAuthHeaders($userName, $password)); + $this->assertEquals($attributes, $response['updateCustomerAddress']['custom_attributes']); + } + /** * Verify the fields for Customer address * * @param AddressInterface $address * @param array $actualResponse + * @param string $countryFieldName */ - private function assertCustomerAddressesFields(AddressInterface $address, $actualResponse): void - { + private function assertCustomerAddressesFields( + AddressInterface $address, + $actualResponse + ): void { /** @var $addresses */ $assertionMap = [ - ['response_field' => 'country_id', 'expected_value' => $address->getCountryId()], + ['response_field' => 'country_code', 'expected_value' => $address->getCountryId()], ['response_field' => 'street', 'expected_value' => $address->getStreet()], ['response_field' => 'company', 'expected_value' => $address->getCompany()], ['response_field' => 'telephone', 'expected_value' => $address->getTelephone()], @@ -187,7 +283,7 @@ public function testUpdateCustomerAddressWithMissingId() region_id: {$updateAddress['region']['region_id']} region_code: "{$updateAddress['region']['region_code']}" } - country_id: {$updateAddress['country_id']} + country_code: {$updateAddress['country_code']} street: ["{$updateAddress['street'][0]}","{$updateAddress['street'][1]}"] company: "{$updateAddress['company']}" telephone: "{$updateAddress['telephone']}" @@ -243,7 +339,7 @@ public function testUpdateCustomerAddressWithInvalidIdType() region_id: {$updateAddress['region']['region_id']} region_code: "{$updateAddress['region']['region_code']}" } - country_id: {$updateAddress['country_id']} + country_code: {$updateAddress['country_code']} street: ["{$updateAddress['street'][0]}","{$updateAddress['street'][1]}"] company: "{$updateAddress['company']}" telephone: "{$updateAddress['telephone']}" @@ -382,11 +478,11 @@ private function getAddressData(): array { return [ 'region' => [ - 'region' => 'Alaska', - 'region_id' => 2, - 'region_code' => 'AK' + 'region' => 'Alberta', + 'region_id' => 66, + 'region_code' => 'AB' ], - 'country_id' => 'US', + 'country_code' => 'CA', 'street' => ['Line 1 Street', 'Line 2'], 'company' => 'Company Name', 'telephone' => '123456789', @@ -423,7 +519,7 @@ private function getMutation(int $addressId): string region_id: {$updateAddress['region']['region_id']} region_code: "{$updateAddress['region']['region_code']}" } - country_id: {$updateAddress['country_id']} + country_code: {$updateAddress['country_code']} street: ["{$updateAddress['street'][0]}","{$updateAddress['street'][1]}"] company: "{$updateAddress['company']}" telephone: "{$updateAddress['telephone']}" @@ -446,7 +542,7 @@ private function getMutation(int $addressId): string region_id region_code } - country_id + country_code street company telephone diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php index d1c6638e8d5ff..7121f12bc2a42 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php @@ -33,7 +33,7 @@ class UpdateCustomerTest extends GraphQlAbstract */ private $lockCustomer; - protected function setUp() + protected function setUp(): void { parent::setUp(); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/RequiredInputArgumentTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/RequiredInputArgumentTest.php new file mode 100644 index 0000000000000..9fecc954d1182 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Framework/RequiredInputArgumentTest.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Framework; + +use Magento\TestFramework\TestCase\GraphQlAbstract; +use Magento\TestFramework\TestCase\GraphQl\ResponseContainsErrorsException; + +/** + * Test that required input parameters are properly validated on framework level + */ +class RequiredInputArgumentTest extends GraphQlAbstract +{ + + /** + * Test that a simple input value will be treated as required + * + * We should see error message from framework not the Resolver + * urlResolver query has required input arg "url" + */ + public function testSimpleInputArgumentRequired() + { + $query = <<<QUERY + { + urlResolver{ + id + type + } + } +QUERY; + + $expectedExceptionsMessage = 'GraphQL response contains errors:' + . ' Field "urlResolver" argument "url" of type "String!" is required but not provided.'; + $this->expectException(ResponseContainsErrorsException::class); + $this->expectExceptionMessage($expectedExceptionsMessage); + + $this->graphQlQuery($query); + } + + /** + * Test that a more complex required argument is handled properly + * + * updateCartItems mutation has required parameter input.cart_items.cart_item_id + */ + public function testInputObjectArgumentRequired() + { + $query = <<<QUERY + mutation { + updateCartItems( + input: { + cart_id: "foobar" + cart_items: [ + { + quantity: 2 + } + ] + } + ) { + cart { + total_quantity + } + } + } +QUERY; + + $expectedExceptionsMessage = 'GraphQL response contains errors:' + . ' Field CartItemUpdateInput.cart_item_id of required type Int! was not provided.'; + $this->expectException(ResponseContainsErrorsException::class); + $this->expectExceptionMessage($expectedExceptionsMessage); + + $this->graphQlMutation($query); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/IntrospectionQueryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/IntrospectionQueryTest.php index 69bcc73dd27a1..0c22bea2a42f8 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/IntrospectionQueryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/IntrospectionQueryTest.php @@ -56,6 +56,124 @@ public function testIntrospectionQuery() $this->assertArrayHasKey('__schema', $this->graphQlQuery($query)); } + /** + * Tests that Introspection is allowed by default + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testIntrospectionQueryWithOnlySchema() + { + $query + = <<<QUERY + { + __schema { + queryType { name } + types{ + ...FullType + } + } + } +fragment FullType on __Type{ + name + kind + fields(includeDeprecated:true){ + name + args{ + ...InputValue + } + } + } + +fragment TypeRef on __Type { + kind + name + ofType{ + kind + name + } +} +fragment InputValue on __InputValue { + name + description + type { ...TypeRef } + defaultValue +} +QUERY; + $this->assertArrayHasKey('__schema', $this->graphQlQuery($query)); + $response = $this->graphQlQuery($query); + + $query + = <<<QUERY +query IntrospectionQuery { + __schema { + queryType { name } + types{ + ...FullType + } + } + } +fragment FullType on __Type{ + name + kind + fields(includeDeprecated:true){ + name + args{ + ...InputValue + } + } + } + +fragment TypeRef on __Type { + kind + name + ofType{ + kind + name + } +} +fragment InputValue on __InputValue { + name + description + type { ...TypeRef } + defaultValue +} +QUERY; + $this->assertArrayHasKey('__schema', $this->graphQlQuery($query)); + $responseFields = $this->graphQlQuery($query); + $this->assertResponseFields($response, $responseFields); + $this->assertEquals($responseFields, $response); + } + + /** + * Tests that Introspection is allowed by default + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testIntrospectionQueryWithOnlyType() + { + $query + = <<<QUERY +{ + __type(name:"Query") + { + name + kind + fields(includeDeprecated:true){ + name + type{ + kind + name + } + description + isDeprecated + deprecationReason + } + } +} +QUERY; + $this->assertArrayHasKey('__type', $this->graphQlQuery($query)); + $response = $this->graphQlQuery($query); + $this->assertNotEmpty($response['__type']['fields']); + } + /** * Tests that Introspection Query with deprecated annotations on enum values, fields are read. */ diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddSimpleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddSimpleProductToCartTest.php index e1b93e0bdb857..aca98e946054c 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddSimpleProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddSimpleProductToCartTest.php @@ -29,7 +29,7 @@ class AddSimpleProductToCartTest extends GraphQlAbstract */ private $getMaskedQuoteIdByReservedOrderId; - protected function setUp() + protected function setUp(): void { $objectManager = Bootstrap::getObjectManager(); $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); @@ -50,34 +50,36 @@ public function testAddSimpleProductToCart() $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('cart', $response['addSimpleProductsToCart']); + self::assertArrayHasKey('shipping_addresses', $response['addSimpleProductsToCart']['cart']); + self::assertEmpty($response['addSimpleProductsToCart']['cart']['shipping_addresses']); self::assertEquals($quantity, $response['addSimpleProductsToCart']['cart']['items'][0]['quantity']); self::assertEquals($sku, $response['addSimpleProductsToCart']['cart']['items'][0]['product']['sku']); - } + self::assertArrayHasKey('prices', $response['addSimpleProductsToCart']['cart']['items'][0]); - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @expectedException Exception - * @expectedExceptionMessage Required parameter "cart_id" is missing - */ - public function testAddSimpleProductToCartIfCartIdIsMissed() - { - $query = <<<QUERY -mutation { - addSimpleProductsToCart( - input: { - cart_items: [] - } - ) { - cart { - items { - id - } - } - } -} -QUERY; + self::assertArrayHasKey('price', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']); + $price = $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['price']; + self::assertArrayHasKey('value', $price); + self::assertEquals(10, $price['value']); + self::assertArrayHasKey('currency', $price); + self::assertEquals('USD', $price['currency']); - $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + self::assertArrayHasKey('row_total', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']); + $rowTotal = $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total']; + self::assertArrayHasKey('value', $rowTotal); + self::assertEquals(20, $rowTotal['value']); + self::assertArrayHasKey('currency', $rowTotal); + self::assertEquals('USD', $rowTotal['currency']); + + self::assertArrayHasKey( + 'row_total_including_tax', + $response['addSimpleProductsToCart']['cart']['items'][0]['prices'] + ); + $rowTotalIncludingTax = + $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total_including_tax']; + self::assertArrayHasKey('value', $rowTotalIncludingTax); + self::assertEquals(20, $rowTotalIncludingTax['value']); + self::assertArrayHasKey('currency', $rowTotalIncludingTax); + self::assertEquals('USD', $rowTotalIncludingTax['currency']); } /** @@ -107,32 +109,6 @@ public function testAddSimpleProductToCartIfCartIdIsEmpty() $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @expectedException Exception - * @expectedExceptionMessage Required parameter "cart_items" is missing - */ - public function testAddSimpleProductToCartIfCartItemsAreMissed() - { - $query = <<<QUERY -mutation { - addSimpleProductsToCart( - input: { - cart_id: "cart_id" - } - ) { - cart { - items { - id - } - } - } -} -QUERY; - - $this->graphQlMutation($query, [], '', $this->getHeaderMap()); - } - /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @expectedException Exception @@ -262,6 +238,34 @@ private function getQuery(string $maskedQuoteId, string $sku, float $quantity): product { sku } + prices { + price { + value + currency + } + row_total { + value + currency + } + row_total_including_tax { + value + currency + } + } + } + shipping_addresses { + firstname + lastname + company + street + city + postcode + telephone + country { + code + label + } + __typename } } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddVirtualProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddVirtualProductToCartTest.php index ae0208ca3101b..4805721de625a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddVirtualProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/AddVirtualProductToCartTest.php @@ -54,32 +54,6 @@ public function testAddVirtualProductToCart() self::assertEquals($sku, $response['addVirtualProductsToCart']['cart']['items'][0]['product']['sku']); } - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @expectedException Exception - * @expectedExceptionMessage Required parameter "cart_id" is missing - */ - public function testAddVirtualProductToCartIfCartIdIsMissed() - { - $query = <<<QUERY -mutation { - addSimpleProductsToCart( - input: { - cart_items: [] - } - ) { - cart { - items { - id - } - } - } -} -QUERY; - - $this->graphQlMutation($query, [], '', $this->getHeaderMap()); - } - /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @expectedException Exception @@ -107,32 +81,6 @@ public function testAddVirtualProductToCartIfCartIdIsEmpty() $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @expectedException Exception - * @expectedExceptionMessage Required parameter "cart_items" is missing - */ - public function testAddVirtualProductToCartIfCartItemsAreMissed() - { - $query = <<<QUERY -mutation { - addSimpleProductsToCart( - input: { - cart_id: "cart_id" - } - ) { - cart { - items { - id - } - } - } -} -QUERY; - - $this->graphQlMutation($query, [], '', $this->getHeaderMap()); - } - /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @expectedException Exception diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/ApplyCouponToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/ApplyCouponToCartTest.php index df138f9014706..d96bf77f2ef0e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/ApplyCouponToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/ApplyCouponToCartTest.php @@ -183,49 +183,6 @@ public function testApplyCouponWhichIsNotApplicable() $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } - /** - * @param string $input - * @param string $message - * @dataProvider dataProviderUpdateWithMissedRequiredParameters - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php - * @expectedException \Exception - */ - public function testApplyCouponWithMissedRequiredParameters(string $input, string $message) - { - $query = <<<QUERY -mutation { - applyCouponToCart(input: {{$input}}) { - cart { - applied_coupon { - code - } - } - } -} -QUERY; - - $this->expectExceptionMessage($message); - $this->graphQlMutation($query, [], '', $this->getHeaderMap()); - } - - /** - * @return array - */ - public function dataProviderUpdateWithMissedRequiredParameters(): array - { - return [ - 'missed_cart_id' => [ - 'coupon_code: "test"', - 'Required parameter "cart_id" is missing' - ], - 'missed_coupon_code' => [ - 'cart_id: "test_quote"', - 'Required parameter "coupon_code" is missing' - ], - ]; - } - /** * Retrieve customer authorization headers * 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 e7f87f362044a..7ffce2a7f541d 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 @@ -65,6 +65,8 @@ public function testGetCart() $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('cart', $response); + self::assertArrayHasKey('id', $response['cart']); + self::assertEquals($maskedQuoteId, $response['cart']['id']); self::assertArrayHasKey('items', $response['cart']); self::assertCount(2, $response['cart']['items']); @@ -192,7 +194,8 @@ public function testGetCartWithNotDefaultStore() * @magentoApiDataFixture Magento/Store/_files/second_store.php * * @expectedException Exception - * @expectedExceptionMessage Wrong store code specified for cart + * @expectedExceptionMessage The account sign-in was incorrect or your account is disabled temporarily. + * Please wait and try again later. */ public function testGetCartWithWrongStore() { @@ -255,6 +258,7 @@ private function getQuery(string $maskedQuoteId): string return <<<QUERY { cart(cart_id: "{$maskedQuoteId}") { + id items { id quantity diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCustomerCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCustomerCartTest.php new file mode 100644 index 0000000000000..8100bce4ac718 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCustomerCartTest.php @@ -0,0 +1,244 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote\Customer; + +use Exception; +use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test for getting Customer cart information + */ +class GetCustomerCartTest extends GraphQlAbstract +{ + /** + * @var GetMaskedQuoteIdByReservedOrderId + */ + private $getMaskedQuoteIdByReservedOrderId; + + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + } + + /** + * Query for an existing active customer cart + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testGetActiveCustomerCart() + { + $quantity = 2; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $customerCartQuery = $this->getCustomerCartQuery(); + $response = $this->graphQlQuery($customerCartQuery, [], '', $this->getHeaderMap()); + $this->assertArrayHasKey('customerCart', $response); + $this->assertArrayHasKey('items', $response['customerCart']); + $this->assertNotEmpty($response['customerCart']['items']); + $this->assertEquals(2, $response['customerCart']['total_quantity']); + $this->assertArrayHasKey('id', $response['customerCart']); + $this->assertNotEmpty($response['customerCart']['id']); + $this->assertEquals($maskedQuoteId, $response['customerCart']['id']); + $this->assertEquals( + $quantity, + $response['customerCart']['items'][0]['quantity'], + 'Incorrect quantity of products in cart' + ); + } + + /** + * Query for an existing customer cart with no masked quote id + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart_without_masked_quote_id.php + */ + public function testGetLoggedInCustomerCartWithoutMaskedQuoteId() + { + $customerCartQuery = $this->getCustomerCartQuery(); + $response = $this->graphQlQuery($customerCartQuery, [], '', $this->getHeaderMap()); + $this->assertArrayHasKey('customerCart', $response); + $this->assertArrayHasKey('items', $response['customerCart']); + $this->assertEmpty($response['customerCart']['items']); + $this->assertEquals(0, $response['customerCart']['total_quantity']); + $this->assertArrayHasKey('id', $response['customerCart']); + $this->assertNotEmpty($response['customerCart']['id']); + $this->assertNotNull($response['customerCart']['id']); + } + + /** + * Query for customer cart for a user with no existing active cart + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + */ + public function testGetNewCustomerCart() + { + $customerCartQuery = $this->getCustomerCartQuery(); + $response = $this->graphQlQuery($customerCartQuery, [], '', $this->getHeaderMap()); + $this->assertArrayHasKey('customerCart', $response); + $this->assertArrayHasKey('id', $response['customerCart']); + $this->assertNotNull($response['customerCart']['id']); + $this->assertNotEmpty($response['customerCart']['id']); + $this->assertEmpty($response['customerCart']['items']); + $this->assertEquals(0, $response['customerCart']['total_quantity']); + } + + /** + * Query for customer cart with no customer token passed + * + * @expectedException Exception + * @expectedExceptionMessage The request is allowed for logged in customer + */ + public function testGetCustomerCartWithNoCustomerToken() + { + $customerCartQuery = $this->getCustomerCartQuery(); + $this->graphQlQuery($customerCartQuery); + } + + /** + * Query for customer cart after customer token is revoked + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @expectedException \Exception + * @expectedExceptionMessage The request is allowed for logged in customer + */ + public function testGetCustomerCartAfterTokenRevoked() + { + $customerCartQuery = $this->getCustomerCartQuery(); + $headers = $this->getHeaderMap(); + $response = $this->graphQlMutation($customerCartQuery, [], '', $headers); + $this->assertArrayHasKey('customerCart', $response); + $this->assertArrayHasKey('id', $response['customerCart']); + $this->assertNotNull($response['customerCart']['id']); + $this->assertNotEmpty($response['customerCart']['id']); + $this->revokeCustomerToken(); + $customerCartQuery = $this->getCustomerCartQuery(); + $this->graphQlQuery($customerCartQuery, [], '', $headers); + } + + /** + * Querying for the customer cart twice->should return the same cart + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + */ + public function testRequestCustomerCartTwice() + { + $customerCartQuery = $this->getCustomerCartQuery(); + $response = $this->graphQlMutation($customerCartQuery, [], '', $this->getHeaderMap()); + $this->assertArrayHasKey('customerCart', $response); + $this->assertArrayHasKey('id', $response['customerCart']); + $this->assertNotNull($response['customerCart']['id']); + $cartId = $response['customerCart']['id']; + $customerCartQuery = $this->getCustomerCartQuery(); + $response2 = $this->graphQlQuery($customerCartQuery, [], '', $this->getHeaderMap()); + $this->assertEquals($cartId, $response2['customerCart']['id']); + } + + /** + * Query for inactive Customer cart - in case of not finding an active cart, it should create a new one + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/make_cart_inactive.php + */ + public function testGetInactiveCustomerCart() + { + $customerCartQuery = $this->getCustomerCartQuery(); + $response = $this->graphQlQuery($customerCartQuery, [], '', $this->getHeaderMap()); + $this->assertArrayHasKey('customerCart', $response); + $this->assertNotEmpty($response['customerCart']['id']); + $this->assertEmpty($response['customerCart']['items']); + $this->assertEmpty($response['customerCart']['total_quantity']); + } + + /** + * Querying for an existing customer cart for second store + * + * @magentoApiDataFixture Magento/Checkout/_files/active_quote_customer_not_default_store.php + */ + public function testGetCustomerCartSecondStore() + { + $maskedQuoteIdSecondStore = $this->getMaskedQuoteIdByReservedOrderId->execute('test_order_1_not_default_store'); + $customerCartQuery = $this->getCustomerCartQuery(); + + $headerMap = $this->getHeaderMap(); + $headerMap['Store'] = 'fixture_second_store'; + $responseSecondStore = $this->graphQlQuery($customerCartQuery, [], '', $headerMap); + $this->assertEquals($maskedQuoteIdSecondStore, $responseSecondStore['customerCart']['id']); + } + + /** + * Query to revoke customer token + * + * @return void + */ + private function revokeCustomerToken(): void + { + $query = <<<QUERY +mutation{ + revokeCustomerToken{ + result + } +} +QUERY; + + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + + /** + * Query customer cart + * + * @return string + */ + private function getCustomerCartQuery(): string + { + return <<<QUERY +{ + customerCart { + total_quantity + id + items { + id + quantity + product { + sku + } + } + } +} +QUERY; + } + + /** + * Create a header with customer token + * + * @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/MergeCartsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/MergeCartsTest.php new file mode 100644 index 0000000000000..b78c8894970b5 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/MergeCartsTest.php @@ -0,0 +1,293 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote\Customer; + +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\Integration\Api\CustomerTokenServiceInterface; + +/** + * Test for merging customer carts + */ +class MergeCartsTest 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); + } + + protected function tearDown() + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, '1', 'customer_id'); + $this->quoteResource->delete($quote); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testMergeGuestWithCustomerCart() + { + $customerQuote = $this->quoteFactory->create(); + $this->quoteResource->load($customerQuote, 'test_quote', 'reserved_order_id'); + + $guestQuote = $this->quoteFactory->create(); + $this->quoteResource->load( + $guestQuote, + 'test_order_with_virtual_product_without_address', + 'reserved_order_id' + ); + + $customerQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$customerQuote->getId()); + $guestQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$guestQuote->getId()); + + $query = $this->getCartMergeMutation($guestQuoteMaskedId, $customerQuoteMaskedId); + $mergeResponse = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + self::assertArrayHasKey('mergeCarts', $mergeResponse); + $cartResponse = $mergeResponse['mergeCarts']; + self::assertArrayHasKey('items', $cartResponse); + self::assertCount(2, $cartResponse['items']); + $cartResponse = $this->graphQlMutation( + $this->getCartQuery($customerQuoteMaskedId), + [], + '', + $this->getHeaderMap() + ); + + self::assertArrayHasKey('cart', $cartResponse); + self::assertArrayHasKey('items', $cartResponse['cart']); + self::assertCount(2, $cartResponse['cart']['items']); + $item1 = $cartResponse['cart']['items'][0]; + self::assertArrayHasKey('quantity', $item1); + self::assertEquals(2, $item1['quantity']); + $item2 = $cartResponse['cart']['items'][1]; + self::assertArrayHasKey('quantity', $item2); + self::assertEquals(1, $item2['quantity']); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @expectedException \Exception + * @expectedExceptionMessage Current user does not have an active cart. + */ + public function testGuestCartExpiryAfterMerge() + { + $customerQuote = $this->quoteFactory->create(); + $this->quoteResource->load($customerQuote, 'test_quote', 'reserved_order_id'); + + $guestQuote = $this->quoteFactory->create(); + $this->quoteResource->load( + $guestQuote, + 'test_order_with_virtual_product_without_address', + 'reserved_order_id' + ); + + $customerQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$customerQuote->getId()); + $guestQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$guestQuote->getId()); + + $query = $this->getCartMergeMutation($guestQuoteMaskedId, $customerQuoteMaskedId); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + $this->graphQlMutation( + $this->getCartQuery($guestQuoteMaskedId), + [], + '', + $this->getHeaderMap() + ); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/two_customers.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @expectedException \Exception + * @expectedExceptionMessage The current user cannot perform operations on cart + */ + public function testMergeTwoCustomerCarts() + { + $firstQuote = $this->quoteFactory->create(); + $this->quoteResource->load($firstQuote, 'test_quote', 'reserved_order_id'); + $firstMaskedId = $this->quoteIdToMaskedId->execute((int)$firstQuote->getId()); + + $createCartResponse = $this->graphQlMutation( + $this->getCreateEmptyCartMutation(), + [], + '', + $this->getHeaderMap('customer_two@example.com') + ); + self::assertArrayHasKey('createEmptyCart', $createCartResponse); + $secondMaskedId = $createCartResponse['createEmptyCart']; + $this->addSimpleProductToCart($secondMaskedId, $this->getHeaderMap()); + + $query = $this->getCartMergeMutation($firstMaskedId, $secondMaskedId); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + + /** + * Add simple product to cart + * + * @param string $maskedId + * @param array $headerMap + * @throws \Exception + */ + private function addSimpleProductToCart(string $maskedId, array $headerMap): void + { + $result = $this->graphQlMutation($this->getAddProductToCartMutation($maskedId), [], '', $headerMap); + self::assertArrayHasKey('addSimpleProductsToCart', $result); + self::assertArrayHasKey('cart', $result['addSimpleProductsToCart']); + self::assertArrayHasKey('items', $result['addSimpleProductsToCart']['cart']); + self::assertArrayHasKey(0, $result['addSimpleProductsToCart']['cart']['items']); + self::assertArrayHasKey('quantity', $result['addSimpleProductsToCart']['cart']['items'][0]); + self::assertEquals(1, $result['addSimpleProductsToCart']['cart']['items'][0]['quantity']); + self::assertArrayHasKey('product', $result['addSimpleProductsToCart']['cart']['items'][0]); + self::assertArrayHasKey('sku', $result['addSimpleProductsToCart']['cart']['items'][0]['product']); + self::assertEquals('simple_product', $result['addSimpleProductsToCart']['cart']['items'][0]['product']['sku']); + } + + /** + * Create the mergeCart mutation + * + * @param string $guestQuoteMaskedId + * @param string $customerQuoteMaskedId + * @return string + */ + private function getCartMergeMutation(string $guestQuoteMaskedId, string $customerQuoteMaskedId): string + { + return <<<QUERY +mutation { + mergeCarts( + source_cart_id: "{$guestQuoteMaskedId}" + destination_cart_id: "{$customerQuoteMaskedId}" + ){ + items { + quantity + product { + sku + } + } + } +} +QUERY; + } + + /** + * Get cart query + * + * @param string $maskedId + * @return string + */ + private function getCartQuery(string $maskedId): string + { + return <<<QUERY +{ + cart(cart_id: "{$maskedId}") { + items { + quantity + product { + sku + } + } + } +} +QUERY; + } + + /** + * Get create empty cart mutation + * + * @return string + */ + private function getCreateEmptyCartMutation(): string + { + return <<<QUERY +mutation { + createEmptyCart +} +QUERY; + } + + /** + * Get add product to cart mutation + * + * @param string $maskedId + * @return string + */ + private function getAddProductToCartMutation(string $maskedId): string + { + return <<<QUERY +mutation { + addSimpleProductsToCart(input: { + cart_id: "{$maskedId}" + cart_items: { + data: { + quantity: 1 + sku: "simple_product" + } + } + }) { + cart { + items { + quantity + product { + sku + } + } + } + } +} +QUERY; + } + + /** + * @param string $username + * @param string $password + * @return array + * @throws \Magento\Framework\Exception\AuthenticationException + */ + private function getHeaderMap(string $username = 'customer@example.com', string $password = 'password'): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($username, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + return $headerMap; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/PlaceOrderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/PlaceOrderTest.php index 38d9ddc4fecc1..88c57cf2fb282 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/PlaceOrderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/PlaceOrderTest.php @@ -103,26 +103,6 @@ public function testPlaceOrderIfCartIdIsEmpty() $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @expectedException Exception - * @expectedExceptionMessage Required parameter "cart_id" is missing - */ - public function testPlaceOrderIfCartIdIsMissed() - { - $query = <<<QUERY -mutation { - placeOrder(input: {}) { - order { - order_number - } - } -} -QUERY; - - $this->graphQlMutation($query, [], '', $this->getHeaderMap()); - } - /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveCouponFromCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveCouponFromCartTest.php index f906b33fe19d1..1b5a308b5a9a8 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveCouponFromCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveCouponFromCartTest.php @@ -70,29 +70,6 @@ public function testRemoveCouponFromCartIfCartIdIsEmpty() $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @expectedException Exception - * @expectedExceptionMessage Required parameter "cart_id" is missing - */ - public function testRemoveCouponFromCartIfCartIdIsMissed() - { - $query = <<<QUERY -mutation { - removeCouponFromCart(input: {}) { - cart { - applied_coupon { - code - } - } - } -} - -QUERY; - - $this->graphQlMutation($query, [], '', $this->getHeaderMap()); - } - /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @expectedException Exception diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveItemFromCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveItemFromCartTest.php index b810022403b65..c93db424834ef 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveItemFromCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveItemFromCartTest.php @@ -90,50 +90,6 @@ public function testRemoveNonExistentItem() $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @param string $input - * @param string $message - * @dataProvider dataProviderUpdateWithMissedRequiredParameters - */ - public function testUpdateWithMissedItemRequiredParameters(string $input, string $message) - { - $query = <<<QUERY -mutation { - removeItemFromCart( - input: { - {$input} - } - ) { - cart { - items { - quantity - } - } - } -} -QUERY; - $this->expectExceptionMessage($message); - $this->graphQlMutation($query, [], '', $this->getHeaderMap()); - } - - /** - * @return array - */ - public function dataProviderUpdateWithMissedRequiredParameters(): array - { - return [ - 'missed_cart_id' => [ - 'cart_item_id: 1', - 'Required parameter "cart_id" is missing.' - ], - 'missed_cart_item_id' => [ - 'cart_id: "test_quote"', - 'Required parameter "cart_item_id" is missing.' - ], - ]; - } - /** * _security * @magentoApiDataFixture Magento/Customer/_files/customer.php 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 dd8be1788c570..05323a5a7ddf4 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 @@ -96,11 +96,12 @@ public function testSetNewBillingAddress() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" } + same_as_shipping: true } } ) { @@ -119,6 +120,20 @@ public function testSetNewBillingAddress() } __typename } + shipping_addresses { + firstname + lastname + company + street + city + postcode + telephone + country { + code + label + } + __typename + } } } } @@ -129,10 +144,15 @@ public function testSetNewBillingAddress() $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, 'ShippingCartAddress'); } /** + * Test case for deprecated `use_for_shipping` param. + * * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php @@ -154,7 +174,7 @@ public function testSetNewBillingAddressWithUseForShippingParameter() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -350,7 +370,7 @@ public function testSetNewBillingAddressAndFromAddressBookAtSameTime() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -389,7 +409,7 @@ public function testSetNewBillingAddressWithoutCustomerAddressIdAndAddress() input: { cart_id: "$maskedQuoteId" billing_address: { - use_for_shipping: true + same_as_shipping: true } } ) { @@ -415,7 +435,7 @@ public function testSetNewBillingAddressWithoutCustomerAddressIdAndAddress() * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_multishipping_with_two_shipping_addresses.php */ - public function testSetNewBillingAddressWithUseForShippingAndMultishipping() + public function testSetNewBillingAddressWithSameAsShippingAndMultishipping() { $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); @@ -431,12 +451,12 @@ public function testSetNewBillingAddressWithUseForShippingAndMultishipping() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" } - use_for_shipping: true + same_as_shipping: true } } ) { @@ -450,7 +470,7 @@ public function testSetNewBillingAddressWithUseForShippingAndMultishipping() QUERY; self::expectExceptionMessage( - 'Using the "use_for_shipping" option with multishipping is not possible.' + 'Using the "same_as_shipping" option with multishipping is not possible.' ); $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } @@ -624,7 +644,7 @@ public function testSetBillingAddressWithoutRequiredParameters(string $input, st QUERY; $this->expectExceptionMessage($message); - $this->graphQlMutation($query); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } /** @@ -633,15 +653,54 @@ public function testSetBillingAddressWithoutRequiredParameters(string $input, st public function dataProviderSetWithoutRequiredParameters(): array { return [ - 'missed_billing_address' => [ - 'cart_id: "cart_id_value"', - 'Field SetBillingAddressOnCartInput.billing_address of required type BillingAddressInput!' - . ' was not provided.', + 'missed_region' => [ + 'cart_id: "cart_id_value" + billing_address: { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + postcode: "887766" + country_code: "US" + telephone: "88776655" + } + }', + '"regionId" is required. Enter and try again.' + ], + 'missed_multiple_fields' => [ + 'cart_id: "cart_id_value" + billing_address: { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + country_code: "US" + telephone: "88776655" + } + }', + '"postcode" is required. Enter and try again. +"regionId" is required. Enter and try again.' + ], + 'wrong_required_region' => [ + 'cart_id: "cart_id_value" + billing_address: { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + region: "wrong region" + city: "test city" + country_code: "US" + telephone: "88776655" + } + }', + 'Region is not available for the selected country' ], - 'missed_cart_id' => [ - 'billing_address: {}', - 'Required parameter "cart_id" is missing' - ] ]; } @@ -667,7 +726,7 @@ public function testSetNewBillingAddressWithRedundantStreetLine() company: "test company" street: ["test street 1", "test street 2", "test street 3"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -709,7 +768,7 @@ public function testSetBillingAddressWithLowerCaseCountry() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "us" telephone: "88776655" @@ -766,7 +825,7 @@ public function testSetNewBillingAddressWithSaveInAddressBook() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -833,7 +892,7 @@ public function testSetNewBillingAddressWithNotSaveInAddressBook() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -879,6 +938,108 @@ public function testSetNewBillingAddressWithNotSaveInAddressBook() } } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testWithInvalidBillingAddressInput() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $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: "AZ" + postcode: "887766" + country_code: "USS" + telephone: "88776655" + save_in_address_book: false + } + } + } + ) { + cart { + billing_address { + firstname + lastname + company + street + city + postcode + telephone + country { + code + label + } + } + } + } +} +QUERY; + $this->expectExceptionMessage('Country is not available'); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testSetShippingAddressesWithNotRequiredRegion() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $query = <<<QUERY +mutation { + setBillingAddressOnCart( + input: { + cart_id: "$maskedQuoteId" + billing_address: { + address: { + firstname: "Vasyl" + lastname: "Doe" + street: ["1 Svobody"] + city: "Lviv" + region: "Lviv" + postcode: "00000" + country_code: "UA" + telephone: "555-555-55-55" + } + } + } + ) { + cart { + billing_address { + region { + label + } + country { + code + } + } + } + } +} +QUERY; + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + self::assertArrayHasKey('cart', $response['setBillingAddressOnCart']); + $cartResponse = $response['setBillingAddressOnCart']['cart']; + self::assertEquals('UA', $cartResponse['billing_address']['country']['code']); + self::assertEquals('Lviv', $cartResponse['billing_address']['region']['label']); + } + /** * Verify the all the whitelisted fields for a New Address Object * diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodAndPlaceOrderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodAndPlaceOrderTest.php index aff124c522309..b31ce8a7302a9 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodAndPlaceOrderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodAndPlaceOrderTest.php @@ -132,16 +132,6 @@ public function testSetPaymentOnCartWithException(string $input, string $message public function dataProviderSetPaymentOnCartWithException(): array { return [ - 'missed_cart_id' => [ - 'payment_method: { - code: "' . Checkmo::PAYMENT_METHOD_CHECKMO_CODE . '" - }', - 'Required parameter "cart_id" is missing', - ], - 'missed_payment_method' => [ - 'cart_id: "cart_id_value"', - 'Required parameter "code" for "payment_method" is missing.', - ], 'place_order_with_out_of_stock_products' => [ 'cart_id: "cart_id_value" payment_method: { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php index efda719ca153c..57aeda3295268 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 @@ -233,14 +233,6 @@ public function testSetDisabledPaymentOnCart() public function dataProviderSetPaymentMethodWithoutRequiredParameters(): array { return [ - 'missed_cart_id' => [ - 'payment_method: {code: "' . Checkmo::PAYMENT_METHOD_CHECKMO_CODE . '"}', - 'Required parameter "cart_id" is missing.' - ], - 'missed_payment_method' => [ - 'cart_id: "cart_id_value"', - 'Required parameter "code" for "payment_method" is missing.' - ], 'missed_payment_method_code' => [ 'cart_id: "cart_id_value", payment_method: {code: ""}', 'Required parameter "code" for "payment_method" is missing.' diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php index 42b662d264a91..2a19fb0d10d6a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingAddressOnCartTest.php @@ -99,7 +99,7 @@ public function testSetNewShippingAddressOnCartWithSimpleProduct() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -164,7 +164,7 @@ public function testSetNewShippingAddressOnCartWithVirtualProduct() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -269,7 +269,6 @@ public function testVerifyShippingAddressType() /** * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php @@ -329,7 +328,7 @@ public function testSetNewShippingAddressAndFromAddressBookAtSameTime() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -462,25 +461,104 @@ public function testSetNewShippingAddressWithMissedRequiredParameters(string $in $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } + /** + * Covers case with empty street + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * + * @expectedException \Exception + * @expectedExceptionMessage Field CartAddressInput.street of required type [String]! was not provided. + */ + public function testSetNewShippingAddressWithMissedRequiredStreetParameters() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + address: { + country_code: "US" + firstname: "J" + lastname: "D" + telephone: "+" + city: "C" + } + } + ] + } + ) { + cart { + shipping_addresses { + city + } + } + } +} +QUERY; + + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + /** * @return array */ public function dataProviderUpdateWithMissedRequiredParameters(): array { return [ - 'missed_shipping_addresses' => [ - 'cart_id: "cart_id_value"', - 'Field SetShippingAddressesOnCartInput.shipping_addresses of required type [ShippingAddressInput]! ' . - 'was not provided.', + 'missed_region' => [ + 'cart_id: "cart_id_value" + shipping_addresses: [{ + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + postcode: "887766" + country_code: "US" + telephone: "88776655" + } + }]', + '"regionId" is required. Enter and try again.' + ], + 'missed_multiple_fields' => [ + 'cart_id: "cart_id_value" + shipping_addresses: [{ + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + country_code: "US" + telephone: "88776655" + } + }]', + '"postcode" is required. Enter and try again. +"regionId" is required. Enter and try again.' ], - 'missed_city' => [ - 'shipping_addresses: [ { address: { save_in_address_book: false } } ]', - 'Field CartAddressInput.city of required type String! was not provided' + 'wrong_required_region' => [ + 'cart_id: "cart_id_value" + shipping_addresses: [{ + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + region: "wrong region" + city: "test city" + country_code: "US" + telephone: "88776655" + } + }]', + 'Region is not available for the selected country' ], - 'missed_cart_id' => [ - 'shipping_addresses: {}', - 'Required parameter "cart_id" is missing' - ] ]; } @@ -510,7 +588,7 @@ public function testSetMultipleNewShippingAddresses() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -523,7 +601,7 @@ public function testSetMultipleNewShippingAddresses() company: "test company 2" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -566,7 +644,7 @@ public function testSetNewShippingAddressOnCartWithRedundantStreetLine() company: "test company" street: ["test street 1", "test street 2", "test street 3"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -710,6 +788,100 @@ public function testSetShippingAddressWithLowerCaseCountry() $this->assertEquals('CA', $address['region']['code']); } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testWithInvalidShippingAddressesInput() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + address: { + firstname: "John" + lastname: "Doe" + street: ["6161 West Centinella Avenue"] + city: "Culver City" + region: "CA" + postcode: "90230" + country_code: "USS" + telephone: "555-555-55-55" + } + } + ] + } + ) { + cart { + shipping_addresses { + city + } + } + } +} +QUERY; + $this->expectExceptionMessage('Country is not available'); + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + */ + public function testSetShippingAddressesWithNotRequiredRegion() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + address: { + firstname: "Vasyl" + lastname: "Doe" + street: ["1 Svobody"] + city: "Lviv" + region: "Lviv" + postcode: "00000" + country_code: "UA" + telephone: "555-555-55-55" + } + } + ] + } + ) { + cart { + shipping_addresses { + region { + label + } + country { + code + } + } + } + } +} +QUERY; + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); + $cartResponse = $response['setShippingAddressesOnCart']['cart']; + self::assertEquals('UA', $cartResponse['shipping_addresses'][0]['country']['code']); + self::assertEquals('Lviv', $cartResponse['shipping_addresses'][0]['region']['label']); + } + /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php @@ -732,7 +904,7 @@ public function testSetNewShippingAddressWithSaveInAddressBook() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -803,7 +975,7 @@ public function testSetNewShippingAddressWithNotSaveInAddressBook() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php index 278f21f30b72d..293bfdaf502d9 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetShippingMethodsOnCartTest.php @@ -169,27 +169,10 @@ public function testSetShippingMethodWithWrongParameters(string $input, string $ public function dataProviderSetShippingMethodWithWrongParameters(): array { return [ - 'missed_cart_id' => [ - 'shipping_methods: [{ - carrier_code: "flatrate" - method_code: "flatrate" - }]', - 'Required parameter "cart_id" is missing' - ], - 'missed_shipping_methods' => [ - 'cart_id: "cart_id_value"', - 'Required parameter "shipping_methods" is missing' - ], 'shipping_methods_are_empty' => [ 'cart_id: "cart_id_value" shipping_methods: []', 'Required parameter "shipping_methods" is missing' ], - 'missed_carrier_code' => [ - 'cart_id: "cart_id_value", shipping_methods: [{ - method_code: "flatrate" - }]', - 'Field ShippingMethodInput.carrier_code of required type String! was not provided.' - ], 'empty_carrier_code' => [ 'cart_id: "cart_id_value", shipping_methods: [{ carrier_code: "" @@ -204,12 +187,6 @@ public function dataProviderSetShippingMethodWithWrongParameters(): array }]', 'Carrier with such method not found: wrong-carrier-code, flatrate' ], - 'missed_method_code' => [ - 'cart_id: "cart_id_value", shipping_methods: [{ - carrier_code: "flatrate" - }]', - 'Required parameter "method_code" is missing.' - ], 'empty_method_code' => [ 'cart_id: "cart_id_value", shipping_methods: [{ carrier_code: "flatrate" diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/UpdateCartItemsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/UpdateCartItemsTest.php index 48ea4ab7a15e3..b351872a69bc7 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/UpdateCartItemsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/UpdateCartItemsTest.php @@ -209,35 +209,6 @@ public function testUpdateItemInAnotherCustomerCart() $this->graphQlMutation($query, [], '', $this->getHeaderMap()); } - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @expectedException \Exception - * @expectedExceptionMessage Required parameter "cart_id" is missing. - */ - public function testUpdateWithMissedCartItemId() - { - $query = <<<QUERY -mutation { - updateCartItems(input: { - cart_items: [ - { - cart_item_id: 1 - quantity: 2 - } - ] - }) { - cart { - items { - id - quantity - } - } - } -} -QUERY; - $this->graphQlMutation($query, [], '', $this->getHeaderMap()); - } - /** * @param string $input * @param string $message @@ -275,14 +246,6 @@ public function testUpdateWithMissedItemRequiredParameters(string $input, string public function dataProviderUpdateWithMissedRequiredParameters(): array { return [ - 'missed_cart_items' => [ - '', - 'Required parameter "cart_items" is missing.' - ], - 'missed_cart_item_id' => [ - 'cart_items: [{ quantity: 2 }]', - 'Required parameter "cart_item_id" for "cart_items" is missing.' - ], 'missed_cart_item_qty' => [ 'cart_items: [{ cart_item_id: 1 }]', 'Required parameter "quantity" for "cart_items" is missing.' diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php index 4deed99243f9d..01ae565f00bf6 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddSimpleProductToCartTest.php @@ -45,33 +45,38 @@ public function testAddSimpleProductToCart() $response = $this->graphQlMutation($query); self::assertArrayHasKey('cart', $response['addSimpleProductsToCart']); + self::assertArrayHasKey('shipping_addresses', $response['addSimpleProductsToCart']['cart']); + self::assertEmpty($response['addSimpleProductsToCart']['cart']['shipping_addresses']); self::assertEquals($quantity, $response['addSimpleProductsToCart']['cart']['items'][0]['quantity']); self::assertEquals($sku, $response['addSimpleProductsToCart']['cart']['items'][0]['product']['sku']); - } - - /** - * @expectedException Exception - * @expectedExceptionMessage Required parameter "cart_id" is missing - */ - public function testAddSimpleProductToCartIfCartIdIsMissed() - { - $query = <<<QUERY -mutation { - addSimpleProductsToCart( - input: { - cart_items: [] - } - ) { - cart { - items { - id - } - } - } -} -QUERY; - - $this->graphQlMutation($query); + self::assertArrayHasKey('prices', $response['addSimpleProductsToCart']['cart']['items'][0]); + self::assertArrayHasKey('id', $response['addSimpleProductsToCart']['cart']); + self::assertEquals($maskedQuoteId, $response['addSimpleProductsToCart']['cart']['id']); + + self::assertArrayHasKey('price', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']); + $price = $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['price']; + self::assertArrayHasKey('value', $price); + self::assertEquals(10, $price['value']); + self::assertArrayHasKey('currency', $price); + self::assertEquals('USD', $price['currency']); + + self::assertArrayHasKey('row_total', $response['addSimpleProductsToCart']['cart']['items'][0]['prices']); + $rowTotal = $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total']; + self::assertArrayHasKey('value', $rowTotal); + self::assertEquals(20, $rowTotal['value']); + self::assertArrayHasKey('currency', $rowTotal); + self::assertEquals('USD', $rowTotal['currency']); + + self::assertArrayHasKey( + 'row_total_including_tax', + $response['addSimpleProductsToCart']['cart']['items'][0]['prices'] + ); + $rowTotalIncludingTax = + $response['addSimpleProductsToCart']['cart']['items'][0]['prices']['row_total_including_tax']; + self::assertArrayHasKey('value', $rowTotalIncludingTax); + self::assertEquals(20, $rowTotalIncludingTax['value']); + self::assertArrayHasKey('currency', $rowTotalIncludingTax); + self::assertEquals('USD', $rowTotalIncludingTax['currency']); } /** @@ -100,31 +105,6 @@ public function testAddSimpleProductToCartIfCartIdIsEmpty() $this->graphQlMutation($query); } - /** - * @expectedException Exception - * @expectedExceptionMessage Required parameter "cart_items" is missing - */ - public function testAddSimpleProductToCartIfCartItemsAreMissed() - { - $query = <<<QUERY -mutation { - addSimpleProductsToCart( - input: { - cart_id: "cart_id" - } - ) { - cart { - items { - id - } - } - } -} -QUERY; - - $this->graphQlMutation($query); - } - /** * @expectedException Exception * @expectedExceptionMessage Required parameter "cart_items" is missing @@ -226,11 +206,40 @@ private function getQuery(string $maskedQuoteId, string $sku, float $quantity): } ) { cart { + id items { quantity product { sku } + prices { + price { + value + currency + } + row_total { + value + currency + } + row_total_including_tax { + value + currency + } + } + } + shipping_addresses { + firstname + lastname + company + street + city + postcode + telephone + country { + code + label + } + __typename } } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddVirtualProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddVirtualProductToCartTest.php index 131ff0c480d64..3811a60ffc522 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddVirtualProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AddVirtualProductToCartTest.php @@ -45,35 +45,12 @@ public function testAddVirtualProductToCart() $response = $this->graphQlMutation($query); self::assertArrayHasKey('cart', $response['addVirtualProductsToCart']); + self::assertArrayHasKey('id', $response['addVirtualProductsToCart']['cart']); + self::assertEquals($maskedQuoteId, $response['addVirtualProductsToCart']['cart']['id']); self::assertEquals($quantity, $response['addVirtualProductsToCart']['cart']['items'][0]['quantity']); self::assertEquals($sku, $response['addVirtualProductsToCart']['cart']['items'][0]['product']['sku']); } - /** - * @expectedException Exception - * @expectedExceptionMessage Required parameter "cart_id" is missing - */ - public function testAddVirtualProductToCartIfCartIdIsMissed() - { - $query = <<<QUERY -mutation { - addSimpleProductsToCart( - input: { - cart_items: [] - } - ) { - cart { - items { - id - } - } - } -} -QUERY; - - $this->graphQlMutation($query); - } - /** * @expectedException Exception * @expectedExceptionMessage Required parameter "cart_id" is missing @@ -100,31 +77,6 @@ public function testAddVirtualProductToCartIfCartIdIsEmpty() $this->graphQlMutation($query); } - /** - * @expectedException Exception - * @expectedExceptionMessage Required parameter "cart_items" is missing - */ - public function testAddVirtualProductToCartIfCartItemsAreMissed() - { - $query = <<<QUERY -mutation { - addSimpleProductsToCart( - input: { - cart_id: "cart_id" - } - ) { - cart { - items { - id - } - } - } -} -QUERY; - - $this->graphQlMutation($query); - } - /** * @expectedException Exception * @expectedExceptionMessage Required parameter "cart_items" is missing @@ -227,6 +179,7 @@ private function getQuery(string $maskedQuoteId, string $sku, float $quantity): } ) { cart { + id items { quantity product { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AllowGuestCheckoutOptionTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AllowGuestCheckoutOptionTest.php index 90ebec763b227..60c3cc2e8b24e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AllowGuestCheckoutOptionTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/AllowGuestCheckoutOptionTest.php @@ -104,7 +104,7 @@ public function testSetBillingAddressToGuestCustomerCart() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" @@ -216,7 +216,7 @@ public function testSetNewShippingAddressOnCartWithGuestCheckoutDisabled() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AZ" postcode: "887766" country_code: "US" telephone: "88776655" diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponToCartTest.php index 35ad3ad34c7d6..454f01b5cde19 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponToCartTest.php @@ -41,6 +41,8 @@ public function testApplyCouponToCart() $response = $this->graphQlMutation($query); self::assertArrayHasKey('applyCouponToCart', $response); + self::assertArrayHasKey('id', $response['applyCouponToCart']['cart']); + self::assertEquals($maskedQuoteId, $response['applyCouponToCart']['cart']['id']); self::assertEquals($couponCode, $response['applyCouponToCart']['cart']['applied_coupon']['code']); } @@ -148,49 +150,6 @@ public function testApplyCouponWhichIsNotApplicable() $this->graphQlMutation($query); } - /** - * @param string $input - * @param string $message - * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php - * @dataProvider dataProviderUpdateWithMissedRequiredParameters - * @expectedException \Exception - */ - public function testApplyCouponWithMissedRequiredParameters(string $input, string $message) - { - $query = <<<QUERY -mutation { - applyCouponToCart(input: {{$input}}) { - cart { - applied_coupon { - code - } - } - } -} -QUERY; - - $this->expectExceptionMessage($message); - $this->graphQlMutation($query); - } - - /** - * @return array - */ - public function dataProviderUpdateWithMissedRequiredParameters(): array - { - return [ - 'missed_cart_id' => [ - 'coupon_code: "test"', - 'Required parameter "cart_id" is missing' - ], - 'missed_coupon_code' => [ - 'cart_id: "test_quote"', - 'Required parameter "coupon_code" is missing' - ], - ]; - } - /** * @param string $maskedQuoteId * @param string $couponCode @@ -202,6 +161,7 @@ private function getQuery(string $maskedQuoteId, string $couponCode): string mutation { applyCouponToCart(input: {cart_id: "$maskedQuoteId", coupon_code: "$couponCode"}) { cart { + id applied_coupon { code } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponsToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponsToCartTest.php index 9adafa7e097f2..0344e274d6fbc 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponsToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponsToCartTest.php @@ -127,49 +127,6 @@ public function testApplyCouponsWhichIsNotApplicable() $this->graphQlMutation($query); } - /** - * @param string $input - * @param string $message - * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php - * @dataProvider dataProviderUpdateWithMissedRequiredParameters - * @expectedException \Exception - */ - public function testApplyCouponsWithMissedRequiredParameters(string $input, string $message) - { - $query = <<<QUERY -mutation { - applyCouponToCart(input: {{$input}}) { - cart { - applied_coupons { - code - } - } - } -} -QUERY; - - $this->expectExceptionMessage($message); - $this->graphQlMutation($query); - } - - /** - * @return array - */ - public function dataProviderUpdateWithMissedRequiredParameters(): array - { - return [ - 'missed_cart_id' => [ - 'coupon_code: "test"', - 'Required parameter "cart_id" is missing' - ], - 'missed_coupon_code' => [ - 'cart_id: "test_quote"', - 'Required parameter "coupon_code" is missing' - ], - ]; - } - /** * @param string $maskedQuoteId * @param string $couponCode diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailableShippingMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailableShippingMethodsTest.php index 0d64d73965d2b..867aaab7b3a58 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailableShippingMethodsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailableShippingMethodsTest.php @@ -73,6 +73,12 @@ public function testGetAvailableShippingMethods() $expectedAddressData, $response['cart']['shipping_addresses'][0]['available_shipping_methods'][0] ); + self::assertCount(1, $response['cart']['shipping_addresses'][0]['cart_items']); + self::assertCount(1, $response['cart']['shipping_addresses'][0]['cart_items_v2']); + self::assertEquals( + 'simple_product', + $response['cart']['shipping_addresses'][0]['cart_items_v2'][0]['product']['sku'] + ); } /** @@ -140,6 +146,13 @@ private function getQuery(string $maskedQuoteId): string cart_item_id quantity } + cart_items_v2 { + id + quantity + product { + sku + } + } available_shipping_methods { amount { value 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 d39f1b42459c7..ae9b7b32b2dab 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 @@ -44,6 +44,8 @@ public function testGetCart() self::assertArrayHasKey('cart', $response); self::assertArrayHasKey('items', $response['cart']); + self::assertArrayHasKey('id', $response['cart']); + self::assertEquals($maskedQuoteId, $response['cart']['id']); self::assertCount(2, $response['cart']['items']); self::assertNotEmpty($response['cart']['items'][0]['id']); @@ -184,6 +186,7 @@ private function getQuery(string $maskedQuoteId): string return <<<QUERY { cart(cart_id: "{$maskedQuoteId}") { + id items { id quantity diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/MergeCartsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/MergeCartsTest.php new file mode 100644 index 0000000000000..e558ac41eae52 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/MergeCartsTest.php @@ -0,0 +1,95 @@ +<?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 merging guest carts + */ +class MergeCartsTest 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/simple_product.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php + * @expectedException \Exception + * @expectedExceptionMessage The current customer isn't authorized. + */ + public function testMergeGuestCarts() + { + $firstQuote = $this->quoteFactory->create(); + $this->quoteResource->load($firstQuote, 'test_order_with_simple_product_without_address', 'reserved_order_id'); + + $secondQuote = $this->quoteFactory->create(); + $this->quoteResource->load( + $secondQuote, + 'test_order_with_virtual_product_without_address', + 'reserved_order_id' + ); + + $firstMaskedId = $this->quoteIdToMaskedId->execute((int)$firstQuote->getId()); + $secondMaskedId = $this->quoteIdToMaskedId->execute((int)$secondQuote->getId()); + + $query = $this->getCartMergeMutation($firstMaskedId, $secondMaskedId); + $this->graphQlMutation($query); + } + + /** + * Create the mergeCart mutation + * + * @param string $guestQuoteMaskedId + * @param string $customerQuoteMaskedId + * @return string + */ + private function getCartMergeMutation(string $guestQuoteMaskedId, string $customerQuoteMaskedId): string + { + return <<<QUERY +mutation { + mergeCarts( + source_cart_id: "{$guestQuoteMaskedId}" + destination_cart_id: "{$customerQuoteMaskedId}" + ){ + items { + quantity + product { + sku + } + } + } +} +QUERY; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/PlaceOrderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/PlaceOrderTest.php index 52caf836d3b46..bf31d3c6fa3f4 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/PlaceOrderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/PlaceOrderTest.php @@ -95,25 +95,6 @@ public function testPlaceOrderIfCartIdIsEmpty() $this->graphQlMutation($query); } - /** - * @expectedException Exception - * @expectedExceptionMessage Required parameter "cart_id" is missing - */ - public function testPlaceOrderIfCartIdIsMissed() - { - $query = <<<QUERY -mutation { - placeOrder(input: {}) { - order { - order_number - } - } -} -QUERY; - - $this->graphQlMutation($query); - } - /** * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php * @magentoConfigFixture default_store carriers/flatrate/active 1 diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveCouponFromCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveCouponFromCartTest.php index 57a13e2f1bc03..e94a70cbd929f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveCouponFromCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveCouponFromCartTest.php @@ -61,28 +61,6 @@ public function testRemoveCouponFromCartIfCartIdIsEmpty() $this->graphQlMutation($query); } - /** - * @expectedException Exception - * @expectedExceptionMessage Required parameter "cart_id" is missing - */ - public function testRemoveCouponFromCartIfCartIdIsMissed() - { - $query = <<<QUERY -mutation { - removeCouponFromCart(input: {}) { - cart { - applied_coupon { - code - } - } - } -} - -QUERY; - - $this->graphQlMutation($query); - } - /** * @expectedException Exception * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id" diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveItemFromCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveItemFromCartTest.php index 7e3d3553ba2f3..6f105259bf65c 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveItemFromCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveItemFromCartTest.php @@ -80,49 +80,6 @@ public function testRemoveNonExistentItem() $this->graphQlMutation($query); } - /** - * @param string $input - * @param string $message - * @dataProvider dataProviderUpdateWithMissedRequiredParameters - */ - public function testUpdateWithMissedItemRequiredParameters(string $input, string $message) - { - $query = <<<QUERY -mutation { - removeItemFromCart( - input: { - {$input} - } - ) { - cart { - items { - quantity - } - } - } -} -QUERY; - $this->expectExceptionMessage($message); - $this->graphQlMutation($query); - } - - /** - * @return array - */ - public function dataProviderUpdateWithMissedRequiredParameters(): array - { - return [ - 'missed_cart_id' => [ - 'cart_item_id: 1', - 'Required parameter "cart_id" is missing.' - ], - 'missed_cart_item_id' => [ - 'cart_id: "test_quote"', - 'Required parameter "cart_item_id" is missing.' - ], - ]; - } - /** * _security * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php 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 5a3d45005c911..ea77ad35d2693 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php @@ -42,17 +42,18 @@ public function testSetNewBillingAddress() input: { cart_id: "$maskedQuoteId" billing_address: { - address: { + address: { firstname: "test firstname" lastname: "test lastname" company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" - } + } + same_as_shipping: true } } ) { @@ -71,6 +72,20 @@ public function testSetNewBillingAddress() } __typename } + shipping_addresses { + firstname + lastname + company + street + city + postcode + telephone + country { + code + label + } + __typename + } } } } @@ -82,9 +97,15 @@ public function testSetNewBillingAddress() self::assertArrayHasKey('billing_address', $cartResponse); $billingAddressResponse = $cartResponse['billing_address']; $this->assertNewAddressFields($billingAddressResponse); + self::assertArrayHasKey('shipping_addresses', $cartResponse); + $shippingAddressResponse = current($cartResponse['shipping_addresses']); + $this->assertNewAddressFields($billingAddressResponse); + $this->assertNewAddressFields($shippingAddressResponse, 'ShippingCartAddress'); } /** + * Test case for deprecated `use_for_shipping` param. + * * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php @@ -105,7 +126,7 @@ public function testSetNewBillingAddressWithUseForShippingParameter() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" @@ -180,7 +201,7 @@ public function testSetBillingAddressToCustomerCart() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" @@ -256,7 +277,7 @@ public function testSetBillingAddressOnNonExistentCart() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" @@ -275,58 +296,6 @@ public function testSetBillingAddressOnNonExistentCart() $this->graphQlMutation($query); } - /** - * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php - * - * @dataProvider dataProviderSetWithoutRequiredParameters - * @param string $input - * @param string $message - * @throws \Exception - */ - public function testSetBillingAddressWithoutRequiredParameters(string $input, string $message) - { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $input = str_replace('cart_id_value', $maskedQuoteId, $input); - - $query = <<<QUERY -mutation { - setBillingAddressOnCart( - input: { - {$input} - } - ) { - cart { - billing_address { - city - } - } - } -} -QUERY; - $this->expectExceptionMessage($message); - $this->graphQlMutation($query); - } - - /** - * @return array - */ - public function dataProviderSetWithoutRequiredParameters(): array - { - return [ - 'missed_billing_address' => [ - 'cart_id: "cart_id_value"', - 'Field SetBillingAddressOnCartInput.billing_address of required type BillingAddressInput!' - . ' was not provided.', - ], - 'missed_cart_id' => [ - 'billing_address: {}', - 'Required parameter "cart_id" is missing' - ] - ]; - } - /** * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php @@ -342,7 +311,7 @@ public function testSetNewBillingAddressWithoutCustomerAddressIdAndAddress() input: { cart_id: "$maskedQuoteId" billing_address: { - use_for_shipping: true + same_as_shipping: true } } ) { @@ -367,7 +336,7 @@ public function testSetNewBillingAddressWithoutCustomerAddressIdAndAddress() * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_multishipping_with_two_shipping_addresses.php */ - public function testSetNewBillingAddressWithUseForShippingAndMultishipping() + public function testSetNewBillingAddressWithSameAsShippingAndMultishipping() { $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); @@ -383,12 +352,12 @@ public function testSetNewBillingAddressWithUseForShippingAndMultishipping() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" } - use_for_shipping: true + same_as_shipping: true } } ) { @@ -402,7 +371,7 @@ public function testSetNewBillingAddressWithUseForShippingAndMultishipping() QUERY; self::expectExceptionMessage( - 'Using the "use_for_shipping" option with multishipping is not possible.' + 'Using the "same_as_shipping" option with multishipping is not possible.' ); $this->graphQlMutation($query); } @@ -428,7 +397,7 @@ public function testSetNewBillingAddressRedundantStreetLine() company: "test company" street: ["test street 1", "test street 2", "test street 3"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" @@ -470,7 +439,7 @@ public function testSetBillingAddressWithLowerCaseCountry() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "us" telephone: "88776655" 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 fdec9a1dd9853..c3e35f0bf80e8 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 @@ -183,14 +183,6 @@ public function testSetPaymentMethodWithoutRequiredParameters(string $input, str public function dataProviderSetPaymentMethodWithoutRequiredParameters(): array { return [ - 'missed_cart_id' => [ - 'payment_method: {code: "' . Checkmo::PAYMENT_METHOD_CHECKMO_CODE . '"}', - 'Required parameter "cart_id" is missing.' - ], - 'missed_payment_method' => [ - 'cart_id: "cart_id_value"', - 'Required parameter "code" for "payment_method" is missing.' - ], 'missed_payment_method_code' => [ 'cart_id: "cart_id_value", payment_method: {code: ""}', 'Required parameter "code" for "payment_method" is missing.' diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php index 2e98773ad9187..53a20b775530b 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingAddressOnCartTest.php @@ -49,7 +49,7 @@ public function testSetNewShippingAddressOnCartWithSimpleProduct() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" @@ -113,7 +113,7 @@ public function testSetNewShippingAddressOnCartWithVirtualProduct() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" @@ -208,40 +208,6 @@ public function testSetShippingAddressToCustomerCart() $this->graphQlMutation($query); } - /** - * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php - * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php - * - * @dataProvider dataProviderUpdateWithMissedRequiredParameters - * @param string $input - * @param string $message - * @throws \Exception - */ - public function testSetNewShippingAddressWithMissedRequiredParameters(string $input, string $message) - { - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); - $input = str_replace('cart_id_value', $maskedQuoteId, $input); - - $query = <<<QUERY -mutation { - setShippingAddressesOnCart( - input: { - {$input} - } - ) { - cart { - shipping_addresses { - city - } - } - } -} -QUERY; - $this->expectExceptionMessage($message); - $this->graphQlMutation($query); - } - /** * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php @@ -264,7 +230,7 @@ public function testSetNewShippingAddressOnCartWithRedundantStreetLine() company: "test company" street: ["test street 1", "test street 2", "test street 3"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" @@ -285,28 +251,6 @@ public function testSetNewShippingAddressOnCartWithRedundantStreetLine() $this->graphQlMutation($query); } - /** - * @return array - */ - public function dataProviderUpdateWithMissedRequiredParameters(): array - { - return [ - 'missed_shipping_addresses' => [ - 'cart_id: "cart_id_value"', - 'Field SetShippingAddressesOnCartInput.shipping_addresses of required type [ShippingAddressInput]! ' . - 'was not provided.', - ], - 'missed_city' => [ - 'shipping_addresses: [ { address: { save_in_address_book: false } } ]', - 'Field CartAddressInput.city of required type String! was not provided' - ], - 'missed_cart_id' => [ - 'shipping_addresses: {}', - 'Required parameter "cart_id" is missing' - ] - ]; - } - /** * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php @@ -332,7 +276,7 @@ public function testSetMultipleNewShippingAddresses() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" @@ -345,7 +289,7 @@ public function testSetMultipleNewShippingAddresses() company: "test company 2" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" @@ -384,7 +328,7 @@ public function testSetShippingAddressOnNonExistentCart() company: "test company" street: ["test street 1", "test street 2"] city: "test city" - region: "test region" + region: "AL" postcode: "887766" country_code: "US" telephone: "88776655" diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingMethodsOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingMethodsOnCartTest.php index 117aedf59b5a5..0a49136421e9a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingMethodsOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetShippingMethodsOnCartTest.php @@ -186,27 +186,10 @@ public function testSetShippingMethodWithWrongParameters(string $input, string $ public function dataProviderSetShippingMethodWithWrongParameters(): array { return [ - 'missed_cart_id' => [ - 'shipping_methods: [{ - carrier_code: "flatrate" - method_code: "flatrate" - }]', - 'Required parameter "cart_id" is missing' - ], - 'missed_shipping_methods' => [ - 'cart_id: "cart_id_value"', - 'Required parameter "shipping_methods" is missing' - ], 'shipping_methods_are_empty' => [ 'cart_id: "cart_id_value" shipping_methods: []', 'Required parameter "shipping_methods" is missing' ], - 'missed_carrier_code' => [ - 'cart_id: "cart_id_value", shipping_methods: [{ - method_code: "flatrate" - }]', - 'Field ShippingMethodInput.carrier_code of required type String! was not provided.' - ], 'empty_carrier_code' => [ 'cart_id: "cart_id_value", shipping_methods: [{ carrier_code: "" @@ -221,12 +204,6 @@ public function dataProviderSetShippingMethodWithWrongParameters(): array }]', 'Carrier with such method not found: wrong-carrier-code, flatrate' ], - 'missed_method_code' => [ - 'cart_id: "cart_id_value", shipping_methods: [{ - carrier_code: "flatrate" - }]', - 'Required parameter "method_code" is missing.' - ], 'empty_method_code' => [ 'cart_id: "cart_id_value", shipping_methods: [{ carrier_code: "flatrate" diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/UpdateCartItemsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/UpdateCartItemsTest.php index 6ac683ef77ade..761993d983db8 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/UpdateCartItemsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/UpdateCartItemsTest.php @@ -181,34 +181,6 @@ public function testUpdateItemFromCustomerCart() $this->graphQlMutation($query); } - /** - * @expectedException \Exception - * @expectedExceptionMessage Required parameter "cart_id" is missing. - */ - public function testUpdateWithMissedCartItemId() - { - $query = <<<QUERY -mutation { - updateCartItems(input: { - cart_items: [ - { - cart_item_id: 1 - quantity: 2 - } - ] - }) { - cart { - items { - id - quantity - } - } - } -} -QUERY; - $this->graphQlMutation($query); - } - /** * @param string $input * @param string $message @@ -246,14 +218,6 @@ public function testUpdateWithMissedItemRequiredParameters(string $input, string public function dataProviderUpdateWithMissedRequiredParameters(): array { return [ - 'missed_cart_items' => [ - '', - 'Required parameter "cart_items" is missing.' - ], - 'missed_cart_item_id' => [ - 'cart_items: [{ quantity: 2 }]', - 'Required parameter "cart_item_id" for "cart_items" is missing.' - ], 'missed_cart_item_qty' => [ 'cart_items: [{ cart_item_id: 1 }]', 'Required parameter "quantity" for "cart_items" is missing.' diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/OrdersTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/OrdersTest.php index 11a2216b6668f..5d1f5847e8419 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/OrdersTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/OrdersTest.php @@ -7,6 +7,7 @@ namespace Magento\GraphQl\Sales; +use Exception; use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\TestFramework\TestCase\GraphQlAbstract; use Magento\TestFramework\Helper\Bootstrap; @@ -99,6 +100,25 @@ public function testOrdersQuery() } } + /** + * @expectedException Exception + * @expectedExceptionMessage The current customer isn't authorized. + */ + public function testOrdersQueryNotAuthorized() + { + $query = <<<QUERY +{ + customerOrders { + items { + increment_id + grand_total + } + } +} +QUERY; + $this->graphQlQuery($query); + } + /** * @param string $email * @param string $password diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreValidatorTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreValidatorTest.php new file mode 100644 index 0000000000000..3e5f868a21da5 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreValidatorTest.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Store; + +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test the GraphQL `Store` header validation + */ +class StoreValidatorTest extends GraphQlAbstract +{ + /** + * @param string $storeCode + * @param string $errorMessage + * + * @dataProvider dataProviderInvalidStore + * @magentoApiDataFixture Magento/Store/_files/inactive_store.php + */ + public function testInvalidStoreHeader(string $storeCode, string $errorMessage) + { + $query + = <<<QUERY +{ + storeConfig{ + code + } +} +QUERY; + $this->expectExceptionMessage($errorMessage); + $this->graphQlMutation($query, [], '', ['Store' => $storeCode]); + } + + /** + * Data provider with invalid store codes and expected error messages + * + * @return array + */ + public function dataProviderInvalidStore(): array + { + return [ + 'non_existing' => [ + 'non_existing', + 'Requested store is not found' + ], + 'inactive_store' => [ + 'inactive_store', + 'Requested store is not found' + ] + ]; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Swatches/ProductSwatchDataTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Swatches/ProductSwatchDataTest.php new file mode 100644 index 0000000000000..c356012c71f47 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Swatches/ProductSwatchDataTest.php @@ -0,0 +1,127 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Swatches; + +use Magento\Swatches\Helper\Media as SwatchesMedia; +use Magento\Swatches\Model\Swatch; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test for configurable product option swatch data + */ +class ProductSwatchDataTest extends GraphQlAbstract +{ + /** + * @var SwatchesMedia + */ + private $swatchMediaHelper; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->swatchMediaHelper = $objectManager->get(SwatchesMedia::class); + } + + /** + * @magentoApiDataFixture Magento/Swatches/_files/text_swatch_attribute.php + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/configurable_products.php + */ + public function testTextSwatchDataValues() + { + $productSku = 'configurable'; + $query = <<<QUERY +{ + products(filter: {sku: {eq: "$productSku"}}) { + items { + ... on ConfigurableProduct{ + configurable_options{ + values { + swatch_data{ + value + } + } + } + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + + $this->assertArrayHasKey('products', $response); + $this->assertArrayHasKey('items', $response['products']); + $this->assertArrayHasKey(0, $response['products']['items']); + + $product = $response['products']['items'][0]; + $this->assertArrayHasKey('configurable_options', $product); + $this->assertArrayHasKey(0, $product['configurable_options']); + + $option = $product['configurable_options'][0]; + $this->assertArrayHasKey('values', $option); + $length = count($option['values']); + for ($i = 0; $i < $length; $i++) { + $this->assertEquals('option ' . ($i + 1), $option['values'][$i]['swatch_data']['value']); + } + } + + /** + * @magentoApiDataFixture Magento/Swatches/_files/visual_swatch_attribute_with_different_options_type.php + * @magentoApiDataFixture Magento/ConfigurableProduct/_files/configurable_products.php + */ + public function testVisualSwatchDataValues() + { + $productSku = 'configurable'; + $imageName = '/visual_swatch_attribute_option_type_image.jpg'; + $color = '#000000'; + $query = <<<QUERY +{ + products(filter: {sku: {eq: "$productSku"}}) { + items { + ... on ConfigurableProduct{ + configurable_options{ + values { + swatch_data{ + value + ... on ImageSwatchData { + thumbnail + } + } + } + } + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + + $this->assertArrayHasKey('products', $response); + $this->assertArrayHasKey('items', $response['products']); + $this->assertArrayHasKey(0, $response['products']['items']); + + $product = $response['products']['items'][0]; + $this->assertArrayHasKey('configurable_options', $product); + $this->assertArrayHasKey(0, $product['configurable_options']); + + $option = $product['configurable_options'][0]; + $this->assertArrayHasKey('values', $option); + $this->assertEquals($color, $option['values'][0]['swatch_data']['value']); + $this->assertContains( + $option['values'][1]['swatch_data']['value'], + $this->swatchMediaHelper->getSwatchAttributeImage(Swatch::SWATCH_IMAGE_NAME, $imageName) + ); + $this->assertEquals( + $option['values'][1]['swatch_data']['thumbnail'], + $this->swatchMediaHelper->getSwatchAttributeImage(Swatch::SWATCH_THUMBNAIL_NAME, $imageName) + ); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Tax/ProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Tax/ProductViewTest.php index 1dc5a813de2b8..461b5673235dd 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Tax/ProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Tax/ProductViewTest.php @@ -9,7 +9,6 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Framework\EntityManager\MetadataPool; use Magento\TestFramework\ObjectManager; use Magento\TestFramework\TestCase\GraphQlAbstract; use Magento\Store\Model\StoreManagerInterface; @@ -208,11 +207,6 @@ public function testQueryAllFieldsSimpleProduct() /** @var \Magento\Catalog\Model\Product $product */ $product = $this->productRepository->get($productSku, false, null, true); - /** @var MetadataPool $metadataPool */ - $metadataPool = ObjectManager::getInstance()->get(MetadataPool::class); - $product->setId( - $product->getData($metadataPool->getMetadata(ProductInterface::class)->getLinkField()) - ); $this->assertArrayHasKey('products', $response); $this->assertArrayHasKey('items', $response['products']); $this->assertEquals(1, count($response['products']['items'])); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistTest.php new file mode 100644 index 0000000000000..fbd9c53faf7f5 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistTest.php @@ -0,0 +1,134 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Wishlist; + +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; +use Magento\Wishlist\Model\Item; +use Magento\Wishlist\Model\ResourceModel\Wishlist\CollectionFactory; + +class CustomerWishlistTest extends GraphQlAbstract +{ + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + /** + * @var CollectionFactory + */ + private $wishlistCollectionFactory; + + protected function setUp() + { + $this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class); + $this->wishlistCollectionFactory = Bootstrap::getObjectManager()->get(CollectionFactory::class); + } + + /** + * @magentoApiDataFixture Magento/Wishlist/_files/wishlist.php + */ + public function testCustomerWishlist(): void + { + /** @var \Magento\Wishlist\Model\Wishlist $wishlist */ + $collection = $this->wishlistCollectionFactory->create()->filterByCustomerId(1); + + /** @var Item $wishlistItem */ + $wishlistItem = $collection->getFirstItem(); + $query = + <<<QUERY +{ + customer { + wishlist { + id + items_count + sharing_code + updated_at + items { + product { + sku + } + } + } + } +} +QUERY; + + $response = $this->graphQlQuery( + $query, + [], + '', + $this->getCustomerAuthHeaders('customer@example.com', 'password') + ); + $this->assertEquals((string)$wishlistItem->getId(), $response['customer']['wishlist']['id']); + $this->assertEquals($wishlistItem->getItemsCount(), $response['customer']['wishlist']['items_count']); + $this->assertEquals($wishlistItem->getSharingCode(), $response['customer']['wishlist']['sharing_code']); + $this->assertEquals($wishlistItem->getUpdatedAt(), $response['customer']['wishlist']['updated_at']); + $this->assertEquals('simple', $response['customer']['wishlist']['items'][0]['product']['sku']); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + */ + public function testCustomerAlwaysHasWishlist(): void + { + $query = + <<<QUERY +{ + customer { + wishlist { + id + } + } +} +QUERY; + + $response = $this->graphQlQuery( + $query, + [], + '', + $this->getCustomerAuthHeaders('customer@example.com', 'password') + ); + + $this->assertNotEmpty($response['customer']['wishlist']['id']); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage The current customer isn't authorized. + */ + public function testGuestCannotGetWishlist() + { + $query = + <<<QUERY +{ + customer { + wishlist { + items_count + sharing_code + updated_at + } + } +} +QUERY; + $this->graphQlQuery($query); + } + + /** + * @param string $email + * @param string $password + * @return array + * @throws \Magento\Framework\Exception\AuthenticationException + */ + private function getCustomerAuthHeaders(string $email, string $password): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password); + return ['Authorization' => 'Bearer ' . $customerToken]; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartManagementTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartManagementTest.php index 6d585561ae3a9..08821b08ede5e 100644 --- a/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartManagementTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartManagementTest.php @@ -310,21 +310,20 @@ public function testAssignCustomerThrowsExceptionIfCartIsAssignedToDifferentStor } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php * @magentoApiDataFixture Magento/Sales/_files/quote.php - * @expectedException \Exception */ - public function testAssignCustomerThrowsExceptionIfCustomerAlreadyHasActiveCart() + public function testAssignCustomerCartMerged() { /** @var $customer \Magento\Customer\Model\Customer */ $customer = $this->objectManager->create(\Magento\Customer\Model\Customer::class)->load(1); // Customer has a quote with reserved order ID test_order_1 (see fixture) /** @var $customerQuote \Magento\Quote\Model\Quote */ $customerQuote = $this->objectManager->create(\Magento\Quote\Model\Quote::class) - ->load('test_order_1', 'reserved_order_id'); - $customerQuote->setIsActive(1)->save(); + ->load('test_order_item_with_items', 'reserved_order_id'); /** @var $quote \Magento\Quote\Model\Quote */ $quote = $this->objectManager->create(\Magento\Quote\Model\Quote::class)->load('test01', 'reserved_order_id'); + $expectedQuoteItemsQty = $customerQuote->getItemsQty() + $quote->getItemsQty(); $cartId = $quote->getId(); $customerId = $customer->getId(); @@ -346,11 +345,13 @@ public function testAssignCustomerThrowsExceptionIfCustomerAlreadyHasActiveCart( 'customerId' => $customerId, 'storeId' => 1, ]; - $this->_webApiCall($serviceInfo, $requestData); + $this->assertTrue($this->_webApiCall($serviceInfo, $requestData)); - $this->expectExceptionMessage( - "The customer can't be assigned to the cart because the customer already has an active cart." - ); + $mergedQuote = $this->objectManager + ->create(\Magento\Quote\Model\Quote::class) + ->load('test01', 'reserved_order_id'); + + $this->assertEquals($expectedQuoteItemsQty, $mergedQuote->getItemsQty()); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartManagementTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartManagementTest.php index bbd1e59f83f90..120781e674d47 100644 --- a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartManagementTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartManagementTest.php @@ -231,21 +231,20 @@ public function testAssignCustomerThrowsExceptionIfTargetCartIsNotAnonymous() } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php * @magentoApiDataFixture Magento/Sales/_files/quote.php - * @expectedException \Exception */ - public function testAssignCustomerThrowsExceptionIfCustomerAlreadyHasActiveCart() + public function testAssignCustomerCartMerged() { /** @var $customer \Magento\Customer\Model\Customer */ $customer = $this->objectManager->create(\Magento\Customer\Model\Customer::class)->load(1); // Customer has a quote with reserved order ID test_order_1 (see fixture) /** @var $customerQuote \Magento\Quote\Model\Quote */ $customerQuote = $this->objectManager->create(\Magento\Quote\Model\Quote::class) - ->load('test_order_1', 'reserved_order_id'); - $customerQuote->setIsActive(1)->save(); + ->load('test_order_item_with_items', 'reserved_order_id'); /** @var $quote \Magento\Quote\Model\Quote */ $quote = $this->objectManager->create(\Magento\Quote\Model\Quote::class)->load('test01', 'reserved_order_id'); + $expectedQuoteItemsQty = $customerQuote->getItemsQty() + $quote->getItemsQty(); $cartId = $quote->getId(); @@ -284,11 +283,12 @@ public function testAssignCustomerThrowsExceptionIfCustomerAlreadyHasActiveCart( 'customerId' => $customerId, 'storeId' => 1, ]; - $this->_webApiCall($serviceInfo, $requestData); + $this->assertTrue($this->_webApiCall($serviceInfo, $requestData)); + $mergedQuote = $this->objectManager + ->create(\Magento\Quote\Model\Quote::class) + ->load('test01', 'reserved_order_id'); - $this->expectExceptionMessage( - "The customer can't be assigned to the cart because the customer already has an active cart." - ); + $this->assertEquals($expectedQuoteItemsQty, $mergedQuote->getItemsQty()); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentAddTrackTest.php b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentAddTrackTest.php index 054f91a295fd9..639adb8da4624 100644 --- a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentAddTrackTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentAddTrackTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Service\V1; use Magento\Framework\Webapi\Rest\Request; @@ -18,17 +20,17 @@ class ShipmentAddTrackTest extends WebapiAbstract { /** - * Service read name + * Read name of service */ const SERVICE_READ_NAME = 'salesShipmentTrackRepositoryV1'; /** - * Service version + * Version of service */ const SERVICE_VERSION = 'V1'; /** - * Shipment increment id + * Increment id for shipment */ const SHIPMENT_INCREMENT_ID = '100000001'; @@ -74,6 +76,52 @@ public function testShipmentAddTrack() self::assertNotEmpty($result[ShipmentTrackInterface::ENTITY_ID]); self::assertEquals($shipment->getId(), $result[ShipmentTrackInterface::PARENT_ID]); } + + /** + * Shipment Tracking throw an error if order doesn't exist. + * + * @magentoApiDataFixture Magento/Sales/_files/shipment.php + * @magentoApiDataFixture Magento/Sales/_files/order_list.php + */ + public function testShipmentTrackWithFailedOrderId() + { + /** @var \Magento\Sales\Model\Order $order */ + $orderCollection = $this->objectManager->get(\Magento\Sales\Model\ResourceModel\Order\Collection::class); + $order = $orderCollection->getLastItem(); + // Order ID from Magento/Sales/_files/order_list.php + $failedOrderId = $order->getId(); + $shipmentCollection = $this->objectManager->get(Collection::class); + /** @var \Magento\Sales\Model\Order\Shipment $shipment */ + $shipment = $shipmentCollection->getFirstItem(); + $trackData = [ + ShipmentTrackInterface::ENTITY_ID => null, + ShipmentTrackInterface::ORDER_ID => $failedOrderId, + ShipmentTrackInterface::PARENT_ID => $shipment->getId(), + ShipmentTrackInterface::WEIGHT => 20, + ShipmentTrackInterface::QTY => 5, + ShipmentTrackInterface::TRACK_NUMBER => 2, + ShipmentTrackInterface::DESCRIPTION => 'Shipment description', + ShipmentTrackInterface::TITLE => 'Shipment title', + ShipmentTrackInterface::CARRIER_CODE => Track::CUSTOM_CARRIER_CODE, + ]; + $exceptionMessage = ''; + + try { + $this->_webApiCall($this->getServiceInfo(), ['entity' => $trackData]); + } catch (\SoapFault $e) { + $exceptionMessage = $e->getMessage(); + } catch (\Exception $e) { + $errorObj = $this->processRestExceptionResult($e); + $exceptionMessage = $errorObj['message']; + } + + $this->assertContains( + $exceptionMessage, + 'Could not save the shipment tracking.', + 'SoapFault or CouldNotSaveException does not contain exception message.' + ); + } + /** * Returns details about API endpoints and services. * diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Form/AuthorizenetCc.php b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Form/AuthorizenetCc.php deleted file mode 100644 index 3cae648602531..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Form/AuthorizenetCc.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Authorizenet\Test\Block\Form; - -use Magento\Mtf\Client\Locator; -use Magento\Payment\Test\Block\Form\PaymentCc; - -/** - * Form for credit card data for Authorize.net payment method. - */ -class AuthorizenetCc extends PaymentCc -{ - /** - * Authorizenet form locators. - * - * @var array - */ - private $authorizenetForm = [ - "cc_number" => "//*[@id='authorizenet_directpost_cc_number']", - "cc_exp_month" => "//*[@id='authorizenet_directpost_expiration']", - "cc_exp_year" => "//*[@id='authorizenet_directpost_expiration_yr']", - "cc_cid" => "//*[@id='authorizenet_directpost_cc_cid']", - ]; - - /** - * Get Filled CC Number. - * - * @return string - */ - public function getCCNumber() - { - return $this->_rootElement->find($this->authorizenetForm['cc_number'], Locator::SELECTOR_XPATH)->getValue(); - } - - /** - * Get Filled CC Number. - * - * @return string - */ - public function getExpMonth() - { - return $this->_rootElement->find($this->authorizenetForm['cc_exp_month'], Locator::SELECTOR_XPATH)->getValue(); - } - - /** - * Get Expiration Year - * - * @return string - */ - public function getExpYear() - { - return $this->_rootElement->find($this->authorizenetForm['cc_exp_year'], Locator::SELECTOR_XPATH)->getValue(); - } - - /** - * Get CID - * - * @return string - */ - public function getCid() - { - return $this->_rootElement->find($this->authorizenetForm['cc_cid'], Locator::SELECTOR_XPATH)->getValue(); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Form/AuthorizenetCc.xml b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Form/AuthorizenetCc.xml deleted file mode 100644 index dbb9b4707f1e7..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Form/AuthorizenetCc.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" ?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<mapping strict="0"> - <fields> - <cc_number> - <selector>#authorizenet_directpost_cc_number</selector> - </cc_number> - <cc_exp_month> - <selector>#authorizenet_directpost_expiration</selector> - </cc_exp_month> - <cc_exp_year> - <selector>#authorizenet_directpost_expiration_yr</selector> - </cc_exp_year> - <cc_cid> - <selector>#authorizenet_directpost_cc_cid</selector> - </cc_cid> - </fields> -</mapping> diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Sandbox/AuthorizenetLogin.php b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Sandbox/AuthorizenetLogin.php deleted file mode 100644 index 236237361d61e..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Sandbox/AuthorizenetLogin.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Test\Block\Sandbox; - -use Magento\Mtf\Block\Form; -use Magento\Mtf\Client\Element\SimpleElement; -use Magento\Mtf\Fixture\FixtureInterface; - -/** - * Login form. - */ -class AuthorizenetLogin extends Form -{ - /** - * Login button on Authorize.Net Sandbox. - * - * @var string - */ - private $loginButton = '[type=submit]'; - - /** - * Switch to the form frame and fill form. {@inheritdoc} - * - * @param FixtureInterface $fixture - * @param SimpleElement|null $element - * @return $this - */ - public function fill(FixtureInterface $fixture, SimpleElement $element = null) - { - parent::fill($fixture, $element); - return $this; - } - - /** - * Login to Authorize.Net Sandbox. - * - * @return void - */ - public function login() - { - $this->_rootElement->find($this->loginButton)->click(); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Sandbox/AuthorizenetLogin.xml b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Sandbox/AuthorizenetLogin.xml deleted file mode 100644 index 81159d8cf8451..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Sandbox/AuthorizenetLogin.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" ?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<mapping strict="1"> - <fields> - <login_id> - <selector>[name=MerchantLogin]</selector> - </login_id> - <password> - <selector>[name=Password]</selector> - </password> - </fields> -</mapping> diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Sandbox/GetStartedModal.php b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Sandbox/GetStartedModal.php deleted file mode 100644 index f7efb023b5799..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Sandbox/GetStartedModal.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Test\Block\Sandbox; - -use Magento\Mtf\Block\Block; - -/** - * 'Get started accepting payments' modal window on Authorize.Net sandbox. - */ -class GetStartedModal extends Block -{ - /** - * 'Got It' button selector. - * This button is located in notification window which may appear immediately after login. - * - * @var string - */ - private $gotItButton = '#btnGetStartedGotIt'; - - /** - * Accept notification if it appears after login. - * - * @return $this - */ - public function acceptNotification() - { - $element = $this->browser->find($this->gotItButton); - if ($element->isVisible()) { - $element->click(); - } - return $this; - } -} diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Sandbox/Menu.php b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Sandbox/Menu.php deleted file mode 100644 index e4cdc5b8d1a29..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Sandbox/Menu.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Test\Block\Sandbox; - -use Magento\Mtf\Block\Block; -use Magento\Mtf\Client\Locator; - -/** - * Menu block on Authorize.Net sandbox. - */ -class Menu extends Block -{ - /** - * Search menu button selector. - * - * @var string - */ - private $searchMenuButton = './/div[@id="topNav"]//a[contains(@href,"search")]'; - - /** - * Open 'Search' menu item. - * - * @return $this - */ - public function openSearchMenu() - { - $this->_rootElement->find($this->searchMenuButton, Locator::SELECTOR_XPATH)->click(); - return $this; - } -} diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Sandbox/SearchForm.php b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Sandbox/SearchForm.php deleted file mode 100644 index cceda81e61a36..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Sandbox/SearchForm.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Test\Block\Sandbox; - -use Magento\Mtf\Block\Form; - -/** - * Transactions search form. - */ -class SearchForm extends Form -{ - /** - * Search button selector. - * - * @var string - */ - private $searchButton = '[type=submit]'; - - /** - * Search for transactions. - * - * @return void - */ - public function search() - { - $this->browser->find($this->searchButton)->click(); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Sandbox/SearchForm.xml b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Sandbox/SearchForm.xml deleted file mode 100644 index 993d3b4fda748..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Sandbox/SearchForm.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" ?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<mapping strict="1"> - <fields> - <settlement_date_from> - <selector>[name=StartBatch]</selector> - <input>select</input> - </settlement_date_from> - <settlement_date_to> - <selector>[name=EndBatch]</selector> - <input>select</input> - </settlement_date_to> - </fields> -</mapping> diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Sandbox/TransactionsGrid.php b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Sandbox/TransactionsGrid.php deleted file mode 100644 index c9b6ab3b8152c..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Block/Sandbox/TransactionsGrid.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Test\Block\Sandbox; - -use Magento\Mtf\Block\Block; -use Magento\Mtf\Client\Locator; - -/** - * Transactions grid block. - */ -class TransactionsGrid extends Block -{ - /** - * Transaction selector. - * - * @var string - */ - private $transaction = './/a[contains(text(), "%s")]'; - - /** - * 'Approve' button selector. - * - * @var string - */ - private $transactionApprove = '(//input[@id="btnConfirmApprove"])[1]'; - - /** - * Confirmation window 'OK' button selector. - * - * @var string - */ - private $transactionApprovalConfirm = '#btnConfirmYes'; - - /** - * Find transaction in grid and open it. - * - * @param string $transactionId - * @return $this - */ - public function openTransaction($transactionId) - { - $this->_rootElement->find(sprintf($this->transaction, $transactionId), Locator::SELECTOR_XPATH)->click(); - return $this; - } - - /** - * Approve selected transaction. - * - * @return $this - */ - public function approveTransaction() - { - $this->_rootElement->find($this->transactionApprove, Locator::SELECTOR_XPATH)->click(); - $this->confirmTransactionApproval(); - return $this; - } - - /** - * Confirm approval of selected transaction. - * - * @return void - */ - private function confirmTransactionApproval() - { - $this->browser->find($this->transactionApprovalConfirm)->click(); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Constraint/AssertCreditCardNumberOnOnePageCheckout.php b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Constraint/AssertCreditCardNumberOnOnePageCheckout.php deleted file mode 100644 index cd1dfce621806..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Constraint/AssertCreditCardNumberOnOnePageCheckout.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Authorizenet\Test\Constraint; - -use Magento\Checkout\Test\Page\CheckoutOnepage; -use Magento\Mtf\Constraint\AbstractConstraint; -use Magento\Payment\Test\Fixture\CreditCard; - -/** - * Assert credit card fields have set values from fixture. - */ -class AssertCreditCardNumberOnOnePageCheckout extends AbstractConstraint -{ - /** - * Assert payment form values did persist from fixture after checkout blocks refresh - * - * @param CheckoutOnepage $checkoutOnepage - * @param CreditCard $creditCard - * @return void - */ - public function processAssert(CheckoutOnepage $checkoutOnepage, CreditCard $creditCard) - { - \PHPUnit\Framework\Assert::assertEquals( - $creditCard->getCcNumber(), - $checkoutOnepage->getAuthorizenetBlock()->getCCNumber(), - 'Credit card data did persist with the values from fixture' - ); - } - - /** - * Returns string representation of successful assertion - * - * @return string - */ - public function toString() - { - return 'Credit card data did persist with the values from fixture.'; - } -} diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Fixture/AuthorizenetSandboxCustomer.xml b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Fixture/AuthorizenetSandboxCustomer.xml deleted file mode 100644 index 89e1aca8c3b78..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Fixture/AuthorizenetSandboxCustomer.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/fixture.xsd"> - <fixture name="authorizenet_sandbox_customer" - module="Magento_Authorizenet" - type="virtual" - repository_class="Magento\Authorizenet\Test\Repository\AuthorizenetSandboxCustomer" - class="Magento\Authorizenet\Test\Fixture\AuthorizenetSandboxCustomer"> - <field name="login_id" /> - <field name="password" /> - </fixture> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Fixture/TransactionSearch.xml b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Fixture/TransactionSearch.xml deleted file mode 100644 index 6d644d2bd3996..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Fixture/TransactionSearch.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/fixture.xsd"> - <fixture name="authorizenet_trasnsaction_search" - module="Magento_Authorizenet" - type="virtual" - repository_class="Magento\Authorizenet\Test\Repository\TransactionSearch" - class="Magento\Authorizenet\Test\Fixture\TransactionSearch"> - <field name="settlement_date_from" /> - <field name="settlement_date_to" /> - </fixture> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Page/CheckoutOnepage.xml b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Page/CheckoutOnepage.xml deleted file mode 100644 index 34e8a1eea62f3..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Page/CheckoutOnepage.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/pages.xsd"> - <page name="CheckoutOnepage" mca="checkout/index"> - <block name="authorizenetBlock" class="Magento\Authorizenet\Test\Block\Form\AuthorizenetCc" locator="#payment_form_authorizenet_directpost" strategy="css selector"/> - <block name="paymentBlock"> - <render name="authorizenet" class="Magento\Authorizenet\Test\Block\Form\AuthorizenetCc" /> - </block> - </page> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Page/Sandbox/Main.xml b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Page/Sandbox/Main.xml deleted file mode 100644 index da9fd5df60ceb..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Page/Sandbox/Main.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/pages.xsd"> - <page name="Main" area="Sandbox" mca="https://sandbox.authorize.net/" module="Magento_Authorizenet"> - <block name="loginForm" class="Magento\Authorizenet\Test\Block\Sandbox\AuthorizenetLogin" locator="#ctl00_MainContent_welcomeText" strategy="css selector" /> - <block name="menuBlock" class="Magento\Authorizenet\Test\Block\Sandbox\Menu" locator="body" strategy="css selector" /> - <block name="modalBlock" class="Magento\Authorizenet\Test\Block\Sandbox\GetStartedModal" locator="body" strategy="css selector" /> - <block name="searchForm" class="Magento\Authorizenet\Test\Block\Sandbox\SearchForm" locator="#Main" strategy="css selector" /> - <block name="transactionsGridBlock" class="Magento\Authorizenet\Test\Block\Sandbox\TransactionsGrid" locator="#Main" strategy="css selector" /> - </page> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Repository/AuthorizenetSandboxCustomer.xml b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Repository/AuthorizenetSandboxCustomer.xml deleted file mode 100644 index 454da0ef6cecd..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Repository/AuthorizenetSandboxCustomer.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" ?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/Magento/Mtf/Repository/etc/repository.xsd"> - <repository class="Magento\Authorizenet\Test\Repository\AuthorizenetSandboxCustomer"> - <dataset name="sandbox_fraud_hold_review"> - <field name="login_id" xsi:type="string">AUTHORIZENET_SANDBOX_LOGIN_ID</field> - <field name="password" xsi:type="string">AUTHORIZENET_SANDBOX_PASSWORD</field> - </dataset> - </repository> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Repository/ConfigData.xml b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Repository/ConfigData.xml deleted file mode 100644 index c759b537a191d..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Repository/ConfigData.xml +++ /dev/null @@ -1,163 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/Magento/Mtf/Repository/etc/repository.xsd"> - <repository class="Magento\Config\Test\Repository\ConfigData"> - <dataset name="authorizenet"> - <field name="payment/authorizenet_directpost/active" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string">Yes</item> - <item name="value" xsi:type="number">1</item> - </field> - <field name="payment/authorizenet_directpost/login" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string"/> - <item name="value" xsi:type="string">%payment_authorizenet_login%</item> - </field> - <field name="payment/authorizenet_directpost/trans_key" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string"/> - <item name="value" xsi:type="string">%payment_authorizenet_trans_key%</item> - </field> - <field name="payment/authorizenet_directpost/trans_md5" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string"/> - <item name="value" xsi:type="string">%payment_authorizenet_trans_md5%</item> - </field> - <field name="payment/authorizenet_directpost/test" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string">No</item> - <item name="value" xsi:type="number">0</item> - </field> - <field name="payment/authorizenet_directpost/cgi_url" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string"/> - <item name="value" xsi:type="string">https://test.authorize.net/gateway/transact.dll</item> - </field> - <field name="payment/authorizenet_directpost/cgi_url_td" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string"/> - <item name="value" xsi:type="string">https://apitest.authorize.net/xml/v1/request.api</item> - </field> - <field name="payment/authorizenet_directpost/debug" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string">Yes</item> - <item name="value" xsi:type="number">1</item> - </field> - <field name="payment/authorizenet_directpost/useccv" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string">Yes</item> - <item name="value" xsi:type="number">1</item> - </field> - </dataset> - <dataset name="authorizenet_rollback"> - <field name="payment/authorizenet_directpost/active" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string">No</item> - <item name="value" xsi:type="number">0</item> - </field> - </dataset> - <dataset name="authorizenet_fraud_review"> - <field name="payment/authorizenet_directpost/active" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string">Yes</item> - <item name="value" xsi:type="number">1</item> - </field> - <field name="payment/authorizenet_directpost/login" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string"/> - <item name="value" xsi:type="string">%authorizenet_fraud_review_login%</item> - </field> - <field name="payment/authorizenet_directpost/trans_key" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string"/> - <item name="value" xsi:type="string">%authorizenet_fraud_review_trans_key%</item> - </field> - <field name="payment/authorizenet_directpost/trans_md5" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string"/> - <item name="value" xsi:type="string">%authorizenet_fraud_review_md5%</item> - </field> - <field name="payment/authorizenet_directpost/test" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string">No</item> - <item name="value" xsi:type="number">0</item> - </field> - <field name="payment/authorizenet_directpost/cgi_url" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string"/> - <item name="value" xsi:type="string">https://test.authorize.net/gateway/transact.dll</item> - </field> - <field name="payment/authorizenet_directpost/cgi_url_td" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string"/> - <item name="value" xsi:type="string">https://apitest.authorize.net/xml/v1/request.api</item> - </field> - <field name="payment/authorizenet_directpost/debug" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string">Yes</item> - <item name="value" xsi:type="number">1</item> - </field> - <field name="payment/authorizenet_directpost/useccv" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string">Yes</item> - <item name="value" xsi:type="number">1</item> - </field> - </dataset> - <dataset name="authorizenet_fraud_review_rollback"> - <field name="payment/authorizenet_directpost/active" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string">No</item> - <item name="value" xsi:type="number">0</item> - </field> - </dataset> - <dataset name="authorizenet_authorize_capture"> - <field name="payment/authorizenet_directpost/payment_action" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string"/> - <item name="value" xsi:type="string">authorize_capture</item> - </field> - </dataset> - <dataset name="authorizenet_authorize_capture_rollback"> - <field name="payment/authorizenet_directpost/payment_action" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string"/> - <item name="value" xsi:type="string">authorize</item> - </field> - </dataset> - <dataset name="authorizenet_wrong_credentials"> - <field name="payment/authorizenet_directpost/trans_md5" xsi:type="array"> - <item name="scope" xsi:type="string">payment</item> - <item name="scope_id" xsi:type="number">1</item> - <item name="label" xsi:type="string"/> - <item name="value" xsi:type="string"></item> - </field> - </dataset> - </repository> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Repository/TransactionSearch.xml b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Repository/TransactionSearch.xml deleted file mode 100644 index 19b2d5eededa6..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/Repository/TransactionSearch.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" ?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/Magento/Mtf/Repository/etc/repository.xsd"> - <repository class="Magento\Authorizenet\Test\Repository\TransactionSearch"> - <dataset name="unsettled"> - <field name="settlement_date_from" xsi:type="string">Unsettled</field> - <field name="settlement_date_to" xsi:type="string">Unsettled</field> - </dataset> - </repository> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/AuthorizenetFraudCheckoutTest.php b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/AuthorizenetFraudCheckoutTest.php deleted file mode 100644 index ca0c473eb685f..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/AuthorizenetFraudCheckoutTest.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Test\TestCase; - -use Magento\Mtf\TestCase\Scenario; - -/** - * Preconditions: - * 1. Configure payment method. - * 2. Create products. - * - * Steps: - * 1. Log in Storefront. - * 2. Add products to the Shopping Cart. - * 3. Click the 'Proceed to Checkout' button. - * 4. Fill shipping information. - * 5. Select shipping method. - * 6. Select payment method. - * 7. Click 'Place Order' button. - * 8. Log in to Authorize.Net sandbox. - * 9. Accept transaction. - * 10. Log in to Magento Admin. - * 11. Open order. - * 12. Perform assertions. - * - * @group Checkout - * @ZephyrId MAGETWO-38379 - */ -class AuthorizenetFraudCheckoutTest extends Scenario -{ - /* tags */ - const TEST_TYPE = '3rd_party_test'; - /* end tags */ - - /** - * Runs one page checkout test. - * - * @return void - */ - public function test() - { - $this->executeScenario(); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/AuthorizenetFraudCheckoutTest.xml b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/AuthorizenetFraudCheckoutTest.xml deleted file mode 100644 index 9ba32a4cc7a6b..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/AuthorizenetFraudCheckoutTest.xml +++ /dev/null @@ -1,38 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> - <testCase name="Magento\Authorizenet\Test\TestCase\AuthorizenetFraudCheckoutTest" summary="Accept transaction for order placed via Authorize.Net."> - <variation name="AuthorizenetFraudCheckoutAccept" summary="Accept transaction with 'Authorize and hold for review' fraud action for order placed with 'Authorize and Capture' payment action" ticketId="MAGETWO-38379"> - <data name="products/0" xsi:type="string">catalogProductSimple::product_10_dollar</data> - <data name="customer/dataset" xsi:type="string">default</data> - <data name="shippingAddress/dataset" xsi:type="string">US_address_1_without_email</data> - <data name="checkoutMethod" xsi:type="string">login</data> - <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> - <data name="shipping/shipping_method" xsi:type="string">Fixed</data> - <data name="prices" xsi:type="array"> - <item name="grandTotal" xsi:type="string">15.00</item> - </data> - <data name="orderBeforeAccept" xsi:type="array"> - <item name="invoiceStatus" xsi:type="string">Pending</item> - <item name="orderStatus" xsi:type="string">Suspected Fraud</item> - <item name="buttonsAvailable" xsi:type="string">Back, Send Email, Get Payment Update</item> - </data> - <data name="payment/method" xsi:type="string">authorizenet_directpost</data> - <data name="creditCard/dataset" xsi:type="string">visa_default</data> - <data name="sandboxCustomer/dataset" xsi:type="string">sandbox_fraud_hold_review</data> - <data name="transactionSearch/dataset" xsi:type="string">unsettled</data> - <data name="configData" xsi:type="string">authorizenet_fraud_review, authorizenet_authorize_capture</data> - <data name="status" xsi:type="string">Processing</data> - <data name="invoiceStatus" xsi:type="string">Paid</data> - <data name="orderButtonsAvailable" xsi:type="string">Back, Send Email, Credit Memo, Hold, Ship, Reorder</data> - <data name="tag" xsi:type="string">test_type:3rd_party_test, severity:S1</data> - <constraint name="Magento\Sales\Test\Constraint\AssertInvoiceStatusInOrdersGrid" /> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderButtonsAvailable" /> - </variation> - </testCase> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/OnePageCheckoutDeclinedTest.xml b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/OnePageCheckoutDeclinedTest.xml deleted file mode 100644 index 00c1a1557e05b..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/OnePageCheckoutDeclinedTest.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> - <testCase name="Magento\Checkout\Test\TestCase\OnePageCheckoutDeclinedTest" summary="Error message during OnePageCheckout"> - <variation name="OnePageCheckoutAuthorizenetWrongCredentials" summary="Error during place order flow with Authorize.net" ticketId="MAGETWO-69995"> - <data name="products/0" xsi:type="string">catalogProductSimple::product_10_dollar</data> - <data name="customer/dataset" xsi:type="string">default</data> - <data name="shippingAddress/dataset" xsi:type="string">US_address_1_without_email</data> - <data name="checkoutMethod" xsi:type="string">login</data> - <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> - <data name="shipping/shipping_method" xsi:type="string">Fixed</data> - <data name="payment/method" xsi:type="string">authorizenet_directpost</data> - <data name="creditCard/dataset" xsi:type="string">visa_default</data> - <data name="configData" xsi:type="string">authorizenet, authorizenet_wrong_credentials</data> - <data name="expectedErrorMessage" xsi:type="string">A server error stopped your order from being placed. Please try to place your order again.</data> - <data name="tag" xsi:type="string">test_type:3rd_party_test, severity:S2</data> - <constraint name="Magento\Checkout\Test\Constraint\AssertCheckoutErrorMessage" /> - </variation> - </testCase> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/OnePageCheckoutPaymentMethodDataPersistenceWithDiscountTest.xml b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/OnePageCheckoutPaymentMethodDataPersistenceWithDiscountTest.xml deleted file mode 100644 index b979745a99b96..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/OnePageCheckoutPaymentMethodDataPersistenceWithDiscountTest.xml +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> - <testCase name="Magento\SalesRule\Test\TestCase\OnePageCheckoutPaymentMethodDataPersistenceWithDiscountTest" summary="Checkout with Authorize.net credit card on Storefront with discount applied during checkout"> - <variation name="OnePageCheckoutPaymentMethodDataPersistWithDiscountTest1" summary="Checkout with Authorize.net credit card on Storefront with discount applied during checkout" ticketId="MAGETWO-69657"> - <data name="description" xsi:type="string">Use saved for Authorize.net credit card on checkout</data> - <data name="products/0" xsi:type="string">catalogProductSimple::product_10_dollar</data> - <data name="customer/dataset" xsi:type="string">default</data> - <data name="salesRule" xsi:type="string">active_sales_rule_for_all_groups</data> - <data name="shippingAddress/dataset" xsi:type="string">US_address_1_without_email</data> - <data name="checkoutMethod" xsi:type="string">login</data> - <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> - <data name="shipping/shipping_method" xsi:type="string">Fixed</data> - <data name="payment/method" xsi:type="string">authorizenet_directpost</data> - <data name="paymentForm" xsi:type="string">default</data> - <data name="creditCard/dataset" xsi:type="string">visa_default</data> - <data name="creditCard/data/payment_code" xsi:type="string">authorizenet</data> - <data name="prices" xsi:type="array"> - <item name="grandTotal" xsi:type="string">10.00</item> - </data> - <data name="creditCardSave" xsi:type="string">Yes</data> - <data name="configData" xsi:type="string">authorizenet</data> - <data name="status" xsi:type="string">Processing</data> - <data name="tag" xsi:type="string">severity:S1</data> - <constraint name="Magento\Authorizenet\Test\Constraint\AssertCreditCardNumberOnOnePageCheckout" /> - </variation> - </testCase> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/OnePageCheckoutTest.xml b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/OnePageCheckoutTest.xml deleted file mode 100644 index a7eaaada1be83..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/OnePageCheckoutTest.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> - <testCase name="Magento\Checkout\Test\TestCase\OnePageCheckoutTest" summary="One page check out with Authorize.Net Direct Post payment method."> - <variation name="OnePageCheckoutAuthorizenetTestVariation1" summary="CheckOut with Authorize.Net Direct Post" ticketId="MAGETWO-59170"> - <data name="products/0" xsi:type="string">catalogProductSimple::product_10_dollar</data> - <data name="customer/dataset" xsi:type="string">default</data> - <data name="checkoutMethod" xsi:type="string">login</data> - <data name="shippingAddress/dataset" xsi:type="string">US_address_1_without_email</data> - <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> - <data name="shipping/shipping_method" xsi:type="string">Fixed</data> - <data name="prices" xsi:type="array"> - <item name="grandTotal" xsi:type="string">15.00</item> - </data> - <data name="payment/method" xsi:type="string">authorizenet_directpost</data> - <data name="creditCard/dataset" xsi:type="string">visa_default</data> - <data name="configData" xsi:type="string">authorizenet</data> - <data name="status" xsi:type="string">Processing</data> - <data name="tag" xsi:type="string">test_type:3rd_party_test, severity:S0</data> - <constraint name="Magento\Checkout\Test\Constraint\AssertOrderSuccessPlacedMessage" /> - <constraint name="Magento\Checkout\Test\Constraint\AssertMinicartEmpty" /> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal" /> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderStatusIsCorrect" /> - <constraint name="Magento\Sales\Test\Constraint\AssertAuthorizationInCommentsHistory" /> - </variation> - </testCase> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/ReorderOrderEntityTest.xml b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/ReorderOrderEntityTest.xml deleted file mode 100644 index b0f4856e28d0d..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestCase/ReorderOrderEntityTest.xml +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - --> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> - <testCase name="Magento\Sales\Test\TestCase\ReorderOrderEntityTest" summary="Reorder Order from Admin using Authorize.Net Direct Post payment method." ticketId="MAGETWO-69939"> - <variation name="ReorderOrderEntityAuthorizenetTestVariation1"> - <data name="tag" xsi:type="string">test_type:3rd_party_test, severity:S2</data> - <data name="description" xsi:type="string">Reorder placed order using Authorize.Net payment method (update products, billing address).</data> - <data name="order/dataset" xsi:type="string">default</data> - <data name="customer/dataset" xsi:type="string">default</data> - <data name="billingAddress/dataset" xsi:type="string">US_address_1_without_email</data> - <data name="shipping/shipping_service" xsi:type="string">Flat Rate</data> - <data name="shipping/shipping_method" xsi:type="string">Fixed</data> - <data name="prices" xsi:type="array"> - <item name="grandTotal" xsi:type="string">565.00</item> - </data> - <data name="payment/method" xsi:type="string">authorizenet_directpost</data> - <data name="configData" xsi:type="string">authorizenet</data> - <data name="creditCard/dataset" xsi:type="string">visa_default_admin</data> - <data name="previousOrderStatus" xsi:type="string">Processing</data> - <data name="status" xsi:type="string">Processing</data> - <data name="orderButtonsAvailable" xsi:type="string">Back, Reorder, Cancel, Send Email, Hold, Invoice, Ship, Edit</data> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderSuccessCreateMessage" /> - <constraint name="Magento\Sales\Test\Constraint\AssertReorderStatusIsCorrect" /> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderButtonsAvailable" /> - <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal" /> - </variation> - </testCase> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestStep/AcceptTransactionOnAuthorizenetStep.php b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestStep/AcceptTransactionOnAuthorizenetStep.php deleted file mode 100644 index 0ff6bb486cf81..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/TestStep/AcceptTransactionOnAuthorizenetStep.php +++ /dev/null @@ -1,173 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Authorizenet\Test\TestStep; - -use Magento\Authorizenet\Test\Fixture\AuthorizenetSandboxCustomer; -use Magento\Authorizenet\Test\Fixture\TransactionSearch; -use Magento\Authorizenet\Test\Page\Sandbox\Main; -use Magento\Mtf\Client\BrowserInterface; -use Magento\Mtf\TestStep\TestStepInterface; -use Magento\Sales\Test\Constraint\AssertInvoiceStatusInOrdersGrid; -use Magento\Sales\Test\Constraint\AssertOrderButtonsAvailable; -use Magento\Sales\Test\Page\Adminhtml\OrderIndex; -use Magento\Sales\Test\Page\Adminhtml\SalesOrderView; - -/** - * Accept transaction on Authorize.Net sandbox. - */ -class AcceptTransactionOnAuthorizenetStep implements TestStepInterface -{ - /** - * Authorize.Net Sandbox customer fixture. - * - * @var AuthorizenetSandboxCustomer - */ - private $sandboxCustomer; - - /** - * Authorize.Net Sandbox account main page. - * - * @var Main - */ - private $main; - - /** - * Sales Order View page. - * - * @var SalesOrderView - */ - private $salesOrderView; - - /** - * Order Index page. - * - * @var OrderIndex - */ - private $salesOrder; - - /** - * Order id. - * - * @var string - */ - private $orderId; - - /** - * Assert invoice status on order page in Admin. - * - * @var AssertInvoiceStatusInOrdersGrid - */ - private $assertInvoiceStatusInOrdersGrid; - - /** - * Unsettled order data. - * - * @var array - */ - private $orderBeforeAccept; - - /** - * Assert that specified in data set buttons exist on order page in Admin. - * - * @var AssertOrderButtonsAvailable - */ - private $assertOrderButtonsAvailable; - - /** - * Client Browser instance. - * - * @var BrowserInterface - */ - private $browser; - - /** - * Form frame selector. - * - * @var string - */ - private $frame = 'frameset > frame'; - - /** - * Transaction search fixture. - * - * @var TransactionSearch - */ - private $transactionSearch; - - /** - * @param AuthorizenetSandboxCustomer $sandboxCustomer - * @param TransactionSearch $transactionSearch - * @param Main $main - * @param SalesOrderView $salesOrderView - * @param OrderIndex $salesOrder - * @param AssertInvoiceStatusInOrdersGrid $assertInvoiceStatusInOrdersGrid - * @param AssertOrderButtonsAvailable $assertOrderButtonsAvailable - * @param BrowserInterface $browser - * @param array $orderBeforeAccept - * @param string $orderId - * - * @SuppressWarnings(PHPMD.ExcessiveParameterList) - */ - public function __construct( - AuthorizenetSandboxCustomer $sandboxCustomer, - TransactionSearch $transactionSearch, - Main $main, - SalesOrderView $salesOrderView, - OrderIndex $salesOrder, - AssertInvoiceStatusInOrdersGrid $assertInvoiceStatusInOrdersGrid, - AssertOrderButtonsAvailable $assertOrderButtonsAvailable, - BrowserInterface $browser, - array $orderBeforeAccept, - $orderId - ) { - $this->sandboxCustomer = $sandboxCustomer; - $this->transactionSearch = $transactionSearch; - $this->main = $main; - $this->salesOrderView = $salesOrderView; - $this->salesOrder = $salesOrder; - $this->assertInvoiceStatusInOrdersGrid = $assertInvoiceStatusInOrdersGrid; - $this->assertOrderButtonsAvailable = $assertOrderButtonsAvailable; - $this->browser = $browser; - $this->orderBeforeAccept = $orderBeforeAccept; - $this->orderId = $orderId; - } - - /** - * Accept transaction on sandbox.authorize.net account. - * - * @return void - * @throws \Exception - */ - public function run() - { - $this->assertInvoiceStatusInOrdersGrid->processAssert( - $this->salesOrderView, - $this->orderBeforeAccept['invoiceStatus'], - $this->orderId - ); - $this->assertOrderButtonsAvailable->processAssert( - $this->salesOrderView, - $this->orderBeforeAccept['buttonsAvailable'] - ); - $this->salesOrder->open(); - $this->salesOrder->getSalesOrderGrid()->searchAndOpen(['id' => $this->orderId]); - - /** @var \Magento\Sales\Test\Block\Adminhtml\Order\View\Tab\Info $infoTab */ - $infoTab = $this->salesOrderView->getOrderForm()->openTab('info')->getTab('info'); - $latestComment = $infoTab->getCommentsHistoryBlock()->getLatestComment(); - if (!preg_match('/"(\d+)"/', $latestComment['comment'], $matches)) { - throw new \Exception('Comment with transaction id cannot be found.'); - } - $transactionId = $matches[1]; - $this->main->open(); - $this->browser->switchToFrame($this->browser->find($this->frame)->getLocator()); - $this->main->getLoginForm()->fill($this->sandboxCustomer)->login(); - $this->main->getModalBlock()->acceptNotification(); - $this->main->getMenuBlock()->openSearchMenu(); - $this->main->getSearchForm()->fill($this->transactionSearch)->search(); - $this->main->getTransactionsGridBlock()->openTransaction($transactionId)->approveTransaction(); - } -} diff --git a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/etc/testcase.xml b/dev/tests/functional/tests/app/Magento/Authorizenet/Test/etc/testcase.xml deleted file mode 100644 index 5ce1f811b4fbb..0000000000000 --- a/dev/tests/functional/tests/app/Magento/Authorizenet/Test/etc/testcase.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0"?> -<!-- -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/Magento/Mtf/TestCase/etc/testcase.xsd"> - <scenario name="AuthorizenetFraudCheckoutTest" firstStep="setupConfiguration"> - <step name="setupConfiguration" module="Magento_Config" next="createProducts" /> - <step name="createProducts" module="Magento_Catalog" next="addProductsToTheCart" /> - <step name="addProductsToTheCart" module="Magento_Checkout" next="proceedToCheckout" /> - <step name="proceedToCheckout" module="Magento_Checkout" next="createCustomer" /> - <step name="createCustomer" module="Magento_Customer" next="selectCheckoutMethod" /> - <step name="selectCheckoutMethod" module="Magento_Checkout" next="fillShippingAddress" /> - <step name="fillShippingAddress" module="Magento_Checkout" next="fillShippingMethod" /> - <step name="fillShippingMethod" module="Magento_Checkout" next="selectPaymentMethod" /> - <step name="selectPaymentMethod" module="Magento_Checkout" next="fillBillingInformation" /> - <step name="fillBillingInformation" module="Magento_Checkout" next="placeOrder" /> - <step name="placeOrder" module="Magento_Checkout" next="acceptTransactionOnAuthorizenet" /> - <step name="acceptTransactionOnAuthorizenet" module="Magento_Authorizenet" next="getPaymentUpdate" /> - <step name="getPaymentUpdate" module="Magento_Sales" /> - </scenario> -</config> diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Fixture/GlobalSearch/Query.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Fixture/GlobalSearch/Query.php index a386584a2817b..5e5034b8ec099 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Fixture/GlobalSearch/Query.php +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Fixture/GlobalSearch/Query.php @@ -32,7 +32,7 @@ public function __construct(FixtureFactory $fixtureFactory, $data, array $params { $this->params = $params; $explodedData = explode('::', $data); - switch (sizeof($explodedData)) { + switch (count($explodedData)) { case 1: $this->data = $explodedData[0]; break; diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ExpireSessionTest.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ExpireSessionTest.xml index e67ceb99f2eef..195d1330ae78a 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ExpireSessionTest.xml +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/ExpireSessionTest.xml @@ -8,12 +8,14 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Backend\Test\TestCase\ExpireSessionTest" summary="Admin Session Expire" ticketId="MAGETWO-47723"> <variation name="ExpireSessionTestVariation1" summary="Check that session expires according with time settings applied in configuration" ticketId="MAGETWO-47722"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="configData" xsi:type="string">default_cookie_lifetime_60_seconds</data> <data name="customer/dataset" xsi:type="string">default</data> <data name="sessionLifetimeInSeconds" xsi:type="number">60</data> <constraint name="Magento\Cms\Test\Constraint\AssertAuthorizationLinkIsVisibleOnStoreFront" /> </variation> <variation name="ExpireAdminSession" summary="Expire Admin Session" ticketId="MAGETWO-47723"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="configData" xsi:type="string">admin_session_lifetime_60_seconds</data> <data name="sessionLifetimeInSeconds" xsi:type="number">60</data> <constraint name="Magento\Backend\Test\Constraint\AssertAdminLoginPageIsAvailable" /> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Category.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Category.xml index fafd9842cc749..c5036555b6635 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Category.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Category.xml @@ -49,7 +49,6 @@ <field name="use_parent_category_settings" is_required="" group="design" /> <field name="theme" is_required="" group="design" /> <field name="layout" is_required="" group="design" /> - <field name="layout_update_xml" is_required="" group="design" /> <field name="apply_design_to_products" is_required="" group="design" /> <field name="schedule_update_from" is_required="" group="schedule_design_update" /> <field name="schedule_update_to" is_required="" group="schedule_design_update" /> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml index 69093b8adb8db..5ea1b692e3eb9 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml @@ -37,7 +37,6 @@ <data name="category/data/meta_keywords" xsi:type="string">custom meta keywords %isolation%</data> <data name="category/data/meta_description" xsi:type="string">Custom meta description %isolation%</data> <data name="category/data/layout" xsi:type="string">2 columns with right bar</data> - <data name="category/data/layout_update_xml" xsi:type="string"><referenceContainer name="catalog.leftnav" remove="true"/></data> <data name="category/data/new_theme" xsi:type="string">Magento Luma</data> <data name="category/data/apply_design_to_products" xsi:type="string">Yes</data> <data name="category/data/schedule_update_from" xsi:type="string">01/10/2014</data> @@ -80,7 +79,6 @@ <data name="category/data/meta_description" xsi:type="string">Custom meta description %isolation%</data> <data name="category/data/category_products/dataset" xsi:type="string">catalogProductSimple::default,catalogProductSimple::default</data> <data name="category/data/layout" xsi:type="string">2 columns with right bar</data> - <data name="category/data/layout_update_xml" xsi:type="string"><referenceContainer name="content.aside" remove="true"/></data> <data name="category/data/new_theme" xsi:type="string">Magento Luma</data> <data name="category/data/apply_design_to_products" xsi:type="string">Yes</data> <data name="category/data/schedule_update_from" xsi:type="string">01/10/2014</data> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ShoppingCartPagerTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ShoppingCartPagerTest.xml index 8b6b69b73b894..e03351de7a2b1 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ShoppingCartPagerTest.xml +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/ShoppingCartPagerTest.xml @@ -8,24 +8,24 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Checkout\Test\TestCase\ShoppingCartPagerTest" summary="Verify pager on Shopping Cart" ticketId="MAGETWO-63337"> <variation name="ShoppingCartPagerTestFor20ItemsPerPageAnd20Products" summary="Verify pager is NOT presented on Shopping Cart page if qty of products = 20, by default system configuration" ticketId="MAGETWO-63337"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> <constraint name="Magento\Checkout\Test\Constraint\AssertPagersNotPresentInShoppingCart"/> </variation> <variation name="ShoppingCartPagerTestFor20ItemsPerPageAnd21Products" summary="Verify pager is presented on Shopping Cart page if items qty=21, by default system configuration" ticketId="MAGETWO-63338"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> <data name="config/dataset" xsi:type="string">default_number_of_items_per_page_on_shopping_cart</data> <data name="products/21" xsi:type="string">catalogProductSimple::default</data> <constraint name="Magento\Checkout\Test\Constraint\AssertPagersPresentInShoppingCart"/> <constraint name="Magento\Checkout\Test\Constraint\AssertPagersSummaryText"/> </variation> <variation name="ShoppingCartPagerTestFor20ItemsPerPageAndRemovingOneProduct" summary="Verify pager is disapeared from Shopping Cart page if change qty from 21 to 20, by default system configuration" ticketId="MAGETWO-63339"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> <data name="products/21" xsi:type="string">catalogProductSimple::default</data> <data name="itemsToRemove" xsi:type="string">1</data> <constraint name="Magento\Checkout\Test\Constraint\AssertPagersNotPresentInShoppingCart"/> </variation> <variation name="ShoppingCartPagerTestForOneItemPerPageAnd20Products" summary="Verify Pager is presented on Shopping Cart page with non-default system configuration" ticketId="MAGETWO-63340"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> <data name="configData" xsi:type="string">one_item_per_page_on_shopping_cart</data> <constraint name="Magento\Checkout\Test\Constraint\AssertPagersPresentInShoppingCart" /> <constraint name="Magento\Checkout\Test\Constraint\AssertPagersSummaryText" /> diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/DeleteCmsPageUrlRewriteEntityTest.xml b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/DeleteCmsPageUrlRewriteEntityTest.xml index 8262d88bdff1a..ea9d49d662bdd 100644 --- a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/DeleteCmsPageUrlRewriteEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/DeleteCmsPageUrlRewriteEntityTest.xml @@ -8,20 +8,20 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Cms\Test\TestCase\DeleteCmsPageUrlRewriteEntityTest" summary="Delete Cms Page URL Rewrites" ticketId="MAGETWO-25915"> <variation name="DeleteCmsPageUrlRewriteEntityTestVariation1"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> <data name="urlRewrite/dataset" xsi:type="string">cms_default_no_redirect</data> <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteDeletedMessage" /> <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteNotInGrid" /> <constraint name="Magento\UrlRewrite\Test\Constraint\AssertPageByUrlRewriteIsNotFound" /> </variation> <variation name="DeleteCmsPageUrlRewriteEntityTestVariation2"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> <data name="urlRewrite/dataset" xsi:type="string">cms_default_permanent_redirect</data> <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteDeletedMessage" /> <constraint name="Magento\UrlRewrite\Test\Constraint\AssertPageByUrlRewriteIsNotFound" /> </variation> <variation name="DeleteCmsPageUrlRewriteEntityTestVariation3"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> <data name="urlRewrite/dataset" xsi:type="string">cms_default_temporary_redirect</data> <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteDeletedMessage" /> <constraint name="Magento\UrlRewrite\Test\Constraint\AssertPageByUrlRewriteIsNotFound" /> diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageRewriteEntityTest.xml b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageRewriteEntityTest.xml index 6ff68599beeb3..1125ce8d916c1 100644 --- a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageRewriteEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/UpdateCmsPageRewriteEntityTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Cms\Test\TestCase\UpdateCmsPageRewriteEntityTest" summary="Update Cms Page URL Rewrites " ticketId="MAGETWO-26173"> <variation name="UpdateCmsPageRewriteEntityTestVariation1"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> <data name="cmsPageRewrite/dataset" xsi:type="string">cms_default_no_redirect</data> <data name="urlRewrite/data/store_id" xsi:type="string">Main Website/Main Website Store/%default%</data> <data name="urlRewrite/data/request_path" xsi:type="string">request_path%isolation%</data> @@ -18,7 +18,7 @@ <constraint name="Magento\Cms\Test\Constraint\AssertUrlRewriteCmsPageRedirect" /> </variation> <variation name="UpdateCmsPageRewriteEntityTestVariation2"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> <data name="cmsPageRewrite/dataset" xsi:type="string">cms_default_temporary_redirect</data> <data name="urlRewrite/data/store_id" xsi:type="string">Main Website/Main Website Store/Default Store View</data> <data name="urlRewrite/data/request_path" xsi:type="string">request_path%isolation%.html</data> @@ -28,7 +28,7 @@ <constraint name="Magento\Cms\Test\Constraint\AssertUrlRewriteCmsPageRedirect" /> </variation> <variation name="UpdateCmsPageRewriteEntityTestVariation3"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2,mftf_migrated:yes</data> <data name="cmsPageRewrite/dataset" xsi:type="string">cms_default_permanent_redirect</data> <data name="urlRewrite/data/store_id" xsi:type="string">Main Website/Main Website Store/Default Store View</data> <data name="urlRewrite/data/request_path" xsi:type="string">request_path%isolation%.htm</data> diff --git a/dev/tests/functional/tests/app/Magento/ImportExport/Test/Constraint/AssertExportSubmittedMessage.php b/dev/tests/functional/tests/app/Magento/ImportExport/Test/Constraint/AssertExportSubmittedMessage.php index 59b1c7570c3de..363614825e568 100644 --- a/dev/tests/functional/tests/app/Magento/ImportExport/Test/Constraint/AssertExportSubmittedMessage.php +++ b/dev/tests/functional/tests/app/Magento/ImportExport/Test/Constraint/AssertExportSubmittedMessage.php @@ -16,7 +16,8 @@ class AssertExportSubmittedMessage extends AbstractConstraint /** * Text value to be checked. */ - const MESSAGE = 'Message is added to queue, wait to get your file soon'; + const MESSAGE = 'Message is added to queue, wait to get your file soon.' + . ' Make sure your cron job is running to export the file'; /** * Assert that export submitted message is visible after exporting. diff --git a/dev/tests/functional/tests/app/Magento/Install/Test/TestCase/InstallTest.xml b/dev/tests/functional/tests/app/Magento/Install/Test/TestCase/InstallTest.xml index 86906d4c09406..ed0c4119dd825 100644 --- a/dev/tests/functional/tests/app/Magento/Install/Test/TestCase/InstallTest.xml +++ b/dev/tests/functional/tests/app/Magento/Install/Test/TestCase/InstallTest.xml @@ -30,7 +30,7 @@ <variation name="InstallTestVariation3" firstConstraint="Magento\Install\Test\Constraint\AssertSuccessInstall" summary="Install with table prefix"> <data name="user/dataset" xsi:type="string">default</data> <data name="install/dbTablePrefix" xsi:type="string">pref_</data> - <data name="install/storeLanguage" xsi:type="string">Chinese (China)</data> + <data name="install/storeLanguage" xsi:type="string">Chinese</data> <constraint name="Magento\Install\Test\Constraint\AssertSuccessInstall" next="Magento\User\Test\Constraint\AssertUserSuccessLogin" /> <constraint name="Magento\User\Test\Constraint\AssertUserSuccessLogin" prev="Magento\Install\Test\Constraint\AssertSuccessInstall" /> </variation> diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/DeleteIntegrationEntityTest.xml b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/DeleteIntegrationEntityTest.xml index 607c0abf4302e..a43b88469faae 100644 --- a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/DeleteIntegrationEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/DeleteIntegrationEntityTest.xml @@ -11,6 +11,7 @@ <data name="integration/dataset" xsi:type="string">default</data> <constraint name="Magento\Integration\Test\Constraint\AssertIntegrationSuccessDeleteMessage" /> <constraint name="Magento\Integration\Test\Constraint\AssertIntegrationNotInGrid" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Review/Test/Block/Adminhtml/Customer/Edit/Tab/Reviews.php b/dev/tests/functional/tests/app/Magento/Review/Test/Block/Adminhtml/Customer/Edit/Tab/Reviews.php index cd46dc24ee3e9..5000fb838ec73 100644 --- a/dev/tests/functional/tests/app/Magento/Review/Test/Block/Adminhtml/Customer/Edit/Tab/Reviews.php +++ b/dev/tests/functional/tests/app/Magento/Review/Test/Block/Adminhtml/Customer/Edit/Tab/Reviews.php @@ -18,7 +18,7 @@ class Reviews extends Tab * * @var string */ - protected $reviews = '#reviwGrid'; + protected $reviews = '#reviewGrid'; /** * Returns product reviews grid. diff --git a/dev/tests/functional/tests/app/Magento/Review/Test/Block/Adminhtml/Grid.php b/dev/tests/functional/tests/app/Magento/Review/Test/Block/Adminhtml/Grid.php index 7614cd332bd04..23f877b96f3a2 100644 --- a/dev/tests/functional/tests/app/Magento/Review/Test/Block/Adminhtml/Grid.php +++ b/dev/tests/functional/tests/app/Magento/Review/Test/Block/Adminhtml/Grid.php @@ -27,7 +27,7 @@ class Grid extends GridAbstract 'selector' => 'input[name="title"]', ], 'status' => [ - 'selector' => '#reviwGrid_filter_status', + 'selector' => '#reviewGrid_filter_status', 'input' => 'select', ], 'nickname' => [ diff --git a/dev/tests/functional/tests/app/Magento/Review/Test/Page/Adminhtml/ReviewIndex.xml b/dev/tests/functional/tests/app/Magento/Review/Test/Page/Adminhtml/ReviewIndex.xml index fa77210b11875..c533067e9516e 100644 --- a/dev/tests/functional/tests/app/Magento/Review/Test/Page/Adminhtml/ReviewIndex.xml +++ b/dev/tests/functional/tests/app/Magento/Review/Test/Page/Adminhtml/ReviewIndex.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/pages.xsd"> <page name="ReviewIndex" area="Adminhtml" mca="review/product/index" module="Magento_Review"> <block name="messagesBlock" class="Magento\Backend\Test\Block\Messages" locator="#messages" strategy="css selector" /> - <block name="reviewGrid" class="Magento\Review\Test\Block\Adminhtml\Grid" locator="#reviwGrid" strategy="css selector" /> + <block name="reviewGrid" class="Magento\Review\Test\Block\Adminhtml\Grid" locator="#reviewGrid" strategy="css selector" /> <block name="reviewActions" class="Magento\Backend\Test\Block\GridPageActions" locator=".page-main-actions" strategy="css selector" /> </page> </config> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Invoice/Grid.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Invoice/Grid.php index a5c172da3a992..4d37ebe95a7ec 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Invoice/Grid.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Invoice/Grid.php @@ -24,10 +24,10 @@ class Grid extends \Magento\Ui\Test\Block\Adminhtml\DataGrid 'selector' => 'input[name="order_increment_id"]', ], 'grand_total_from' => [ - 'selector' => 'input[name="base_grand_total[from]"]', + 'selector' => 'input[name="grand_total[from]"]', ], 'grand_total_to' => [ - 'selector' => 'input[name="base_grand_total[to]"]', + 'selector' => 'input[name="grand_total[to]"]', ], ]; diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Creditmemo/Totals.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Creditmemo/Totals.php index d98c5696c81f8..8aff098fb9c3e 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Creditmemo/Totals.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Creditmemo/Totals.php @@ -4,6 +4,8 @@ * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Test\Block\Adminhtml\Order\Creditmemo; use Magento\Mtf\Client\Locator; @@ -27,6 +29,34 @@ class Totals extends \Magento\Sales\Test\Block\Adminhtml\Order\Totals */ protected $capture = '[name="invoice[capture_case]"]'; + /** + * Refund Shipping css selector. + * + * @var string + */ + private $refundShippingSelector = '#shipping_amount'; + + /** + * Adjustment Refund css selector. + * + * @var string + */ + private $adjustmentRefundSelector = '#adjustment_positive'; + + /** + * Adjustment Fee css selector. + * + * @var string + */ + private $adjustmentFeeSelector = '#adjustment_negative'; + + /** + * Update Totals button css selector. + * + * @var string + */ + private $updateTotalsSelector = '.update-totals-button'; + /** * Submit invoice. * @@ -57,4 +87,44 @@ public function setCaptureOption($option) { $this->_rootElement->find($this->capture, Locator::SELECTOR_CSS, 'select')->setValue($option); } + + /** + * Get Refund Shipping input element. + * + * @return \Magento\Mtf\Client\ElementInterface + */ + public function getRefundShippingElement() + { + return $this->_rootElement->find($this->refundShippingSelector, Locator::SELECTOR_CSS); + } + + /** + * Get Adjustment Refund input element. + * + * @return \Magento\Mtf\Client\ElementInterface + */ + public function getAdjustmentRefundElement() + { + return $this->_rootElement->find($this->adjustmentRefundSelector, Locator::SELECTOR_CSS); + } + + /** + * Get Adjustment Fee input element. + * + * @return \Magento\Mtf\Client\ElementInterface + */ + public function getAdjustmentFeeElement() + { + return $this->_rootElement->find($this->adjustmentFeeSelector, Locator::SELECTOR_CSS); + } + + /** + * Click update totals button. + * + * @return void + */ + public function clickUpdateTotals() + { + $this->_rootElement->find($this->updateTotalsSelector)->click(); + } } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendTest.xml index c4e03b94d2ada..b1e3b9a9d9f1e 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderBackendTest.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\Sales\Test\TestCase\CreateOrderBackendTest" summary="Create Order from Admin within Offline Payment Methods" ticketId="MAGETWO-28696"> <variation name="CreateOrderBackendTestVariation18" summary="Create order with condition available product qty = ordered product qty" ticketId="MAGETWO-12798"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="products/0" xsi:type="string">catalogProductSimple::product_with_qty_25</data> <data name="customer/dataset" xsi:type="string">default</data> <data name="billingAddress/dataset" xsi:type="string">US_address_1_without_email</data> @@ -24,6 +25,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductsOutOfStock" /> </variation> <variation name="CreateOrderBackendTestVariation19" summary="'Reorder' button is not visible for customer if ordered item is out of stock" ticketId="MAGETWO-63924"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="products/0" xsi:type="string">catalogProductSimple::default_qty_1</data> <data name="customer/dataset" xsi:type="string">default</data> <data name="billingAddress/dataset" xsi:type="string">US_address_1_without_email</data> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.php b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.php index 4dce560693ab3..cf93da5e9f6fb 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveRecentlyComparedProductsOnOrderPageTest.php @@ -8,10 +8,13 @@ use Magento\Catalog\Test\Page\Product\CatalogProductCompare; use Magento\Catalog\Test\Page\Product\CatalogProductView; +use Magento\Catalog\Test\TestStep\CreateProductsStep; use Magento\Cms\Test\Page\CmsIndex; use Magento\Customer\Test\Fixture\Customer; use Magento\Customer\Test\Page\Adminhtml\CustomerIndex; use Magento\Customer\Test\Page\Adminhtml\CustomerIndexEdit; +use Magento\Customer\Test\TestStep\LoginCustomerOnFrontendStep; +use Magento\Mtf\Util\Command\Cli\Config; use Magento\Sales\Test\Page\Adminhtml\OrderCreateIndex; use Magento\Mtf\Client\BrowserInterface; use Magento\Mtf\TestCase\Injectable; @@ -91,21 +94,27 @@ class MoveRecentlyComparedProductsOnOrderPageTest extends Injectable */ protected $catalogProductCompare; + /** + * @var Config + */ + private $config; + /** * Create customer. - * * @param Customer $customer * @param BrowserInterface $browser + * @param Config $config * @return array */ - public function __prepare(Customer $customer, BrowserInterface $browser) + public function __prepare(Customer $customer, BrowserInterface $browser, Config $config) { $customer->persist(); // Login under customer $this->objectManager - ->create(\Magento\Customer\Test\TestStep\LoginCustomerOnFrontendStep::class, ['customer' => $customer]) + ->create(LoginCustomerOnFrontendStep::class, ['customer' => $customer]) ->run(); $this->browser = $browser; + $this->config = $config; return ['customer' => $customer]; } @@ -137,20 +146,26 @@ public function __inject( $this->catalogProductCompare = $catalogProductCompare; } + public function setUp() + { + $this->config->setConfig('reports/options/enabled', 1); + parent::setUp(); + } + /** * Move recently compared products on order page. - * * @param Customer $customer * @param string $products * @param bool $productsIsConfigured * @return array + * @throws \Exception */ public function test(Customer $customer, $products, $productsIsConfigured = false) { // Preconditions // Create product $products = $this->objectManager->create( - \Magento\Catalog\Test\TestStep\CreateProductsStep::class, + CreateProductsStep::class, ['products' => $products] )->run()['products']; foreach ($products as $itemProduct) { @@ -171,4 +186,10 @@ public function test(Customer $customer, $products, $productsIsConfigured = fals return ['products' => $products, 'productsIsConfigured' => $productsIsConfigured]; } + + public function tearDown() + { + $this->config->setConfig('reports/options/enabled', 0); + parent::tearDown(); + } } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/CreateCreditMemoStep.php b/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/CreateCreditMemoStep.php index 45298c5898c25..6ef1713c8542f 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/CreateCreditMemoStep.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/CreateCreditMemoStep.php @@ -4,6 +4,8 @@ * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Test\TestStep; use Magento\Checkout\Test\Fixture\Cart; @@ -95,8 +97,11 @@ public function run() if ($this->compare($items, $refundData)) { $this->orderCreditMemoNew->getFormBlock()->updateQty(); } - + $hasChangeTotals = $this->isTotalsDataChanged($refundData); $this->orderCreditMemoNew->getFormBlock()->fillFormData($refundData); + if ($hasChangeTotals) { + $this->orderCreditMemoNew->getTotalsBlock()->clickUpdateTotals(); + } $this->orderCreditMemoNew->getFormBlock()->submit(); } @@ -116,4 +121,30 @@ protected function getCreditMemoIds() $this->salesOrderView->getOrderForm()->openTab('creditmemos'); return $this->salesOrderView->getOrderForm()->getTab('creditmemos')->getGridBlock()->getIds(); } + + /** + * Is totals data changed. + * + * @param array $data + * @return bool + */ + private function isTotalsDataChanged(array $data): bool + { + $compareData = [ + 'shipping_amount' => + $this->orderCreditMemoNew->getTotalsBlock()->getRefundShippingElement()->getValue(), + 'adjustment_positive' => + $this->orderCreditMemoNew->getTotalsBlock()->getAdjustmentRefundElement()->getValue(), + 'adjustment_negative' => + $this->orderCreditMemoNew->getTotalsBlock()->getAdjustmentFeeElement()->getValue(), + ]; + + foreach ($compareData as $fieldName => $fieldValue) { + if (isset($data['form_data'][$fieldName]) && $fieldValue !== $data['form_data'][$fieldName]) { + return true; + } + } + + return false; + } } diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/NewCustomerPasswordComplexityTest.xml b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/NewCustomerPasswordComplexityTest.xml index a169a3f175cc6..534f692a36aef 100644 --- a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/NewCustomerPasswordComplexityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/NewCustomerPasswordComplexityTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Security\Test\TestCase\NewCustomerPasswordComplexityTest" summary="New customer password complexity" ticketId="MAGETWO-49044"> <variation name="PasswordLengthTest" summary="Customer password length is below 8 characters"> - <data name="tag" xsi:type="string">severity:S1</data> + <data name="tag" xsi:type="string">severity:S1,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> @@ -18,7 +18,7 @@ <constraint name="Magento\Security\Test\Constraint\AssertPasswordLengthErrorMessage" /> </variation> <variation name="PasswordComplexityTest" summary="Customer password is not secure enough"> - <data name="tag" xsi:type="string">severity:S1</data> + <data name="tag" xsi:type="string">severity:S1,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/Shipping/Test/TestCase/CreateShipmentEntityTest.xml b/dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/CreateShipmentEntityTest.xml index 06acf95effdbf..3fa93602e5256 100644 --- a/dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/CreateShipmentEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Shipping/Test/TestCase/CreateShipmentEntityTest.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\Shipping\Test\TestCase\CreateShipmentEntityTest" summary="Create Shipment for Offline Payment Methods" ticketId="MAGETWO-28708"> <variation name="CreateShipmentEntityTestVariation1" summary="Shipment with tracking number"> - <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="order/dataset" xsi:type="string">default</data> <data name="order/data/entity_id/products" xsi:type="string">catalogProductSimple::default</data> <data name="order/data/total_qty_ordered/0" xsi:type="string">1</data> @@ -38,6 +38,7 @@ <constraint name="Magento\Shipping\Test\Constraint\AssertShipmentInShipmentsGrid" /> <constraint name="Magento\Shipping\Test\Constraint\AssertShipmentItems" /> <constraint name="Magento\Shipping\Test\Constraint\AssertShipTotalQuantity" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteCategoryUrlRewriteEntityTest.xml b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteCategoryUrlRewriteEntityTest.xml index 56440e8a8492b..42f71b8d01f76 100644 --- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteCategoryUrlRewriteEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/DeleteCategoryUrlRewriteEntityTest.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\DeleteCategoryUrlRewriteEntityTest" summary="Delete Category URL Rewrites" ticketId="MAGETWO-25086"> <variation name="DeleteCategoryUrlRewriteEntityTestVariation1"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="urlRewrite/data/target_path/entity" xsi:type="string">catalog/category/view/id/%category::default%</data> <data name="urlRewrite/data/redirect_type" xsi:type="string">No</data> <data name="urlRewrite/data/request_path" xsi:type="string">-</data> @@ -15,6 +16,7 @@ <constraint name="Magento\UrlRewrite\Test\Constraint\AssertPageByUrlRewriteIsNotFound" /> </variation> <variation name="DeleteCategoryUrlRewriteEntityTestVariation2"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="urlRewrite/data/target_path/entity" xsi:type="string">catalog/category/view/id/%category::default%</data> <data name="urlRewrite/data/redirect_type" xsi:type="string">No</data> <data name="urlRewrite/data/request_path" xsi:type="string">example%isolation%.html</data> diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/LockAdminUserEntityTest.xml b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/LockAdminUserEntityTest.xml index 052197e3db33c..f89f94ba03e73 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/LockAdminUserEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/LockAdminUserEntityTest.xml @@ -24,6 +24,7 @@ <data name="incorrectPassword" xsi:type="string">honey boo boo</data> <data name="attempts" xsi:type="string">7</data> <constraint name="Magento\User\Test\Constraint\AssertUserFailedLoginMessage" /> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserEntityTest.xml b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserEntityTest.xml index a89d1ede80112..ac99c2d8721f3 100644 --- a/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/User/Test/TestCase/UpdateAdminUserEntityTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\User\Test\TestCase\UpdateAdminUserEntityTest" summary="Update Admin User" ticketId="MAGETWO-24345"> <variation name="UpdateAdminUserEntityTestVariation2"> - <data name="tag" xsi:type="string">severity:S3</data> + <data name="tag" xsi:type="string">severity:S3, mftf_migrated:yes</data> <data name="initialUser/dataset" xsi:type="string">custom_admin_with_default_role</data> <data name="user/data/role_id/dataset" xsi:type="string">role::role_sales</data> <data name="user/data/current_password" xsi:type="string">123123q</data> diff --git a/dev/tests/functional/tests/app/Magento/Widget/Test/Constraint/AssertWidgetRecentlyComparedProducts.php b/dev/tests/functional/tests/app/Magento/Widget/Test/Constraint/AssertWidgetRecentlyComparedProducts.php index 5a67fe43a691b..bf70f0f901352 100644 --- a/dev/tests/functional/tests/app/Magento/Widget/Test/Constraint/AssertWidgetRecentlyComparedProducts.php +++ b/dev/tests/functional/tests/app/Magento/Widget/Test/Constraint/AssertWidgetRecentlyComparedProducts.php @@ -89,8 +89,27 @@ public function processAssert( $this->addProducts($products); $this->removeCompareProducts(); + $cmsIndex->open(); + //Widgets data is cache via LocalStorage so it might take couple of refreshes before cache is invalidated. + $refreshCount = 3; + $refreshNo = 1; + $isVisible = false; + while (!$isVisible && $refreshNo <= $refreshCount) { + $browser->refresh(); + try { + $isVisible = $browser->waitUntil( + function () use ($widget) { + return $this->catalogProductCompare->getWidgetView() + ->isWidgetVisible($widget, 'Recently Compared') ? true : null; + } + ); + } catch (\Throwable $exception) { + $isVisible = false; + } + $refreshNo++; + } \PHPUnit\Framework\Assert::assertTrue( - $this->catalogProductCompare->getWidgetView()->isWidgetVisible($widget, 'Recently Compared'), + $isVisible, 'Widget is absent on Product Compare page.' ); } diff --git a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/DeleteProductsFromWishlistOnFrontendTest.xml b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/DeleteProductsFromWishlistOnFrontendTest.xml index d6b930d999537..26593636d3fcb 100644 --- a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/DeleteProductsFromWishlistOnFrontendTest.xml +++ b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/DeleteProductsFromWishlistOnFrontendTest.xml @@ -39,6 +39,7 @@ <constraint name="Magento\Wishlist\Test\Constraint\AssertWishlistIsEmpty" /> </variation> <variation name="DeleteProductsFromWishlistOnFrontendTestVariation4"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="products/0" xsi:type="string">bundleProduct::bundle_dynamic_product</data> <data name="removedProductsIndex" xsi:type="array"> <item name="0" xsi:type="string">1</item> @@ -46,6 +47,7 @@ <constraint name="Magento\Wishlist\Test\Constraint\AssertWishlistIsEmpty" /> </variation> <variation name="DeleteProductsFromWishlistOnFrontendTestVariation5"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="products/0" xsi:type="string">bundleProduct::bundle_fixed_product</data> <data name="removedProductsIndex" xsi:type="array"> <item name="0" xsi:type="string">1</item> @@ -53,6 +55,7 @@ <constraint name="Magento\Wishlist\Test\Constraint\AssertWishlistIsEmpty" /> </variation> <variation name="DeleteProductsFromWishlistOnFrontendTestVariation6"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="products/0" xsi:type="string">configurableProduct::default</data> <data name="removedProductsIndex" xsi:type="array"> <item name="0" xsi:type="string">1</item> diff --git a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/MoveProductFromShoppingCartToWishlistTest.xml b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/MoveProductFromShoppingCartToWishlistTest.xml index 95e6a854ed266..aa3b646161a17 100644 --- a/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/MoveProductFromShoppingCartToWishlistTest.xml +++ b/dev/tests/functional/tests/app/Magento/Wishlist/Test/TestCase/MoveProductFromShoppingCartToWishlistTest.xml @@ -15,6 +15,7 @@ <constraint name="Magento\Checkout\Test\Constraint\AssertCartIsEmpty" /> </variation> <variation name="MoveProductFromShoppingCartToWishlistTestVariation2"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="product/0" xsi:type="string">catalogProductVirtual::default</data> <constraint name="Magento\Wishlist\Test\Constraint\AssertMoveProductToWishlistSuccessMessage" /> <constraint name="Magento\Wishlist\Test\Constraint\AssertProductIsPresentInWishlist" /> @@ -29,7 +30,7 @@ <constraint name="Magento\Wishlist\Test\Constraint\AssertProductDetailsInWishlist" /> </variation> <variation name="MoveProductFromShoppingCartToWishlistTestVariation4"> - <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="product/0" xsi:type="string">configurableProduct::default</data> <constraint name="Magento\Wishlist\Test\Constraint\AssertMoveProductToWishlistSuccessMessage" /> <constraint name="Magento\Wishlist\Test\Constraint\AssertProductIsPresentInWishlist" /> @@ -37,6 +38,7 @@ <constraint name="Magento\Wishlist\Test\Constraint\AssertProductDetailsInWishlist" /> </variation> <variation name="MoveProductFromShoppingCartToWishlistTestVariation5"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="product/0" xsi:type="string">bundleProduct::bundle_dynamic_product</data> <constraint name="Magento\Wishlist\Test\Constraint\AssertMoveProductToWishlistSuccessMessage" /> <constraint name="Magento\Wishlist\Test\Constraint\AssertProductIsPresentInWishlist" /> @@ -44,6 +46,7 @@ <constraint name="Magento\Wishlist\Test\Constraint\AssertProductDetailsInWishlist" /> </variation> <variation name="MoveProductFromShoppingCartToWishlistTestVariation6"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="product/0" xsi:type="string">bundleProduct::bundle_fixed_product</data> <constraint name="Magento\Wishlist\Test\Constraint\AssertMoveProductToWishlistSuccessMessage" /> <constraint name="Magento\Wishlist\Test\Constraint\AssertProductIsPresentInWishlist" /> diff --git a/dev/tests/integration/_files/Magento/TestModuleCspConfig/composer.json b/dev/tests/integration/_files/Magento/TestModuleCspConfig/composer.json new file mode 100644 index 0000000000000..f4d4075fe3377 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleCspConfig/composer.json @@ -0,0 +1,21 @@ +{ + "name": "magento/module-csp-config", + "description": "test csp module", + "config": { + "sort-packages": true + }, + "require": { + "php": "~7.1.3||~7.2.0||~7.3.0", + "magento/framework": "*", + "magento/module-integration": "*" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/TestModuleCspConfig" + ] + ] + } +} diff --git a/dev/tests/integration/_files/Magento/TestModuleCspConfig/etc/csp_whitelist.xml b/dev/tests/integration/_files/Magento/TestModuleCspConfig/etc/csp_whitelist.xml new file mode 100644 index 0000000000000..e9eb1fe21aa4f --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleCspConfig/etc/csp_whitelist.xml @@ -0,0 +1,23 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<csp_whitelist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Csp/etc/csp_whitelist.xsd"> + <policies> + <policy id="object-src"> + <values> + <value id="mage-base" type="host">https://magento.com</value> + <value id="hash" type="hash" algorithm="sha256">B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF8=</value> + </values> + </policy> + <policy id="media-src"> + <values> + <value id="mage-base" type="host">https://magento.com</value> + <value id="devdocs-base" type="host">https://devdocs.magento.com</value> + </values> + </policy> + </policies> +</csp_whitelist> diff --git a/dev/tests/integration/_files/Magento/TestModuleCspConfig/etc/module.xml b/dev/tests/integration/_files/Magento/TestModuleCspConfig/etc/module.xml new file mode 100644 index 0000000000000..ee90595cacf19 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleCspConfig/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_TestModuleCspConfig" active="true" /> +</config> diff --git a/dev/tests/integration/_files/Magento/TestModuleCspConfig/registration.php b/dev/tests/integration/_files/Magento/TestModuleCspConfig/registration.php new file mode 100644 index 0000000000000..9749bf5a7f621 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleCspConfig/registration.php @@ -0,0 +1,12 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +$registrar = new ComponentRegistrar(); +if ($registrar->getPath(ComponentRegistrar::MODULE, 'Magento_TestModuleCspConfig') === null) { + ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_TestModuleCspConfig', __DIR__); +} diff --git a/dev/tests/integration/_files/Magento/TestModuleCspConfig2/composer.json b/dev/tests/integration/_files/Magento/TestModuleCspConfig2/composer.json new file mode 100644 index 0000000000000..ebf1d57fe6593 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleCspConfig2/composer.json @@ -0,0 +1,21 @@ +{ + "name": "magento/module-csp-config2", + "description": "test csp module 2", + "config": { + "sort-packages": true + }, + "require": { + "php": "~7.1.3||~7.2.0||~7.3.0", + "magento/framework": "*", + "magento/module-integration": "*" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/TestModuleCspConfig2" + ] + ] + } +} diff --git a/dev/tests/integration/_files/Magento/TestModuleCspConfig2/etc/csp_whitelist.xml b/dev/tests/integration/_files/Magento/TestModuleCspConfig2/etc/csp_whitelist.xml new file mode 100644 index 0000000000000..eff59271bf422 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleCspConfig2/etc/csp_whitelist.xml @@ -0,0 +1,17 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<csp_whitelist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Csp/etc/csp_whitelist.xsd"> + <policies> + <policy id="object-src"> + <values> + <value id="devdocs-base" type="host">https://devdocs.magento.com</value> + <value id="mage-base" type="host">http://magento.com</value> + </values> + </policy> + </policies> +</csp_whitelist> diff --git a/dev/tests/integration/_files/Magento/TestModuleCspConfig2/etc/module.xml b/dev/tests/integration/_files/Magento/TestModuleCspConfig2/etc/module.xml new file mode 100644 index 0000000000000..de3c2ea25f4f2 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleCspConfig2/etc/module.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_TestModuleCspConfig2" active="true"> + <sequence> + <module name="Magento_TestModuleCspConfig"/> + </sequence> + </module> +</config> diff --git a/dev/tests/integration/_files/Magento/TestModuleCspConfig2/registration.php b/dev/tests/integration/_files/Magento/TestModuleCspConfig2/registration.php new file mode 100644 index 0000000000000..539d9cd20e10f --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleCspConfig2/registration.php @@ -0,0 +1,12 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +$registrar = new ComponentRegistrar(); +if ($registrar->getPath(ComponentRegistrar::MODULE, 'Magento_TestModuleCspConfig2') === null) { + ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_TestModuleCspConfig2', __DIR__); +} diff --git a/dev/tests/integration/_files/Magento/TestModuleSimpleTemplateDirective/Model/FooFilter.php b/dev/tests/integration/_files/Magento/TestModuleSimpleTemplateDirective/Model/FooFilter.php new file mode 100644 index 0000000000000..9cdf909f41823 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleSimpleTemplateDirective/Model/FooFilter.php @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\TestModuleSimpleTemplateDirective\Model; + +use Magento\Framework\Filter\SimpleDirective\ProcessorInterface; +use Magento\Framework\Filter\Template; + +/** + * Filters a value for testing purposes + */ +class FooFilter implements \Magento\Framework\Filter\DirectiveProcessor\FilterInterface +{ + /** + * @inheritDoc + */ + public function filterValue(string $value, array $params): string + { + $arg1 = $params[0] ?? null; + + return strtoupper(strrev($value . $arg1 ?? '')); + } + + /** + * @inheritDoc + */ + public function getName(): string + { + return 'foofilter'; + } +} diff --git a/dev/tests/integration/_files/Magento/TestModuleSimpleTemplateDirective/Model/LegacyFilter.php b/dev/tests/integration/_files/Magento/TestModuleSimpleTemplateDirective/Model/LegacyFilter.php new file mode 100644 index 0000000000000..2f45183585dec --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleSimpleTemplateDirective/Model/LegacyFilter.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\TestModuleSimpleTemplateDirective\Model; + +use Magento\Framework\Filter\Template; + +/** + * A legacy directive test entity + */ +class LegacyFilter extends Template +{ + /** + * Filter a directive + * + * @param $construction + * @return string + */ + protected function coolDirective($construction) + { + return 'value1: ' . $construction[1] . ':' . $construction[2]; + } + + /** + * Filter a directive + * + * @param $construction + * @return string + */ + public function coolerDirective($construction) + { + return 'value2: ' . $construction[1] . ':' . $construction[2]; + } +} diff --git a/dev/tests/integration/_files/Magento/TestModuleSimpleTemplateDirective/Model/MyDirProcessor.php b/dev/tests/integration/_files/Magento/TestModuleSimpleTemplateDirective/Model/MyDirProcessor.php new file mode 100644 index 0000000000000..faf4728f3c338 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleSimpleTemplateDirective/Model/MyDirProcessor.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\TestModuleSimpleTemplateDirective\Model; + +use Magento\Framework\Filter\SimpleDirective\ProcessorInterface; +use Magento\Framework\Filter\Template; + +/** + * Handles the {{mydir}} directive + */ +class MyDirProcessor implements ProcessorInterface +{ + /** + * @inheritDoc + */ + public function getName(): string + { + return 'mydir'; + } + + /** + * @inheritDoc + */ + public function process( + $value, + array $parameters, + ?string $html + ): string { + return $value . $parameters['param1'] . $html; + } + + /** + * @inheritDoc + */ + public function getDefaultFilters(): ?array + { + return ['foofilter']; + } +} diff --git a/dev/tests/integration/_files/Magento/TestModuleSimpleTemplateDirective/etc/di.xml b/dev/tests/integration/_files/Magento/TestModuleSimpleTemplateDirective/etc/di.xml new file mode 100644 index 0000000000000..e102c28c7307d --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleSimpleTemplateDirective/etc/di.xml @@ -0,0 +1,23 @@ +<?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\Framework\Filter\SimpleDirective\ProcessorPool"> + <arguments> + <argument name="processors" xsi:type="array"> + <item name="mydir" xsi:type="object">Magento\TestModuleSimpleTemplateDirective\Model\MyDirProcessor</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\Filter\DirectiveProcessor\Filter\FilterPool"> + <arguments> + <argument name="filters" xsi:type="array"> + <item name="foofilter" xsi:type="object">Magento\TestModuleSimpleTemplateDirective\Model\FooFilter</item> + </argument> + </arguments> + </type> +</config> diff --git a/dev/tests/integration/_files/Magento/TestModuleSimpleTemplateDirective/etc/module.xml b/dev/tests/integration/_files/Magento/TestModuleSimpleTemplateDirective/etc/module.xml new file mode 100644 index 0000000000000..30f1c3f08cbfe --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleSimpleTemplateDirective/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_TestModuleSimpleTemplateDirective" /> +</config> diff --git a/dev/tests/integration/_files/Magento/TestModuleSimpleTemplateDirective/registration.php b/dev/tests/integration/_files/Magento/TestModuleSimpleTemplateDirective/registration.php new file mode 100644 index 0000000000000..4b1c200857b90 --- /dev/null +++ b/dev/tests/integration/_files/Magento/TestModuleSimpleTemplateDirective/registration.php @@ -0,0 +1,12 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +$registrar = new ComponentRegistrar(); +if ($registrar->getPath(ComponentRegistrar::MODULE, 'Magento_TestModuleSimpleTemplateDirective') === null) { + ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_TestModuleSimpleTemplateDirective', __DIR__); +} diff --git a/dev/tests/integration/etc/di/preferences/ce.php b/dev/tests/integration/etc/di/preferences/ce.php index b8264ea977750..2770283637dc7 100644 --- a/dev/tests/integration/etc/di/preferences/ce.php +++ b/dev/tests/integration/etc/di/preferences/ce.php @@ -31,4 +31,10 @@ \Magento\TestFramework\Lock\Backend\DummyLocker::class, \Magento\Framework\Session\SessionStartChecker::class => \Magento\TestFramework\Session\SessionStartChecker::class, \Magento\Framework\HTTP\AsyncClientInterface::class => \Magento\TestFramework\HTTP\AsyncClientInterfaceMock::class, + \Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager::class => + \Magento\TestFramework\Catalog\Model\CategoryLayoutUpdateManager::class, + \Magento\Catalog\Model\Product\Attribute\LayoutUpdateManager::class => + \Magento\TestFramework\Catalog\Model\ProductLayoutUpdateManager::class, + \Magento\Cms\Model\Page\CustomLayoutManagerInterface::class => + \Magento\TestFramework\Cms\Model\CustomLayoutManager::class ]; diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/DateGroupDataProvider.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/DateGroupDataProvider.php new file mode 100644 index 0000000000000..7f9d5362c4f83 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/DateGroupDataProvider.php @@ -0,0 +1,210 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Block\Product\View\Options; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Model\Product\Option; + +/** + * Data provider with product custom options from date group(date, date & time, time). + */ +class DateGroupDataProvider +{ + /** + * Return options data. + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * + * @return array + */ + public function getData(): array + { + return [ + 'type_date_required' => [ + [ + Option::KEY_TITLE => 'Test option date title 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DATE, + Option::KEY_IS_REQUIRE => 1, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => 'fixed', + Option::KEY_SKU => 'test-option-date-title-1', + ], + [ + 'block_with_required_class' => '<div class="field date required"', + 'title' => '<span>Test option date title 1</span>', + 'price' => 'data-price-amount="10"', + ], + ], + 'type_date_not_required' => [ + [ + Option::KEY_TITLE => 'Test option date title 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DATE, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => 'fixed', + Option::KEY_SKU => 'test-option-date-title-2', + ], + [ + 'block_with_required_class' => '<div class="field date"', + 'title' => '<span>Test option date title 2</span>', + 'price' => 'data-price-amount="10"', + ], + ], + 'type_date_fixed_price' => [ + [ + Option::KEY_TITLE => 'Test option date title 3', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DATE, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => 'fixed', + Option::KEY_SKU => 'test-option-date-title-3', + ], + [ + 'block_with_required_class' => '<div class="field date"', + 'title' => '<span>Test option date title 3</span>', + 'price' => 'data-price-amount="50"', + ], + ], + 'type_date_percent_price' => [ + [ + Option::KEY_TITLE => 'Test option date title 4', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DATE, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => 'percent', + Option::KEY_SKU => 'test-option-date-title-4', + ], + [ + 'block_with_required_class' => '<div class="field date"', + 'title' => '<span>Test option date title 4</span>', + 'price' => 'data-price-amount="5"', + ], + ], + 'type_date_and_time_required' => [ + [ + Option::KEY_TITLE => 'Test option date and time title 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DATE_TIME, + Option::KEY_IS_REQUIRE => 1, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => 'fixed', + Option::KEY_SKU => 'test-option-date-and-time-title-1', + ], + [ + 'block_with_required_class' => '<div class="field date required"', + 'title' => '<span>Test option date and time title 1</span>', + 'price' => 'data-price-amount="10"', + ], + ], + 'type_date_and_time_not_required' => [ + [ + Option::KEY_TITLE => 'Test option date and time title 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DATE_TIME, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => 'fixed', + Option::KEY_SKU => 'test-option-date-and-time-title-2', + ], + [ + 'block_with_required_class' => '<div class="field date"', + 'title' => '<span>Test option date and time title 2</span>', + 'price' => 'data-price-amount="10"', + ], + ], + 'type_date_and_time_fixed_price' => [ + [ + Option::KEY_TITLE => 'Test option date and time title 3', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DATE_TIME, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => 'fixed', + Option::KEY_SKU => 'test-option-date-and-time-title-3', + ], + [ + 'block_with_required_class' => '<div class="field date"', + 'title' => '<span>Test option date and time title 3</span>', + 'price' => 'data-price-amount="50"', + ], + ], + 'type_date_and_time_percent_price' => [ + [ + Option::KEY_TITLE => 'Test option date and time title 4', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DATE_TIME, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => 'percent', + Option::KEY_SKU => 'test-option-date-and-time-title-4', + ], + [ + 'block_with_required_class' => '<div class="field date"', + 'title' => '<span>Test option date and time title 4</span>', + 'price' => 'data-price-amount="5"', + ], + ], + 'type_time_required' => [ + [ + Option::KEY_TITLE => 'Test option time title 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_TIME, + Option::KEY_IS_REQUIRE => 1, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => 'fixed', + Option::KEY_SKU => 'test-option-time-title-1', + ], + [ + 'block_with_required_class' => '<div class="field date required"', + 'title' => '<span>Test option time title 1</span>', + 'price' => 'data-price-amount="10"', + ], + ], + 'type_time_not_required' => [ + [ + Option::KEY_TITLE => 'Test option time title 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_TIME, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => 'fixed', + Option::KEY_SKU => 'test-option-time-title-2', + ], + [ + 'block_with_required_class' => '<div class="field date"', + 'title' => '<span>Test option time title 2</span>', + 'price' => 'data-price-amount="10"', + ], + ], + 'type_time_fixed_price' => [ + [ + Option::KEY_TITLE => 'Test option time title 3', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_TIME, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => 'fixed', + Option::KEY_SKU => 'test-option-time-title-3', + ], + [ + 'block_with_required_class' => '<div class="field date"', + 'title' => '<span>Test option time title 3</span>', + 'price' => 'data-price-amount="50"', + ], + ], + 'type_time_percent_price' => [ + [ + Option::KEY_TITLE => 'Test option time title 4', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_TIME, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => 'percent', + Option::KEY_SKU => 'test-option-time-title-4', + ], + [ + 'block_with_required_class' => '<div class="field date"', + 'title' => '<span>Test option time title 4</span>', + 'price' => 'data-price-amount="5"', + ], + ], + ]; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/FileGroupDataProvider.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/FileGroupDataProvider.php new file mode 100644 index 0000000000000..c28cb770a806e --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/FileGroupDataProvider.php @@ -0,0 +1,134 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Block\Product\View\Options; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Model\Product\Option; + +/** + * Data provider with product custom options from file group(file). + */ +class FileGroupDataProvider +{ + /** + * Return options data. + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * + * @return array + */ + public function getData(): array + { + return [ + 'type_file_required' => [ + [ + Option::KEY_TITLE => 'Test option file title 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FILE, + Option::KEY_IS_REQUIRE => 1, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => 'fixed', + Option::KEY_SKU => 'test-option-file-title-1', + Option::KEY_SORT_ORDER => 1, + Option::KEY_FILE_EXTENSION => 'png, jpg', + ], + [ + 'block_with_required_class' => '<div class="field file required">', + 'label_for_created_option' => '<label class="label" for="options_%s_file"', + 'title' => '<span>Test option file title 1</span>', + 'price' => 'data-price-amount="10"', + 'required_element' => '/<input type="file"/', + 'file_extension' => '<strong>png, jpg</strong>', + ], + ], + 'type_file_not_required' => [ + [ + Option::KEY_TITLE => 'Test option file title 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FILE, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => 'fixed', + Option::KEY_SKU => 'test-option-file-title-2', + Option::KEY_SORT_ORDER => 1, + Option::KEY_FILE_EXTENSION => 'png, jpg', + ], + [ + 'block_with_required_class' => '<div class="field file">', + 'label_for_created_option' => '<label class="label" for="options_%s_file"', + 'title' => '<span>Test option file title 2</span>', + 'price' => 'data-price-amount="10"', + 'required_element' => '/<input type="file"/', + 'file_extension' => '<strong>png, jpg</strong>', + ], + ], + 'type_file_fixed_price' => [ + [ + Option::KEY_TITLE => 'Test option file title 3', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FILE, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => 'fixed', + Option::KEY_SKU => 'test-option-file-title-3', + Option::KEY_SORT_ORDER => 1, + Option::KEY_FILE_EXTENSION => 'png, jpg', + ], + [ + 'block_with_required_class' => '<div class="field file">', + 'label_for_created_option' => '<label class="label" for="options_%s_file"', + 'title' => '<span>Test option file title 3</span>', + 'price' => 'data-price-amount="50"', + 'required_element' => '/<input type="file"/', + 'file_extension' => '<strong>png, jpg</strong>', + ], + ], + 'type_file_percent_price' => [ + [ + Option::KEY_TITLE => 'Test option file title 4', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FILE, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => 'percent', + Option::KEY_SKU => 'test-option-file-title-4', + Option::KEY_SORT_ORDER => 1, + Option::KEY_FILE_EXTENSION => 'png, jpg', + ], + [ + 'block_with_required_class' => '<div class="field file">', + 'label_for_created_option' => '<label class="label" for="options_%s_file"', + 'title' => '<span>Test option file title 4</span>', + 'price' => 'data-price-amount="5"', + 'required_element' => '/<input type="file"/', + 'file_extension' => '<strong>png, jpg</strong>', + ], + ], + 'type_file_with_width_and_height' => [ + [ + Option::KEY_TITLE => 'Test option file title 5', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FILE, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => 'percent', + Option::KEY_SKU => 'test-option-file-title-5', + Option::KEY_SORT_ORDER => 1, + Option::KEY_FILE_EXTENSION => 'png, jpg', + Option::KEY_IMAGE_SIZE_X => 10, + Option::KEY_IMAGE_SIZE_Y => 81, + ], + [ + 'block_with_required_class' => '<div class="field file">', + 'label_for_created_option' => '<label class="label" for="options_%s_file"', + 'title' => '<span>Test option file title 5</span>', + 'price' => 'data-price-amount="5"', + 'required_element' => '/<input type="file"/', + 'file_extension' => '<strong>png, jpg</strong>', + 'file_width' => '/%s:.*<strong>10 px.<\/strong>/', + 'file_height' => '/%s:.*<strong>81 px.<\/strong>/', + ], + ], + ]; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/SelectGroupDataProvider.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/SelectGroupDataProvider.php new file mode 100644 index 0000000000000..2a13c1cd45466 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/SelectGroupDataProvider.php @@ -0,0 +1,363 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Block\Product\View\Options; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Model\Product\Option; +use Magento\Catalog\Model\Product\Option\Value; + +/** + * Data provider with product custom options from select group(drop-down, radio buttons, checkbox, multiple select). + */ +class SelectGroupDataProvider +{ + /** + * Return options data. + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * + * @return array + */ + public function getData(): array + { + return [ + 'type_drop_down_required' => [ + [ + Option::KEY_TITLE => 'Test option drop-down title 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN, + Option::KEY_IS_REQUIRE => 1, + ], + [ + Value::KEY_TITLE => 'Test option drop-down title 1 value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => 'fixed', + Value::KEY_SKU => 'test-option-drop-down-title-1-value-1', + ], + [ + 'block_with_required_class' => '<div class="field required">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option drop-down title 1</span>', + 'required_element' => '/<select/', + 'option_value_item' => '/<option value="%s" price="10" >%s \+\s{11}\$10.00.*/', + 'not_contain_arr' => [ + '/<select.*multiple="multiple"/', + ], + ], + ], + 'type_drop_down_not_required' => [ + [ + Option::KEY_TITLE => 'Test option drop-down title 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option drop-down title 2 value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => 'fixed', + Value::KEY_SKU => 'test-option-drop-down-title-2-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option drop-down title 2</span>', + 'required_element' => '/<select/', + 'option_value_item' => '/<option value="%s" price="10" >%s \+\s{11}\$10.00.*/', + 'not_contain_arr' => [ + '/<select.*multiple="multiple"/', + ], + ], + ], + 'type_drop_down_value_fixed_price' => [ + [ + Option::KEY_TITLE => 'Test option drop-down title 3', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option drop-down title 3 value 1', + Value::KEY_PRICE => 50, + Value::KEY_PRICE_TYPE => 'fixed', + Value::KEY_SKU => 'test-option-drop-down-title-3-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option drop-down title 3</span>', + 'required_element' => '/<select/', + 'option_value_item' => '/<option value="%s" price="50" >%s \+\s{11}\$50.00.*/', + 'not_contain_arr' => [ + '/<select.*multiple="multiple"/', + ], + ], + ], + 'type_drop_down_value_percent_price' => [ + [ + Option::KEY_TITLE => 'Test option drop-down title 4', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option drop-down title 4 value 1', + Value::KEY_PRICE => 50, + Value::KEY_PRICE_TYPE => 'percent', + Value::KEY_SKU => 'test-option-drop-down-title-4-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option drop-down title 4</span>', + 'required_element' => '/<select/', + 'option_value_item' => '/<option value="%s" price="5" >%s \+\s{11}\$5.00.*/', + 'not_contain_arr' => [ + '/<select.*multiple="multiple"/', + ], + ], + ], + 'type_radio_button_required' => [ + [ + Option::KEY_TITLE => 'Test option radio-button title 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_RADIO, + Option::KEY_IS_REQUIRE => 1, + ], + [ + Value::KEY_TITLE => 'Test option radio-button title 1 value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => 'fixed', + Value::KEY_SKU => 'test-option-radio-button-title-1-value-1', + ], + [ + 'block_with_required_class' => '<div class="field required">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option radio-button title 1</span>', + 'required_element' => '/<input type="radio"/', + 'price' => 'data-price-amount="10"', + ], + ], + 'type_radio_button_not_required' => [ + [ + Option::KEY_TITLE => 'Test option radio-button title 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_RADIO, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option radio-button title 2 value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => 'fixed', + Value::KEY_SKU => 'test-option-radio-button-title-2-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option radio-button title 2</span>', + 'required_element' => '/<input type="radio"/', + 'price' => 'data-price-amount="10"', + ], + ], + 'type_radio_button_value_fixed_price' => [ + [ + Option::KEY_TITLE => 'Test option radio-button title 3', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_RADIO, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option radio-button title 3 value 1', + Value::KEY_PRICE => 50, + Value::KEY_PRICE_TYPE => 'fixed', + Value::KEY_SKU => 'test-option-radio-button-title-3-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option radio-button title 3</span>', + 'required_element' => '/<input type="radio"/', + 'price' => 'data-price-amount="50"', + ], + ], + 'type_radio_button_value_percent_price' => [ + [ + Option::KEY_TITLE => 'Test option radio-button title 4', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_RADIO, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option radio-button title 4 value 1', + Value::KEY_PRICE => 50, + Value::KEY_PRICE_TYPE => 'percent', + Value::KEY_SKU => 'test-option-radio-button-title-4-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option radio-button title 4</span>', + 'required_element' => '/<input type="radio"/', + 'price' => 'data-price-amount="5"', + ], + ], + 'type_checkbox_required' => [ + [ + Option::KEY_TITLE => 'Test option checkbox title 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_CHECKBOX, + Option::KEY_IS_REQUIRE => 1, + ], + [ + Value::KEY_TITLE => 'Test option checkbox title 1 value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => 'fixed', + Value::KEY_SKU => 'test-option-checkbox-title-1-value-1', + ], + [ + 'block_with_required_class' => '<div class="field required">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option checkbox title 1</span>', + 'required_element' => '/<input type="checkbox"/', + 'price' => 'data-price-amount="10"', + ], + ], + 'type_checkbox_not_required' => [ + [ + Option::KEY_TITLE => 'Test option checkbox title 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_CHECKBOX, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option checkbox title 2 value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => 'fixed', + Value::KEY_SKU => 'test-option-checkbox-title-2-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option checkbox title 2</span>', + 'required_element' => '/<input type="checkbox"/', + 'price' => 'data-price-amount="10"', + ], + ], + 'type_checkbox_value_fixed_price' => [ + [ + Option::KEY_TITLE => 'Test option checkbox title 3', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_CHECKBOX, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option checkbox title 3 value 1', + Value::KEY_PRICE => 50, + Value::KEY_PRICE_TYPE => 'fixed', + Value::KEY_SKU => 'test-option-checkbox-title-3-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option checkbox title 3</span>', + 'required_element' => '/<input type="checkbox"/', + 'price' => 'data-price-amount="50"', + ], + ], + 'type_checkbox_value_percent_price' => [ + [ + Option::KEY_TITLE => 'Test option checkbox title 4', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_CHECKBOX, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option checkbox title 4 value 1', + Value::KEY_PRICE => 50, + Value::KEY_PRICE_TYPE => 'percent', + Value::KEY_SKU => 'test-option-checkbox-title-4-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option checkbox title 4</span>', + 'required_element' => '/<input type="checkbox"/', + 'price' => 'data-price-amount="5"', + ], + ], + 'type_multiselect_required' => [ + [ + Option::KEY_TITLE => 'Test option multiselect title 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_MULTIPLE, + Option::KEY_IS_REQUIRE => 1, + ], + [ + Value::KEY_TITLE => 'Test option multiselect title 1 value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => 'fixed', + Value::KEY_SKU => 'test-option-multiselect-title-1-value-1', + ], + [ + 'block_with_required_class' => '<div class="field required">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option multiselect title 1</span>', + 'required_element' => '/<select.*multiple="multiple"/', + 'option_value_item' => '/<option value="%s" price="10" >%s \+\s{11}\$10.00.*/', + ], + ], + 'type_multiselect_not_required' => [ + [ + Option::KEY_TITLE => 'Test option multiselect title 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_MULTIPLE, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option multiselect title 2 value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => 'fixed', + Value::KEY_SKU => 'test-option-multiselect-title-2-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option multiselect title 2</span>', + 'required_element' => '/<select.*multiple="multiple"/', + 'option_value_item' => '/<option value="%s" price="10" >%s \+\s{11}\$10.00.*/', + ], + ], + 'type_multiselect_value_fixed_price' => [ + [ + Option::KEY_TITLE => 'Test option multiselect title 3', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_MULTIPLE, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option multiselect title 3 value 1', + Value::KEY_PRICE => 50, + Value::KEY_PRICE_TYPE => 'fixed', + Value::KEY_SKU => 'test-option-multiselect-title-3-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option multiselect title 3</span>', + 'required_element' => '/<select.*multiple="multiple"/', + 'option_value_item' => '/<option value="%s" price="50" >%s \+\s{11}\$50.00.*/', + ], + ], + 'type_multiselect_value_percent_price' => [ + [ + Option::KEY_TITLE => 'Test option multiselect title 4', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_MULTIPLE, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Test option multiselect title 4 value 1', + Value::KEY_PRICE => 50, + Value::KEY_PRICE_TYPE => 'percent', + Value::KEY_SKU => 'test-option-multiselect-title-4-value-1', + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="select_%s">', + 'title' => '<span>Test option multiselect title 4</span>', + 'required_element' => '/<select.*multiple="multiple"/', + 'option_value_item' => '/<option value="%s" price="5" >%s \+\s{11}\$5.00.*/', + ], + ], + ]; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/TextGroupDataProvider.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/TextGroupDataProvider.php new file mode 100644 index 0000000000000..75a6da0593d73 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Block/Product/View/Options/TextGroupDataProvider.php @@ -0,0 +1,212 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Block\Product\View\Options; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Model\Product\Option; + +/** + * Data provider with product custom options from text group(field, area). + */ +class TextGroupDataProvider +{ + /** + * Return options data. + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * + * @return array + */ + public function getData(): array + { + return [ + 'type_field_required' => [ + [ + Option::KEY_TITLE => 'Test option field title 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD, + Option::KEY_IS_REQUIRE => 1, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => 'fixed', + Option::KEY_SKU => 'test-option-field-title-1', + Option::KEY_MAX_CHARACTERS => 0, + ], + [ + 'block_with_required_class' => '<div class="field required">', + 'label_for_created_option' => '<label class="label" for="options_%s_text">', + 'title' => '<span>Test option field title 1</span>', + 'price' => 'data-price-amount="10"', + 'required_element' => '/<input type="text"/', + ], + ], + 'type_field_not_required' => [ + [ + Option::KEY_TITLE => 'Test option field title 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => 'fixed', + Option::KEY_SKU => 'test-option-field-title-2', + Option::KEY_MAX_CHARACTERS => 0, + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="options_%s_text">', + 'title' => '<span>Test option field title 2</span>', + 'price' => 'data-price-amount="10"', + 'required_element' => '/<input type="text"/', + ], + ], + 'type_field_fixed_price' => [ + [ + Option::KEY_TITLE => 'Test option field title 3', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => 'fixed', + Option::KEY_SKU => 'test-option-field-title-3', + Option::KEY_MAX_CHARACTERS => 0, + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="options_%s_text">', + 'title' => '<span>Test option field title 3</span>', + 'price' => 'data-price-amount="50"', + 'required_element' => '/<input type="text"/', + ], + ], + 'type_field_percent_price' => [ + [ + Option::KEY_TITLE => 'Test option field title 4', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => 'percent', + Option::KEY_SKU => 'test-option-field-title-4', + Option::KEY_MAX_CHARACTERS => 0, + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="options_%s_text">', + 'title' => '<span>Test option field title 4</span>', + 'price' => 'data-price-amount="5"', + 'required_element' => '/<input type="text"/', + ], + ], + 'type_field_max_characters' => [ + [ + Option::KEY_TITLE => 'Test option field title 5', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => 'fixed', + Option::KEY_SKU => 'test-option-field-title-5', + Option::KEY_MAX_CHARACTERS => 99, + ], + [ + 'block_with_required_class' => '<div class="field">', + 'label_for_created_option' => '<label class="label" for="options_%s_text">', + 'title' => '<span>Test option field title 5</span>', + 'price' => 'data-price-amount="10"', + 'required_element' => '/<input type="text"/', + 'max_characters' => 'Maximum 99 characters', + ], + ], + 'type_area_required' => [ + [ + Option::KEY_TITLE => 'Test option area title 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_AREA, + Option::KEY_IS_REQUIRE => 1, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => 'fixed', + Option::KEY_SKU => 'test-option-area-title-1', + Option::KEY_MAX_CHARACTERS => 0, + ], + [ + 'block_with_required_class' => '<div class="field textarea required">', + 'label_for_created_option' => '<label class="label" for="options_%s_text">', + 'title' => '<span>Test option area title 1</span>', + 'price' => 'data-price-amount="10"', + 'required_element' => '/<textarea/', + ], + ], + 'type_area_not_required' => [ + [ + Option::KEY_TITLE => 'Test option area title 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_AREA, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => 'fixed', + Option::KEY_SKU => 'test-option-area-title-2', + Option::KEY_MAX_CHARACTERS => 0, + ], + [ + 'block_with_required_class' => '<div class="field textarea">', + 'label_for_created_option' => '<label class="label" for="options_%s_text">', + 'title' => '<span>Test option area title 2</span>', + 'price' => 'data-price-amount="10"', + 'required_element' => '/<textarea/', + ], + ], + 'type_area_fixed_price' => [ + [ + Option::KEY_TITLE => 'Test option area title 3', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_AREA, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => 'fixed', + Option::KEY_SKU => 'test-option-area-title-3', + Option::KEY_MAX_CHARACTERS => 0, + ], + [ + 'block_with_required_class' => '<div class="field textarea">', + 'label_for_created_option' => '<label class="label" for="options_%s_text">', + 'title' => '<span>Test option area title 3</span>', + 'price' => 'data-price-amount="50"', + 'required_element' => '/<textarea/', + ], + ], + 'type_area_percent_price' => [ + [ + Option::KEY_TITLE => 'Test option area title 4', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_AREA, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => 'percent', + Option::KEY_SKU => 'test-option-area-title-4', + Option::KEY_MAX_CHARACTERS => 0, + ], + [ + 'block_with_required_class' => '<div class="field textarea">', + 'label_for_created_option' => '<label class="label" for="options_%s_text">', + 'title' => '<span>Test option area title 4</span>', + 'price' => 'data-price-amount="5"', + 'required_element' => '/<textarea/', + ], + ], + 'type_area_max_characters' => [ + [ + Option::KEY_TITLE => 'Test option area title 5', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_AREA, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => 'fixed', + Option::KEY_SKU => 'test-option-area-title-5', + Option::KEY_MAX_CHARACTERS => 99, + ], + [ + 'block_with_required_class' => '<div class="field textarea">', + 'label_for_created_option' => '<label class="label" for="options_%s_text">', + 'title' => '<span>Test option area title 5</span>', + 'price' => 'data-price-amount="10"', + 'required_element' => '/<textarea/', + 'max_characters' => 'Maximum 99 characters', + ], + ], + ]; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/CategoryLayoutUpdateManager.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/CategoryLayoutUpdateManager.php new file mode 100644 index 0000000000000..48ff3a6496722 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/CategoryLayoutUpdateManager.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model; + +use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager; + +/** + * Easy way to fake available files. + */ +class CategoryLayoutUpdateManager extends LayoutUpdateManager +{ + /** + * @var array Keys are category IDs, values - file names. + */ + private $fakeFiles = []; + + /** + * Supply fake files for a category. + * + * @param int $forCategoryId + * @param string[]|null $files Pass null to reset. + */ + public function setCategoryFakeFiles(int $forCategoryId, ?array $files): void + { + if ($files === null) { + unset($this->fakeFiles[$forCategoryId]); + } else { + $this->fakeFiles[$forCategoryId] = $files; + } + } + + /** + * @inheritDoc + */ + public function fetchAvailableFiles(CategoryInterface $category): array + { + if (array_key_exists($category->getId(), $this->fakeFiles)) { + return $this->fakeFiles[$category->getId()]; + } + + return parent::fetchAvailableFiles($category); + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Attribute/DataProvider/MediaImage.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Attribute/DataProvider/MediaImage.php new file mode 100644 index 0000000000000..dd706ab29c326 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Attribute/DataProvider/MediaImage.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider; + +use Magento\TestFramework\Eav\Model\Attribute\DataProvider\AbstractBaseAttributeData; + +/** + * Product attribute data for attribute with input type media image. + */ +class MediaImage extends AbstractBaseAttributeData +{ + /** + * @inheritdoc + */ + public function __construct() + { + parent::__construct(); + $this->defaultAttributePostData['used_for_sort_by'] = '0'; + } + + /** + * @inheritdoc + */ + public function getAttributeData(): array + { + $result = parent::getAttributeData(); + unset($result["{$this->getFrontendInput()}_with_default_value"]); + unset($result["{$this->getFrontendInput()}_without_default_value"]); + + return $result; + } + + /** + * @inheritdoc + */ + public function getAttributeDataWithCheckArray(): array + { + $result = parent::getAttributeDataWithCheckArray(); + unset($result["{$this->getFrontendInput()}_with_default_value"]); + unset($result["{$this->getFrontendInput()}_without_default_value"]); + + return $result; + } + + /** + * @inheritdoc + */ + protected function getFrontendInput(): string + { + return 'media_image'; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Attribute/DataProvider/Price.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Attribute/DataProvider/Price.php new file mode 100644 index 0000000000000..04ee6bb0a5740 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Attribute/DataProvider/Price.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider; + +use Magento\TestFramework\Eav\Model\Attribute\DataProvider\AbstractBaseAttributeData; + +/** + * Product attribute data for attribute with input type weee. + */ +class Price extends AbstractBaseAttributeData +{ + /** + * @inheritdoc + */ + public function __construct() + { + parent::__construct(); + $this->defaultAttributePostData['is_filterable'] = '0'; + $this->defaultAttributePostData['is_filterable_in_search'] = '0'; + $this->defaultAttributePostData['used_for_sort_by'] = '0'; + } + + /** + * @inheritdoc + */ + public function getAttributeData(): array + { + $result = parent::getAttributeData(); + unset($result["{$this->getFrontendInput()}_with_default_value"]); + unset($result["{$this->getFrontendInput()}_without_default_value"]); + + return $result; + } + + /** + * @inheritdoc + */ + public function getAttributeDataWithCheckArray(): array + { + $result = parent::getAttributeDataWithCheckArray(); + unset($result["{$this->getFrontendInput()}_with_default_value"]); + unset($result["{$this->getFrontendInput()}_without_default_value"]); + + return $result; + } + + /** + * @inheritdoc + */ + protected function getFrontendInput(): string + { + return 'price'; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/AbstractBase.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/AbstractBase.php new file mode 100644 index 0000000000000..36a4f6662cb09 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/AbstractBase.php @@ -0,0 +1,172 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type; + +/** + * Base custom options data provider. + */ +abstract class AbstractBase +{ + /** + * Return data for create options for all cases. + * + * @return array + */ + public function getDataForCreateOptions(): array + { + return [ + "type_{$this->getType()}_title" => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'title' => 'Test option title 1', + 'type' => $this->getType(), + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + "type_{$this->getType()}_required_options" => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'title' => 'Test option title 1', + 'type' => $this->getType(), + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + "type_{$this->getType()}_not_required_options" => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 0, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'title' => 'Test option title 1', + 'type' => $this->getType(), + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + "type_{$this->getType()}_options_with_fixed_price" => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'title' => 'Test option title 1', + 'type' => $this->getType(), + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + "type_{$this->getType()}_options_with_percent_price" => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'title' => 'Test option title 1', + 'type' => $this->getType(), + 'price' => 10, + 'price_type' => 'percent', + ], + ], + "type_{$this->getType()}_price" => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'title' => 'Test option title 1', + 'type' => $this->getType(), + 'price' => 22, + 'price_type' => 'percent', + ], + ], + "type_{$this->getType()}_sku" => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 50, + 'title' => 'Test option title 1', + 'type' => $this->getType(), + 'price' => 22, + 'price_type' => 'percent', + ], + ], + ]; + } + + /** + * Return data for create options for all cases. + * + * @return array + */ + public function getDataForUpdateOptions(): array + { + return array_merge_recursive( + $this->getDataForCreateOptions(), + [ + "type_{$this->getType()}_title" => [ + [ + 'title' => 'Test updated option title', + ] + ], + "type_{$this->getType()}_required_options" => [ + [ + 'is_require' => 0, + ], + ], + "type_{$this->getType()}_not_required_options" => [ + [ + 'is_require' => 1, + ], + ], + "type_{$this->getType()}_options_with_fixed_price" => [ + [ + 'price_type' => 'percent', + ], + ], + "type_{$this->getType()}_options_with_percent_price" => [ + [ + 'price_type' => 'fixed', + ], + ], + "type_{$this->getType()}_price" => [ + [ + 'price' => 60, + ], + ], + "type_{$this->getType()}_sku" => [ + [ + 'sku' => 'Updated option sku', + ], + ], + ] + ); + } + + /** + * Return option type. + * + * @return string + */ + abstract protected function getType(): string; +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/AbstractSelect.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/AbstractSelect.php new file mode 100644 index 0000000000000..0cbe64bf1d965 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/AbstractSelect.php @@ -0,0 +1,199 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type; + +use Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\AbstractBase; + +/** + * Abstract data provider for options from select group. + */ +abstract class AbstractSelect extends AbstractBase +{ + /** + * @inheritdoc + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function getDataForCreateOptions(): array + { + return [ + "type_{$this->getType()}_title" => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'title' => 'Test option title 1', + 'type' => $this->getType(), + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'fixed', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + "type_{$this->getType()}_required_options" => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'title' => 'Test option title 1', + 'type' => $this->getType(), + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'fixed', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + "type_{$this->getType()}_not_required_options" => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 0, + 'title' => 'Test option title 1', + 'type' => $this->getType(), + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'fixed', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + "type_{$this->getType()}_options_with_fixed_price" => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'title' => 'Test option title 1', + 'type' => $this->getType(), + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'fixed', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + "type_{$this->getType()}_options_with_percent_price" => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'title' => 'Test option title 1', + 'type' => $this->getType(), + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'percent', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + "type_{$this->getType()}_price" => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'title' => 'Test option title 1', + 'type' => $this->getType(), + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 22, + 'price_type' => 'fixed', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + "type_{$this->getType()}_sku" => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'title' => 'Test option title 1', + 'type' => $this->getType(), + ], + [ + 'record_id' => 0, + 'title' => 'Test option 1 value 1', + 'price' => 10, + 'price_type' => 'fixed', + 'sku' => 'test-option-1-value-1', + 'sort_order' => 1, + ], + ], + ]; + } + + /** + * @inheritdoc + */ + public function getDataForUpdateOptions(): array + { + return array_merge_recursive( + $this->getDataForCreateOptions(), + [ + "type_{$this->getType()}_title" => [ + [ + 'title' => 'Updated test option title 1', + ], + [], + ], + "type_{$this->getType()}_required_options" => [ + [ + 'is_require' => 0, + ], + [], + ], + "type_{$this->getType()}_not_required_options" => [ + [ + 'is_require' => 1, + ], + [], + ], + "type_{$this->getType()}_options_with_fixed_price" => [ + [], + [ + 'price_type' => 'percent', + ], + ], + "type_{$this->getType()}_options_with_percent_price" => [ + [], + [ + 'price_type' => 'fixed', + ], + ], + "type_{$this->getType()}_price" => [ + [], + [ + 'price' => 666, + ], + ], + "type_{$this->getType()}_sku" => [ + [], + [ + 'sku' => 'updated-test-option-1-value-1', + ], + ], + ] + ); + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/AbstractText.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/AbstractText.php new file mode 100644 index 0000000000000..42028eee632be --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/AbstractText.php @@ -0,0 +1,75 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type; + +use Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\AbstractBase; + +/** + * Abstract data provider for options from text group. + */ +abstract class AbstractText extends AbstractBase +{ + /** + * @inheritdoc + */ + public function getDataForCreateOptions(): array + { + return array_merge_recursive( + parent::getDataForCreateOptions(), + [ + "type_{$this->getType()}_options_with_max_charters_configuration" => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 30, + 'title' => 'Test option title 1', + 'type' => $this->getType(), + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + "type_{$this->getType()}_options_without_max_charters_configuration" => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'title' => 'Test option title 1', + 'type' => $this->getType(), + 'price' => 10, + 'price_type' => 'fixed', + ], + ], + ] + ); + } + + /** + * @inheritdoc + */ + public function getDataForUpdateOptions(): array + { + return array_merge_recursive( + parent::getDataForUpdateOptions(), + [ + "type_{$this->getType()}_options_with_max_charters_configuration" => [ + [ + 'max_characters' => 0, + ], + ], + "type_{$this->getType()}_options_without_max_charters_configuration" => [ + [ + 'max_characters' => 55, + ], + ], + ] + ); + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/Area.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/Area.php new file mode 100644 index 0000000000000..137e77412f7b9 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/Area.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\AbstractText; + +/** + * Data provider for custom options from text group with type "area". + */ +class Area extends AbstractText +{ + /** + * @inheritdoc + */ + protected function getType(): string + { + return ProductCustomOptionInterface::OPTION_TYPE_AREA; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/Checkbox.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/Checkbox.php new file mode 100644 index 0000000000000..9a5f57d30946b --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/Checkbox.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\AbstractSelect; + +/** + * Data provider for custom options from select group with type "Checkbox". + */ +class Checkbox extends AbstractSelect +{ + /** + * @inheritdoc + */ + protected function getType(): string + { + return ProductCustomOptionInterface::OPTION_TYPE_CHECKBOX; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/Date.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/Date.php new file mode 100644 index 0000000000000..501265849be93 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/Date.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\AbstractBase; + +/** + * Data provider for custom options from date group with type "date". + */ +class Date extends AbstractBase +{ + /** + * @inheritdoc + */ + protected function getType(): string + { + return ProductCustomOptionInterface::OPTION_TYPE_DATE; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/DateTime.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/DateTime.php new file mode 100644 index 0000000000000..e3e154cc386e0 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/DateTime.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\AbstractBase; + +/** + * Data provider for custom options from date group with type "date & time". + */ +class DateTime extends AbstractBase +{ + /** + * @inheritdoc + */ + protected function getType(): string + { + return ProductCustomOptionInterface::OPTION_TYPE_DATE_TIME; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/DropDown.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/DropDown.php new file mode 100644 index 0000000000000..2c8b9f2f25dc6 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/DropDown.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\AbstractSelect; + +/** + * Data provider for custom options from select group with type "Drop-down". + */ +class DropDown extends AbstractSelect +{ + /** + * @inheritdoc + */ + protected function getType(): string + { + return ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/Field.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/Field.php new file mode 100644 index 0000000000000..12df838bd46c2 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/Field.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\AbstractText; + +/** + * Data provider for custom options from text group with type "field". + */ +class Field extends AbstractText +{ + /** + * @inheritdoc + */ + protected function getType(): string + { + return ProductCustomOptionInterface::OPTION_TYPE_FIELD; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/File.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/File.php new file mode 100644 index 0000000000000..d2aa20a005ec4 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/File.php @@ -0,0 +1,117 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Model\Product\Option; + +/** + * Data provider for options from file group with type "file". + */ +class File extends AbstractBase +{ + /** + * @inheritdoc + */ + public function getDataForCreateOptions(): array + { + return $this->injectFileExtension( + array_merge_recursive( + parent::getDataForCreateOptions(), + [ + "type_{$this->getType()}_option_file_extension" => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'max_characters' => 30, + 'title' => 'Test option title 1', + 'type' => $this->getType(), + 'price' => 10, + 'price_type' => 'fixed', + 'file_extension' => 'gif', + 'image_size_x' => 10, + 'image_size_y' => 20, + ], + ], + "type_{$this->getType()}_option_maximum_file_size" => [ + [ + 'record_id' => 0, + 'sort_order' => 1, + 'is_require' => 1, + 'sku' => 'test-option-title-1', + 'title' => 'Test option title 1', + 'type' => $this->getType(), + 'price' => 10, + 'price_type' => 'fixed', + 'file_extension' => 'gif', + 'image_size_x' => 10, + 'image_size_y' => 20, + ], + ], + ] + ), + 'png' + ); + } + + /** + * @inheritdoc + */ + public function getDataForUpdateOptions(): array + { + return $this->injectFileExtension( + array_merge_recursive( + parent::getDataForUpdateOptions(), + [ + "type_{$this->getType()}_option_file_extension" => [ + [ + 'file_extension' => 'jpg', + ], + ], + "type_{$this->getType()}_option_maximum_file_size" => [ + [ + 'image_size_x' => 300, + 'image_size_y' => 815, + ], + ], + ] + ), + '' + ); + } + + /** + * @inheritdoc + */ + protected function getType(): string + { + return ProductCustomOptionInterface::OPTION_TYPE_FILE; + } + + /** + * Add 'file_extension' value to each option. + * + * @param array $data + * @param string $extension + * @return array + */ + private function injectFileExtension(array $data, string $extension): array + { + foreach ($data as &$caseData) { + foreach ($caseData as &$option) { + if (!isset($option[Option::KEY_FILE_EXTENSION])) { + $option[Option::KEY_FILE_EXTENSION] = $extension; + } + } + } + + return $data; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/MultipleSelect.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/MultipleSelect.php new file mode 100644 index 0000000000000..3e958b1ac39a0 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/MultipleSelect.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\AbstractSelect; + +/** + * Data provider for custom options from select group with type "Drop-down". + */ +class MultipleSelect extends AbstractSelect +{ + /** + * @inheritdoc + */ + protected function getType(): string + { + return ProductCustomOptionInterface::OPTION_TYPE_MULTIPLE; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/RadioButtons.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/RadioButtons.php new file mode 100644 index 0000000000000..345aa289283e8 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/RadioButtons.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\AbstractSelect; + +/** + * Data provider for custom options from select group with type "Radio Buttons". + */ +class RadioButtons extends AbstractSelect +{ + /** + * @inheritdoc + */ + protected function getType(): string + { + return ProductCustomOptionInterface::OPTION_TYPE_RADIO; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/Time.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/Time.php new file mode 100644 index 0000000000000..cc6fb1e86a6c6 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/DataProvider/Type/Time.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\AbstractBase; + +/** + * Data provider for custom options from date group with type "time". + */ +class Time extends AbstractBase +{ + /** + * @inheritdoc + */ + protected function getType(): string + { + return ProductCustomOptionInterface::OPTION_TYPE_TIME; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/ProductLayoutUpdateManager.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/ProductLayoutUpdateManager.php new file mode 100644 index 0000000000000..6c8826583be70 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/ProductLayoutUpdateManager.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\TestFramework\Catalog\Model; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Product\Attribute\LayoutUpdateManager; + +/** + * Easy way to fake available files. + */ +class ProductLayoutUpdateManager extends LayoutUpdateManager +{ + /** + * @var array Keys are product IDs, values - file names. + */ + private $fakeFiles = []; + + /** + * Supply fake files for a product. + * + * @param int $forProductId + * @param string[]|null $files Pass null to reset. + */ + public function setFakeFiles(int $forProductId, ?array $files): void + { + if ($files === null) { + unset($this->fakeFiles[$forProductId]); + } else { + $this->fakeFiles[$forProductId] = $files; + } + } + + /** + * @inheritDoc + */ + public function fetchAvailableFiles(ProductInterface $product): array + { + if (array_key_exists($product->getId(), $this->fakeFiles)) { + return $this->fakeFiles[$product->getId()]; + } + + return parent::fetchAvailableFiles($product); + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Cms/Model/CustomLayoutManager.php b/dev/tests/integration/framework/Magento/TestFramework/Cms/Model/CustomLayoutManager.php new file mode 100644 index 0000000000000..527454c297d48 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Cms/Model/CustomLayoutManager.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\TestFramework\Cms\Model; + +use Magento\Cms\Api\Data\PageInterface; + +/** + * Manager allowing to fake available files. + */ +class CustomLayoutManager extends \Magento\Cms\Model\Page\CustomLayout\CustomLayoutManager +{ + /** + * @var string[][] + */ + private $files = []; + + /** + * Fake available files for given page. + * + * Pass null to unassign fake files. + * + * @param int $forPageId + * @param string[]|null $files + * @return void + */ + public function fakeAvailableFiles(int $forPageId, ?array $files): void + { + if ($files === null) { + unset($this->files[$forPageId]); + } else { + $this->files[$forPageId] = $files; + } + } + + /** + * @inheritDoc + */ + public function fetchAvailableFiles(PageInterface $page): array + { + if (array_key_exists($page->getId(), $this->files)) { + return $this->files[$page->getId()]; + } + + return parent::fetchAvailableFiles($page); + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/AbstractAttributeDataWithOptions.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/AbstractAttributeDataWithOptions.php new file mode 100644 index 0000000000000..8f25651e2e036 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/AbstractAttributeDataWithOptions.php @@ -0,0 +1,117 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Eav\Model\Attribute\DataProvider; + +/** + * Base POST data for create attribute with options. + */ +abstract class AbstractAttributeDataWithOptions extends AbstractBaseAttributeData +{ + /** + * @inheritdoc + */ + public function __construct() + { + parent::__construct(); + $this->defaultAttributePostData['serialized_options_arr'] = $this->getOptionsDataArr(); + $this->defaultAttributePostData['is_filterable'] = '0'; + $this->defaultAttributePostData['is_filterable_in_search'] = '0'; + } + + /** + * @inheritdoc + */ + public function getAttributeData(): array + { + $result = parent::getAttributeData(); + unset($result["{$this->getFrontendInput()}_with_default_value"]); + unset($result["{$this->getFrontendInput()}_without_default_value"]); + + return $result; + } + + /** + * @inheritdoc + */ + public function getAttributeDataWithErrorMessage(): array + { + $wrongSerializeMessage = 'The attribute couldn\'t be saved due to an error. Verify your information and '; + $wrongSerializeMessage .= 'try again. If the error persists, please try again later.'; + + return array_replace_recursive( + parent::getAttributeDataWithErrorMessage(), + [ + "{$this->getFrontendInput()}_with_wrong_serialized_options" => [ + array_merge( + $this->defaultAttributePostData, + [ + 'serialized_options_arr' => [], + 'serialized_options' => '?.\\//', + ] + ), + (string)__($wrongSerializeMessage) + ], + ] + ); + } + + /** + * @inheritdoc + */ + public function getAttributeDataWithCheckArray(): array + { + $result = parent::getAttributeDataWithCheckArray(); + unset($result["{$this->getFrontendInput()}_with_default_value"]); + unset($result["{$this->getFrontendInput()}_without_default_value"]); + + return $result; + } + + /** + * Return attribute options data. + * + * @return array + */ + protected function getOptionsDataArr(): array + { + return [ + [ + 'option' => [ + 'order' => [ + 'option_0' => '1', + ], + 'value' => [ + 'option_0' => [ + 'Admin value 1', + 'Default store view value 1', + ], + ], + 'delete' => [ + 'option_0' => '', + ], + ], + ], + [ + 'option' => [ + 'order' => [ + 'option_1' => '2', + ], + 'value' => [ + 'option_1' => [ + 'Admin value 2', + 'Default store view value 2', + ], + ], + 'delete' => [ + 'option_1' => '', + ], + ], + ], + ]; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/AbstractBaseAttributeData.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/AbstractBaseAttributeData.php new file mode 100644 index 0000000000000..af9e58d02fb5a --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/AbstractBaseAttributeData.php @@ -0,0 +1,224 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Eav\Model\Attribute\DataProvider; + +use Magento\Store\Model\Store; + +/** + * Base POST data for create attribute. + */ +abstract class AbstractBaseAttributeData +{ + /** + * Default POST data for create attribute. + * + * @var array + */ + protected $defaultAttributePostData = [ + 'active_tab' => 'main', + 'frontend_label' => [ + Store::DEFAULT_STORE_ID => 'Test attribute name', + ], + 'is_required' => '0', + 'dropdown_attribute_validation' => '', + 'dropdown_attribute_validation_unique' => '', + 'attribute_code' => '', + 'is_global' => '0', + 'default_value_text' => '', + 'default_value_yesno' => '0', + 'default_value_date' => '', + 'default_value_textarea' => '', + 'is_unique' => '0', + 'is_used_in_grid' => '1', + 'is_visible_in_grid' => '1', + 'is_filterable_in_grid' => '1', + 'is_searchable' => '0', + 'is_comparable' => '0', + 'is_used_for_promo_rules' => '0', + 'is_html_allowed_on_front' => '1', + 'is_visible_on_front' => '0', + 'used_in_product_listing' => '0', + ]; + + /** + * @inheritdoc + */ + public function __construct() + { + $this->defaultAttributePostData['frontend_input'] = $this->getFrontendInput(); + } + + /** + * Return create product attribute data set. + * + * @return array + */ + public function getAttributeData(): array + { + return [ + "{$this->getFrontendInput()}_with_required_fields" => [ + $this->defaultAttributePostData, + ], + "{$this->getFrontendInput()}_with_store_view_scope" => [ + $this->defaultAttributePostData, + ], + "{$this->getFrontendInput()}_with_global_scope" => [ + array_merge($this->defaultAttributePostData, ['is_global' => '1']), + ], + "{$this->getFrontendInput()}_with_website_scope" => [ + array_merge($this->defaultAttributePostData, ['is_global' => '2']), + ], + "{$this->getFrontendInput()}_with_attribute_code" => [ + array_merge($this->defaultAttributePostData, ['attribute_code' => 'test_custom_attribute_code']), + ], + "{$this->getFrontendInput()}_with_default_value" => [ + array_merge($this->defaultAttributePostData, ['default_value_text' => 'Default attribute value']), + ], + "{$this->getFrontendInput()}_without_default_value" => [ + $this->defaultAttributePostData, + ], + "{$this->getFrontendInput()}_with_unique_value" => [ + array_merge($this->defaultAttributePostData, ['is_unique' => '1']), + ], + "{$this->getFrontendInput()}_without_unique_value" => [ + $this->defaultAttributePostData, + ], + "{$this->getFrontendInput()}_with_enabled_add_to_column_options" => [ + array_merge($this->defaultAttributePostData, ['is_used_in_grid' => '1']), + ], + "{$this->getFrontendInput()}_without_enabled_add_to_column_options" => [ + array_merge($this->defaultAttributePostData, ['is_used_in_grid' => '0']), + ], + "{$this->getFrontendInput()}_with_enabled_use_in_filter_options" => [ + $this->defaultAttributePostData, + ], + "{$this->getFrontendInput()}_without_enabled_use_in_filter_options" => [ + array_merge($this->defaultAttributePostData, ['is_filterable_in_grid' => '0']), + ], + ]; + } + + /** + * Return create product attribute data set with error message. + * + * @return array + */ + public function getAttributeDataWithErrorMessage(): array + { + $wrongAttributeCode = 'Attribute code "????" is invalid. Please use only letters (a-z or A-Z), numbers '; + $wrongAttributeCode .= '(0-9) or underscore (_) in this field, and the first character should be a letter.'; + + return [ + "{$this->getFrontendInput()}_with_wrong_frontend_input" => [ + array_merge($this->defaultAttributePostData, ['frontend_input' => 'wrong_input_type']), + (string)__('Input type "wrong_input_type" not found in the input types list.') + ], + "{$this->getFrontendInput()}_with_wrong_attribute_code" => [ + array_merge($this->defaultAttributePostData, ['attribute_code' => '????']), + (string)__($wrongAttributeCode) + ], + ]; + } + + /** + * Return create product attribute data set with array for check data. + * + * @return array + */ + public function getAttributeDataWithCheckArray(): array + { + return array_merge_recursive( + $this->getAttributeData(), + [ + "{$this->getFrontendInput()}_with_required_fields" => [ + [ + 'attribute_code' => 'test_attribute_name', + ], + ], + "{$this->getFrontendInput()}_with_store_view_scope" => [ + [ + 'attribute_code' => 'test_attribute_name', + 'is_global' => '0', + ], + ], + "{$this->getFrontendInput()}_with_global_scope" => [ + [ + 'attribute_code' => 'test_attribute_name', + 'is_global' => '1', + ], + ], + "{$this->getFrontendInput()}_with_website_scope" => [ + [ + 'attribute_code' => 'test_attribute_name', + 'is_global' => '2', + ], + ], + "{$this->getFrontendInput()}_with_attribute_code" => [ + [ + 'attribute_code' => 'test_custom_attribute_code', + ], + ], + "{$this->getFrontendInput()}_with_default_value" => [ + [ + 'attribute_code' => 'test_attribute_name', + 'default_value' => 'Default attribute value', + ], + ], + "{$this->getFrontendInput()}_without_default_value" => [ + [ + 'attribute_code' => 'test_attribute_name', + 'default_value_text' => '', + ], + ], + "{$this->getFrontendInput()}_with_unique_value" => [ + [ + 'attribute_code' => 'test_attribute_name', + 'is_unique' => '1', + ], + ], + "{$this->getFrontendInput()}_without_unique_value" => [ + [ + 'attribute_code' => 'test_attribute_name', + 'is_unique' => '0', + ], + ], + "{$this->getFrontendInput()}_with_enabled_add_to_column_options" => [ + [ + 'attribute_code' => 'test_attribute_name', + 'is_used_in_grid' => '1', + ], + ], + "{$this->getFrontendInput()}_without_enabled_add_to_column_options" => [ + [ + 'attribute_code' => 'test_attribute_name', + 'is_used_in_grid' => false, + ], + ], + "{$this->getFrontendInput()}_with_enabled_use_in_filter_options" => [ + [ + 'attribute_code' => 'test_attribute_name', + 'is_filterable_in_grid' => '1', + ], + ], + "{$this->getFrontendInput()}_without_enabled_use_in_filter_options" => [ + [ + 'attribute_code' => 'test_attribute_name', + 'is_filterable_in_grid' => false, + ], + ], + ] + ); + } + + /** + * Return attribute frontend input. + * + * @return string + */ + abstract protected function getFrontendInput(): string; +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/Date.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/Date.php new file mode 100644 index 0000000000000..7a6f8ee41c1f8 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/Date.php @@ -0,0 +1,66 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Eav\Model\Attribute\DataProvider; + +/** + * Product attribute data for attribute with input type date. + */ +class Date extends AbstractBaseAttributeData +{ + /** + * @inheritdoc + */ + public function __construct() + { + parent::__construct(); + $this->defaultAttributePostData['used_for_sort_by'] = '0'; + } + + /** + * @inheritdoc + */ + public function getAttributeData(): array + { + return array_replace_recursive( + parent::getAttributeData(), + [ + "{$this->getFrontendInput()}_with_default_value" => [ + [ + 'default_value_text' => '', + 'default_value_date' => '10/29/2019', + ] + ] + ] + ); + } + + /** + * @inheritdoc + */ + public function getAttributeDataWithCheckArray(): array + { + return array_replace_recursive( + parent::getAttributeDataWithCheckArray(), + [ + "{$this->getFrontendInput()}_with_default_value" => [ + 1 => [ + 'default_value' => '2019-10-29 00:00:00', + ], + ], + ] + ); + } + + /** + * @inheritdoc + */ + protected function getFrontendInput(): string + { + return 'date'; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/DropDown.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/DropDown.php new file mode 100644 index 0000000000000..3c1acb5a33a54 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/DropDown.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Eav\Model\Attribute\DataProvider; + +/** + * Product attribute data for attribute with input type dropdown. + */ +class DropDown extends AbstractAttributeDataWithOptions +{ + /** + * @inheritdoc + */ + public function __construct() + { + parent::__construct(); + $this->defaultAttributePostData['used_for_sort_by'] = '0'; + $this->defaultAttributePostData['swatch_input_type'] = 'dropdown'; + } + + /** + * @inheritdoc + */ + protected function getFrontendInput(): string + { + return 'select'; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/MultipleSelect.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/MultipleSelect.php new file mode 100644 index 0000000000000..5fb5f745aebdc --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/MultipleSelect.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Eav\Model\Attribute\DataProvider; + +/** + * Product attribute data for attribute with input type multiple select. + */ +class MultipleSelect extends AbstractAttributeDataWithOptions +{ + /** + * @inheritdoc + */ + protected function getFrontendInput(): string + { + return 'multiselect'; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/Text.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/Text.php new file mode 100644 index 0000000000000..5b37248e27361 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/Text.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Eav\Model\Attribute\DataProvider; + +/** + * Product attribute data for attribute with input type text. + */ +class Text extends AbstractBaseAttributeData +{ + /** + * @inheritdoc + */ + public function __construct() + { + parent::__construct(); + $this->defaultAttributePostData['frontend_class'] = ''; + $this->defaultAttributePostData['used_for_sort_by'] = '0'; + } + + /** + * @inheritdoc + */ + public function getAttributeData(): array + { + return array_replace_recursive( + parent::getAttributeData(), + [ + "{$this->getFrontendInput()}_with_input_validation" => [ + array_merge($this->defaultAttributePostData, ['frontend_class' => 'validate-alpha']), + ], + "{$this->getFrontendInput()}_without_input_validation" => [ + $this->defaultAttributePostData, + ], + ] + ); + } + + /** + * @inheritdoc + */ + public function getAttributeDataWithCheckArray(): array + { + return array_merge_recursive( + parent::getAttributeDataWithCheckArray(), + [ + "{$this->getFrontendInput()}_with_input_validation" => [ + [ + 'attribute_code' => 'test_attribute_name', + 'frontend_class' => 'validate-alpha', + ], + ], + "{$this->getFrontendInput()}_without_input_validation" => [ + [ + 'attribute_code' => 'test_attribute_name', + 'frontend_class' => '', + ], + ], + ] + ); + } + + /** + * @inheritdoc + */ + protected function getFrontendInput(): string + { + return 'text'; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/TextArea.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/TextArea.php new file mode 100644 index 0000000000000..7588b12700272 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/TextArea.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Eav\Model\Attribute\DataProvider; + +/** + * Product attribute data for attribute with text area input type. + */ +class TextArea extends AbstractBaseAttributeData +{ + /** + * @inheritdoc + */ + public function getAttributeData(): array + { + return array_replace_recursive( + parent::getAttributeData(), + [ + "{$this->getFrontendInput()}_with_default_value" => [ + [ + 'default_value_text' => '', + 'default_value_textarea' => 'Default attribute value', + ], + ], + ] + ); + } + + /** + * @inheritdoc + */ + protected function getFrontendInput(): string + { + return 'textarea'; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/TextEditor.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/TextEditor.php new file mode 100644 index 0000000000000..d7a6276c1720f --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/TextEditor.php @@ -0,0 +1,126 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Eav\Model\Attribute\DataProvider; + +/** + * Product attribute data for attribute with text editor input type. + */ +class TextEditor extends AbstractBaseAttributeData +{ + /** + * @inheritdoc + */ + public function __construct() + { + parent::__construct(); + $this->defaultAttributePostData['used_for_sort_by'] = '0'; + } + + /** + * @inheritdoc + */ + public function getAttributeData(): array + { + return array_replace_recursive( + parent::getAttributeData(), + [ + "{$this->getFrontendInput()}_with_default_value" => [ + [ + 'default_value_text' => '', + 'default_value_textarea' => 'Default attribute value', + ], + ], + ] + ); + } + + /** + * @inheritDoc + */ + public function getAttributeDataWithCheckArray(): array + { + return array_replace_recursive( + parent::getAttributeDataWithCheckArray(), + [ + "{$this->getFrontendInput()}_with_required_fields" => [ + 1 => [ + 'frontend_input' => 'textarea', + ], + ], + "{$this->getFrontendInput()}_with_store_view_scope" => [ + 1 => [ + 'frontend_input' => 'textarea' + ], + ], + "{$this->getFrontendInput()}_with_global_scope" => [ + 1 => [ + 'frontend_input' => 'textarea' + ], + ], + "{$this->getFrontendInput()}_with_website_scope" => [ + 1 => [ + 'frontend_input' => 'textarea' + ], + ], + "{$this->getFrontendInput()}_with_attribute_code" => [ + 1 => [ + 'frontend_input' => 'textarea' + ], + ], + "{$this->getFrontendInput()}_with_default_value" => [ + 1 => [ + 'frontend_input' => 'textarea' + ], + ], + "{$this->getFrontendInput()}_without_default_value" => [ + 1 => [ + 'frontend_input' => 'textarea' + ], + ], + "{$this->getFrontendInput()}_with_unique_value" => [ + 1 => [ + 'frontend_input' => 'textarea' + ], + ], + "{$this->getFrontendInput()}_without_unique_value" => [ + 1 => [ + 'frontend_input' => 'textarea' + ], + ], + "{$this->getFrontendInput()}_with_enabled_add_to_column_options" => [ + 1 => [ + 'frontend_input' => 'textarea' + ], + ], + "{$this->getFrontendInput()}_without_enabled_add_to_column_options" => [ + 1 => [ + 'frontend_input' => 'textarea' + ], + ], + "{$this->getFrontendInput()}_with_enabled_use_in_filter_options" => [ + 1 => [ + 'frontend_input' => 'textarea' + ], + ], + "{$this->getFrontendInput()}_without_enabled_use_in_filter_options" => [ + 1 => [ + 'frontend_input' => 'textarea' + ], + ], + ] + ); + } + + /** + * @inheritdoc + */ + protected function getFrontendInput(): string + { + return 'texteditor'; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/YesNo.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/YesNo.php new file mode 100644 index 0000000000000..8fece70f0273c --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/Attribute/DataProvider/YesNo.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Eav\Model\Attribute\DataProvider; + +/** + * Product attribute data for attribute with yes/no input type. + */ +class YesNo extends AbstractBaseAttributeData +{ + /** + * @inheritdoc + */ + public function __construct() + { + parent::__construct(); + $this->defaultAttributePostData['is_filterable'] = '0'; + $this->defaultAttributePostData['is_filterable_in_search'] = '0'; + $this->defaultAttributePostData['used_for_sort_by'] = '0'; + } + + /** + * @inheritdoc + */ + public function getAttributeData(): array + { + return array_replace_recursive( + parent::getAttributeData(), + [ + "{$this->getFrontendInput()}_with_default_value" => [ + [ + 'default_value_text' => '', + 'default_value_yesno' => 1, + ], + ], + ] + ); + } + + /** + * @inheritdoc + */ + public function getAttributeDataWithCheckArray(): array + { + return array_replace_recursive( + parent::getAttributeDataWithCheckArray(), + [ + "{$this->getFrontendInput()}_with_default_value" => [ + 1 => [ + 'default_value' => 1, + ], + ], + ] + ); + } + + /** + * @inheritdoc + */ + protected function getFrontendInput(): string + { + return 'boolean'; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/GetAttributeGroupByName.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/GetAttributeGroupByName.php new file mode 100644 index 0000000000000..65ebe326fb939 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/GetAttributeGroupByName.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Eav\Model; + +use Magento\Eav\Api\AttributeGroupRepositoryInterface; +use Magento\Eav\Api\Data\AttributeGroupInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; + +/** + * Search and return attribute group by name. + */ +class GetAttributeGroupByName +{ + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * @var AttributeGroupRepositoryInterface + */ + private $groupRepository; + + /** + * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @param AttributeGroupRepositoryInterface $attributeGroupRepository + */ + public function __construct( + SearchCriteriaBuilder $searchCriteriaBuilder, + AttributeGroupRepositoryInterface $attributeGroupRepository + ) { + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->groupRepository = $attributeGroupRepository; + } + + /** + * Returns attribute group by name. + * + * @param int $setId + * @param string $groupName + * @return AttributeGroupInterface|null + */ + public function execute(int $setId, string $groupName): ?AttributeGroupInterface + { + $searchCriteria = $this->searchCriteriaBuilder->addFilter( + AttributeGroupInterface::GROUP_NAME, + $groupName + )->addFilter( + AttributeGroupInterface::ATTRIBUTE_SET_ID, + $setId + )->create(); + $result = $this->groupRepository->getList($searchCriteria)->getItems(); + + return array_shift($result); + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/GetAttributeSetByName.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/GetAttributeSetByName.php new file mode 100644 index 0000000000000..d7a7f0646742a --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/GetAttributeSetByName.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Eav\Model; + +use Magento\Eav\Api\AttributeSetRepositoryInterface; +use Magento\Eav\Api\Data\AttributeSetInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; + +/** + * Search and return attribute set by name. + */ +class GetAttributeSetByName +{ + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * @var AttributeSetRepositoryInterface + */ + private $attributeSetRepository; + + /** + * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @param AttributeSetRepositoryInterface $attributeSetRepository + */ + public function __construct( + SearchCriteriaBuilder $searchCriteriaBuilder, + AttributeSetRepositoryInterface $attributeSetRepository + ) { + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->attributeSetRepository = $attributeSetRepository; + } + + /** + * Find attribute set by name and return it. + * + * @param string $attributeSetName + * @return AttributeSetInterface|null + */ + public function execute(string $attributeSetName): ?AttributeSetInterface + { + $this->searchCriteriaBuilder->addFilter('attribute_set_name', $attributeSetName); + $searchCriteria = $this->searchCriteriaBuilder->create(); + $result = $this->attributeSetRepository->getList($searchCriteria); + $items = $result->getItems(); + + return array_pop($items); + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/ResourceModel/GetEntityIdByAttributeId.php b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/ResourceModel/GetEntityIdByAttributeId.php new file mode 100644 index 0000000000000..edb75a5d8d1bd --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Eav/Model/ResourceModel/GetEntityIdByAttributeId.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Eav\Model\ResourceModel; + +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Set as AttributeSetResource; + +/** + * Search and return attribute data from eav entity attribute table. + */ +class GetEntityIdByAttributeId +{ + /** + * @var AttributeSetResource + */ + private $attributeSetResource; + + /** + * @param AttributeSetResource $setResource + */ + public function __construct( + AttributeSetResource $setResource + ) { + $this->attributeSetResource = $setResource; + } + + /** + * Returns entity attribute by id. + * + * @param int $setId + * @param int $attributeId + * @return int|null + */ + public function execute(int $setId, int $attributeId): ?int + { + $select = $this->attributeSetResource->getConnection()->select() + ->from($this->attributeSetResource->getTable('eav_entity_attribute')) + ->where('attribute_set_id = ?', $setId) + ->where('attribute_id = ?', $attributeId); + + $result = $this->attributeSetResource->getConnection()->fetchOne($select); + return $result ? (int)$result : null; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Helper/Memory.php b/dev/tests/integration/framework/Magento/TestFramework/Helper/Memory.php index 0924bf8b7db0b..933d86fe6a4f5 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Helper/Memory.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Helper/Memory.php @@ -10,6 +10,9 @@ */ namespace Magento\TestFramework\Helper; +/** + * Integration Test Framework memory management logic. + */ class Memory { /** @@ -38,21 +41,21 @@ public function __construct(\Magento\Framework\Shell $shell) /** * Retrieve the effective memory usage of the current process * - * memory_get_usage() cannot be used because of the bug - * @link https://bugs.php.net/bug.php?id=62467 + * Function memory_get_usage() cannot be used because of the bug * + * @link https://bugs.php.net/bug.php?id=62467 * @return int Memory usage in bytes */ public function getRealMemoryUsage() { $pid = getmypid(); try { + // fall back to the Unix command line + $result = $this->_getUnixProcessMemoryUsage($pid); + } catch (\Magento\Framework\Exception\LocalizedException $e) { // try to use the Windows command line // some ports of Unix commands on Windows, such as MinGW, have limited capabilities and cannot be used $result = $this->_getWinProcessMemoryUsage($pid); - } catch (\Magento\Framework\Exception\LocalizedException $e) { - // fall back to the Unix command line - $result = $this->_getUnixProcessMemoryUsage($pid); } return $result; } @@ -100,9 +103,11 @@ protected function _getWinProcessMemoryUsage($pid) * @return int * @throws \InvalidArgumentException * @throws \OutOfBoundsException + * phpcs:disable Magento2.Functions.StaticFunction */ public static function convertToBytes($number) { + // phpcs:enable Magento2.Functions.StaticFunction if (!preg_match('/^(.*\d)\h*(\D)$/', $number, $matches)) { throw new \InvalidArgumentException("Number format '{$number}' is not recognized."); } @@ -132,12 +137,14 @@ public static function convertToBytes($number) * - but the value has only one delimiter, such as "234,56", then it is impossible to know whether it is decimal * separator or not. Only knowing the right format would allow this. * - * @param $number + * @param string $number * @return string * @throws \InvalidArgumentException + * phpcs:disable Magento2.Functions.StaticFunction */ protected static function _convertToNumber($number) { + // phpcs:enable Magento2.Functions.StaticFunction preg_match_all('/(\D+)/', $number, $matches); if (count(array_unique($matches[0])) > 1) { throw new \InvalidArgumentException( @@ -152,9 +159,11 @@ protected static function _convertToNumber($number) * * @link http://php.net/manual/en/function.php-uname.php * @return boolean + * phpcs:disable Magento2.Functions.StaticFunction */ public static function isMacOs() { + // phpcs:enable Magento2.Functions.StaticFunction return strtoupper(PHP_OS) === 'DARWIN'; } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Quote/Model/GetQuoteByReservedOrderId.php b/dev/tests/integration/framework/Magento/TestFramework/Quote/Model/GetQuoteByReservedOrderId.php new file mode 100644 index 0000000000000..bd83bc595692f --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Quote/Model/GetQuoteByReservedOrderId.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Quote\Model; + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\CartInterface; + +/** + * Search and return quote by reserved order id. + */ +class GetQuoteByReservedOrderId +{ + /** @var SearchCriteriaBuilder */ + private $searchCriteriaBuilder; + + /** @var CartRepositoryInterface */ + private $cartRepository; + + /** + * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @param CartRepositoryInterface $cartRepository + */ + public function __construct(SearchCriteriaBuilder $searchCriteriaBuilder, CartRepositoryInterface $cartRepository) + { + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->cartRepository = $cartRepository; + } + + /** + * Return quote by reserved order id. + * + * @param string $reservedOrderId + * @return CartInterface|null + */ + public function execute(string $reservedOrderId): ?CartInterface + { + $searchCriteria = $this->searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId)->create(); + $quotes = $this->cartRepository->getList($searchCriteria)->getItems(); + + return array_shift($quotes); + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/SalesRule/Model/GetSalesRuleByName.php b/dev/tests/integration/framework/Magento/TestFramework/SalesRule/Model/GetSalesRuleByName.php new file mode 100644 index 0000000000000..db94d9ee97e04 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/SalesRule/Model/GetSalesRuleByName.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\SalesRule\Model; + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\SalesRule\Api\RuleRepositoryInterface; +use Magento\SalesRule\Api\Data\RuleInterface; + +/** + * Search and return Sales rule by name. + */ +class GetSalesRuleByName +{ + /** @var SearchCriteriaBuilder */ + private $searchCriteriaBuilder; + + /** @var RuleRepositoryInterface */ + private $ruleRepository; + + /** + * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @param RuleRepositoryInterface $ruleRepository + */ + public function __construct(SearchCriteriaBuilder $searchCriteriaBuilder, RuleRepositoryInterface $ruleRepository) + { + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->ruleRepository = $ruleRepository; + } + + /** + * Return Sales Rule by name. + * + * @param string $name + * @return RuleInterface|null + */ + public function execute(string $name): ?RuleInterface + { + $searchCriteria = $this->searchCriteriaBuilder->addFilter('name', $name)->create(); + $salesRules = $this->ruleRepository->getList($searchCriteria)->getItems(); + + return array_shift($salesRules); + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Swatches/Model/Attribute/DataProvider/AbstractSwatchAttributeData.php b/dev/tests/integration/framework/Magento/TestFramework/Swatches/Model/Attribute/DataProvider/AbstractSwatchAttributeData.php new file mode 100644 index 0000000000000..ce9a6b551986c --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Swatches/Model/Attribute/DataProvider/AbstractSwatchAttributeData.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Swatches\Model\Attribute\DataProvider; + +use Magento\TestFramework\Eav\Model\Attribute\DataProvider\AbstractAttributeDataWithOptions; + +/** + * Base attribute data for swatch attributes. + */ +abstract class AbstractSwatchAttributeData extends AbstractAttributeDataWithOptions +{ + /** + * @inheritdoc + */ + public function __construct() + { + parent::__construct(); + $this->defaultAttributePostData = array_replace( + $this->defaultAttributePostData, + [ + 'update_product_preview_image' => 0, + 'use_product_image_for_swatch' => 0, + 'visual_swatch_validation' => '', + 'visual_swatch_validation_unique' => '', + 'text_swatch_validation' => '', + 'text_swatch_validation_unique' => '', + 'used_for_sort_by' => 0, + ] + ); + $this->defaultAttributePostData['swatch_input_type'] = 'text'; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Swatches/Model/Attribute/DataProvider/TextSwatch.php b/dev/tests/integration/framework/Magento/TestFramework/Swatches/Model/Attribute/DataProvider/TextSwatch.php new file mode 100644 index 0000000000000..c63873469e2f8 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Swatches/Model/Attribute/DataProvider/TextSwatch.php @@ -0,0 +1,160 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Swatches\Model\Attribute\DataProvider; + +use Magento\Swatches\Model\Swatch; + +/** + * Product attribute data for attribute with input type visual swatch. + */ +class TextSwatch extends AbstractSwatchAttributeData +{ + /** + * @inheritdoc + */ + public function __construct() + { + parent::__construct(); + $this->defaultAttributePostData['swatch_input_type'] = 'text'; + } + + /** + * @inheritdoc + */ + public function getAttributeDataWithCheckArray(): array + { + return array_replace_recursive( + parent::getAttributeDataWithCheckArray(), + [ + "{$this->getFrontendInput()}_with_required_fields" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + "{$this->getFrontendInput()}_with_store_view_scope" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + "{$this->getFrontendInput()}_with_global_scope" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + "{$this->getFrontendInput()}_with_website_scope" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + "{$this->getFrontendInput()}_with_attribute_code" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + "{$this->getFrontendInput()}_with_unique_value" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + "{$this->getFrontendInput()}_without_unique_value" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + "{$this->getFrontendInput()}_with_enabled_add_to_column_options" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + "{$this->getFrontendInput()}_without_enabled_add_to_column_options" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + "{$this->getFrontendInput()}_with_enabled_use_in_filter_options" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + "{$this->getFrontendInput()}_without_enabled_use_in_filter_options" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + ] + ); + } + + /** + * @inheritdoc + */ + protected function getOptionsDataArr(): array + { + return [ + [ + 'optiontext' => [ + 'order' => [ + 'option_0' => '1', + ], + 'value' => [ + 'option_0' => [ + 0 => 'Admin value description 1', + 1 => 'Default store view value description 1', + ], + ], + 'delete' => [ + 'option_0' => '', + ], + ], + 'defaulttext' => [ + 0 => 'option_0', + ], + 'swatchtext' => [ + 'value' => [ + 'option_0' => [ + 0 => 'Admin value 1', + 1 => 'Default store view value 1', + ], + ], + ], + ], + [ + 'optiontext' => [ + 'order' => [ + 'option_1' => '2', + ], + 'value' => [ + 'option_1' => [ + 0 => 'Admin value description 2', + 1 => 'Default store view value description 2', + ], + ], + 'delete' => [ + 'option_1' => '', + ], + ], + 'swatchtext' => [ + 'value' => [ + 'option_1' => [ + 0 => 'Admin value 2', + 1 => 'Default store view value 2', + ], + ], + ], + ], + ]; + } + + /** + * @inheritdoc + */ + protected function getFrontendInput(): string + { + return Swatch::SWATCH_TYPE_TEXTUAL_ATTRIBUTE_FRONTEND_INPUT; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Swatches/Model/Attribute/DataProvider/VisualSwatch.php b/dev/tests/integration/framework/Magento/TestFramework/Swatches/Model/Attribute/DataProvider/VisualSwatch.php new file mode 100644 index 0000000000000..b5e32c40ef8a1 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Swatches/Model/Attribute/DataProvider/VisualSwatch.php @@ -0,0 +1,151 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Swatches\Model\Attribute\DataProvider; + +use Magento\Swatches\Model\Swatch; + +/** + * Product attribute data for attribute with input type visual swatch. + */ +class VisualSwatch extends AbstractSwatchAttributeData +{ + /** + * @inheritdoc + */ + public function __construct() + { + parent::__construct(); + $this->defaultAttributePostData['swatch_input_type'] = 'visual'; + } + + /** + * @inheritdoc + */ + public function getAttributeDataWithCheckArray(): array + { + return array_replace_recursive( + parent::getAttributeDataWithCheckArray(), + [ + "{$this->getFrontendInput()}_with_required_fields" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + "{$this->getFrontendInput()}_with_store_view_scope" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + "{$this->getFrontendInput()}_with_global_scope" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + "{$this->getFrontendInput()}_with_website_scope" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + "{$this->getFrontendInput()}_with_attribute_code" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + "{$this->getFrontendInput()}_with_unique_value" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + "{$this->getFrontendInput()}_without_unique_value" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + "{$this->getFrontendInput()}_with_enabled_add_to_column_options" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + "{$this->getFrontendInput()}_without_enabled_add_to_column_options" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + "{$this->getFrontendInput()}_with_enabled_use_in_filter_options" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + "{$this->getFrontendInput()}_without_enabled_use_in_filter_options" => [ + 1 => [ + 'frontend_input' => 'select', + ], + ], + ] + ); + } + + /** + * @inheritdoc + */ + protected function getOptionsDataArr(): array + { + return [ + [ + 'optionvisual' => [ + 'order' => [ + 'option_0' => '1', + ], + 'value' => [ + 'option_0' => [ + 0 => 'Admin black test 1', + 1 => 'Default store view black test 1', + ], + ], + 'delete' => [ + 'option_0' => '', + ] + ], + 'swatchvisual' => [ + 'value' => [ + 'option_0' => '#000000', + ] + ] + ], + [ + 'optionvisual' => [ + 'order' => [ + 'option_1' => '2', + ], + 'value' => [ + 'option_1' => [ + 0 => 'Admin white test 2', + 1 => 'Default store view white test 2', + ], + ], + 'delete' => [ + 'option_1' => '', + ], + ], + 'swatchvisual' => [ + 'value' => [ + 'option_1' => '#ffffff', + ], + ], + ], + ]; + } + + /** + * @inheritdoc + */ + protected function getFrontendInput(): string + { + return Swatch::SWATCH_TYPE_VISUAL_ATTRIBUTE_FRONTEND_INPUT; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractBackendController.php b/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractBackendController.php index 7a387bd41eec2..920cde4b7df09 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractBackendController.php +++ b/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractBackendController.php @@ -41,6 +41,13 @@ abstract class AbstractBackendController extends \Magento\TestFramework\TestCase */ protected $httpMethod; + /** + * Expected no access response + * + * @var int + */ + protected $expectedNoAccessResponseCode = 403; + /** * @inheritDoc * @@ -84,21 +91,6 @@ protected function tearDown() parent::tearDown(); } - /** - * Utilize backend session model by default - * - * @param \PHPUnit\Framework\Constraint\Constraint $constraint - * @param string|null $messageType - * @param string $messageManagerClass - */ - public function assertSessionMessages( - \PHPUnit\Framework\Constraint\Constraint $constraint, - $messageType = null, - $messageManagerClass = \Magento\Framework\Message\Manager::class - ) { - parent::assertSessionMessages($constraint, $messageType, $messageManagerClass); - } - /** * Test ACL configuration for action working. */ @@ -111,8 +103,8 @@ public function testAclHasAccess() $this->getRequest()->setMethod($this->httpMethod); } $this->dispatch($this->uri); - $this->assertNotSame(403, $this->getResponse()->getHttpResponseCode()); $this->assertNotSame(404, $this->getResponse()->getHttpResponseCode()); + $this->assertNotSame($this->expectedNoAccessResponseCode, $this->getResponse()->getHttpResponseCode()); } /** @@ -130,6 +122,6 @@ public function testAclNoAccess() ->getAcl() ->deny(null, $this->resource); $this->dispatch($this->uri); - $this->assertSame(403, $this->getResponse()->getHttpResponseCode()); + $this->assertSame($this->expectedNoAccessResponseCode, $this->getResponse()->getHttpResponseCode()); } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Weee/Model/Attribute/DataProvider/FixedProductTax.php b/dev/tests/integration/framework/Magento/TestFramework/Weee/Model/Attribute/DataProvider/FixedProductTax.php new file mode 100644 index 0000000000000..2f1f625ad48ac --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Weee/Model/Attribute/DataProvider/FixedProductTax.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Weee\Model\Attribute\DataProvider; + +use Magento\TestFramework\Eav\Model\Attribute\DataProvider\AbstractBaseAttributeData; + +/** + * Product attribute data for attribute with input type fixed product tax. + */ +class FixedProductTax extends AbstractBaseAttributeData +{ + /** + * @inheritdoc + */ + public function __construct() + { + parent::__construct(); + $this->defaultAttributePostData['used_for_sort_by'] = '0'; + } + + /** + * @inheritdoc + */ + public function getAttributeData(): array + { + $result = parent::getAttributeData(); + unset($result["{$this->getFrontendInput()}_with_default_value"]); + unset($result["{$this->getFrontendInput()}_without_default_value"]); + + return $result; + } + + /** + * @inheritdoc + */ + public function getAttributeDataWithCheckArray(): array + { + $result = parent::getAttributeDataWithCheckArray(); + unset($result["{$this->getFrontendInput()}_with_default_value"]); + unset($result["{$this->getFrontendInput()}_without_default_value"]); + + return $result; + } + + /** + * @inheritdoc + */ + protected function getFrontendInput(): string + { + return 'weee'; + } +} diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/a/aa/aaa/registration.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/a/aa/aaa/registration.php index 6dba3551281e3..b3a410e609d52 100644 --- a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/a/aa/aaa/registration.php +++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/a/aa/aaa/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Magento/theme', __DIR__); diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/a/aa/registration.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/a/aa/registration.php index 30dd39c36f2a4..045a53181a7d3 100644 --- a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/a/aa/registration.php +++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/a/aa/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'magento_language', __DIR__); diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/b/registration.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/b/registration.php index 8d3b9b440fd7d..8c3c7576dc3f4 100644 --- a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/b/registration.php +++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/b/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LIBRARY, 'magento/library', __DIR__); diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/registration.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/registration.php index ddc564ae5a143..effc8b6954787 100644 --- a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/registration.php +++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/_files/components/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_ModuleOne', __DIR__); diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Helper/MemoryTest.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Helper/MemoryTest.php index a033aba7e90b6..04a82607668ec 100644 --- a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Helper/MemoryTest.php +++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Helper/MemoryTest.php @@ -21,16 +21,7 @@ public function testGetRealMemoryUsageUnix() { $object = new \Magento\TestFramework\Helper\Memory($this->_shell); $this->_shell->expects( - $this->at(0) - )->method( - 'execute' - )->with( - $this->stringStartsWith('tasklist.exe ') - )->will( - $this->throwException(new \Magento\Framework\Exception\LocalizedException(__('command not found'))) - ); - $this->_shell->expects( - $this->at(1) + $this->once() )->method( 'execute' )->with( @@ -44,7 +35,16 @@ public function testGetRealMemoryUsageUnix() public function testGetRealMemoryUsageWin() { $this->_shell->expects( - $this->once() + $this->at(0) + )->method( + 'execute' + )->with( + $this->stringStartsWith('ps ') + )->will( + $this->throwException(new \Magento\Framework\Exception\LocalizedException(__('command not found'))) + ); + $this->_shell->expects( + $this->at(1) )->method( 'execute' )->with( diff --git a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricingTest.php b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricingTest.php index b4f38d207c1f4..ce0cc79b0a5e0 100644 --- a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricingTest.php +++ b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricingTest.php @@ -6,6 +6,7 @@ namespace Magento\AdvancedPricingImportExport\Model\Export; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\File\Csv; use Magento\TestFramework\Indexer\TestCase; use Magento\TestFramework\Helper\Bootstrap; use Magento\Framework\Filesystem; @@ -19,6 +20,8 @@ /** * Advanced pricing test + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class AdvancedPricingTest extends TestCase { @@ -161,6 +164,110 @@ public function testExportMultipleWebsites() } } + /** + * Export and Import of Advanced Pricing with different Price Types. + * + * @magentoDataFixture Magento/Catalog/_files/two_simple_products_with_tier_price.php + * @return void + */ + public function testExportImportOfAdvancedPricing(): void + { + $csvfile = uniqid('importexport_') . '.csv'; + $exportContent = $this->exportData($csvfile); + $this->assertContains( + 'second_simple,"All Websites [USD]","ALL GROUPS",10.0000,3.00,Discount', + $exportContent + ); + $this->assertContains( + 'simple,"All Websites [USD]",General,5.0000,95.000000,Fixed', + $exportContent + ); + $this->updateTierPriceDataInCsv($csvfile); + $this->importData($csvfile); + + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $firstProductTierPrices = $productRepository->get('simple')->getTierPrices(); + $secondProductTierPrices = $productRepository->get('second_simple')->getTierPrices(); + + $this->assertSame( + ['0', '1'], + [ + $firstProductTierPrices[0]->getExtensionAttributes()->getWebsiteId(), + $firstProductTierPrices[0]->getCustomerGroupId(), + ] + ); + + $this->assertEquals( + ['5.0000', '90.000000'], + [ + $firstProductTierPrices[0]->getQty(), + $firstProductTierPrices[0]->getValue(), + ], + '', + 0.1 + ); + + $this->assertSame( + ['0', \Magento\Customer\Model\Group::CUST_GROUP_ALL], + [ + $secondProductTierPrices[0]->getExtensionAttributes()->getWebsiteId(), + $secondProductTierPrices[0]->getCustomerGroupId(), + ] + ); + + $this->assertEquals( + ['5.00', '10.0000'], + [ + $secondProductTierPrices[0]->getExtensionAttributes()->getPercentageValue(), + $secondProductTierPrices[0]->getQty(), + ], + '', + 0.1 + ); + } + + /** + * Update tier price data in CSV. + * + * @param string $csvfile + * @return void + */ + private function updateTierPriceDataInCsv(string $csvfile): void + { + $csvNewData = [ + 0 => [ + 0 => 'sku', + 1 => 'tier_price_website', + 2 => 'tier_price_customer_group', + 3 => 'tier_price_qty', + 4 => 'tier_price', + 5 => 'tier_price_value_type', + ], + 1 => [ + 0 => 'simple', + 1 => 'All Websites [USD]', + 2 => 'General', + 3 => '5', + 4 => '90', + 5 => 'Fixed', + ], + 2 => [ + 0 => 'second_simple', + 1 => 'All Websites [USD]', + 2 => 'ALL GROUPS', + 3 => '10', + 4 => '5', + 5 => 'Discount', + ], + ]; + + /** @var Csv $csv */ + $csv = $this->objectManager->get(Csv::class); + $varDirectory = $this->fileSystem->getDirectoryWrite(DirectoryList::VAR_DIR); + $csv->appendData($varDirectory->getAbsolutePath($csvfile), $csvNewData); + } + /** * @param string $csvFile * @return string diff --git a/dev/tests/integration/testsuite/Magento/AsynchronousOperations/_files/operation_searchable.php b/dev/tests/integration/testsuite/Magento/AsynchronousOperations/_files/operation_searchable.php index e74f995f8b57b..7e0d7594c3510 100644 --- a/dev/tests/integration/testsuite/Magento/AsynchronousOperations/_files/operation_searchable.php +++ b/dev/tests/integration/testsuite/Magento/AsynchronousOperations/_files/operation_searchable.php @@ -52,7 +52,22 @@ 'error_code' => 2222, 'result_message' => 'Entity with ID=4 does not exist', ], - + [ + 'bulk_uuid' => 'bulk-uuid-searchable-6', + 'topic_name' => 'topic-5', + 'serialized_data' => json_encode(['entity_id' => 5]), + 'status' => OperationInterface::STATUS_TYPE_OPEN, + 'error_code' => null, + 'result_message' => '', + ], + [ + 'bulk_uuid' => 'bulk-uuid-searchable-6', + 'topic_name' => 'topic-5', + 'serialized_data' => json_encode(['entity_id' => 5]), + 'status' => OperationInterface::STATUS_TYPE_OPEN, + 'error_code' => null, + 'result_message' => '', + ] ]; $bulkQuery = "INSERT INTO {$bulkTable} (`uuid`, `user_id`, `description`, `operation_count`, `start_time`)" diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/Tab/Products/ViewedTest.php b/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/Tab/Products/ViewedTest.php index 0c400cd76b9a1..f1a9e97ecaa9c 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/Tab/Products/ViewedTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Block/Dashboard/Tab/Products/ViewedTest.php @@ -49,6 +49,7 @@ protected function setUp() * @magentoDataFixture Magento/Catalog/_files/product_simple.php * @magentoDbIsolation enabled * @magentoAppIsolation enabled + * @magentoConfigFixture default/reports/options/enabled 1 */ public function testGetPreparedCollectionProductPrice() { @@ -57,9 +58,11 @@ public function testGetPreparedCollectionProductPrice() $product = $this->productRepository->getById(1); $this->eventManager->dispatch('catalog_controller_product_view', ['product' => $product]); + $collection = $viewedProductsTabBlock->getPreparedCollection(); + $this->assertEquals( 10, - $viewedProductsTabBlock->getPreparedCollection()->getFirstItem()->getDataByKey('price') + $collection->getFirstItem()->getDataByKey('price') ); } } diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/_files/design/adminhtml/Magento/test_default/Magento_Backend/layout/layout_test_grid_handle.xml b/dev/tests/integration/testsuite/Magento/Backend/Block/_files/design/adminhtml/Magento/test_default/Magento_Backend/layout/layout_test_grid_handle.xml index 621e9f13409f1..70bf2af6a5fcf 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Block/_files/design/adminhtml/Magento/test_default/Magento_Backend/layout/layout_test_grid_handle.xml +++ b/dev/tests/integration/testsuite/Magento/Backend/Block/_files/design/adminhtml/Magento/test_default/Magento_Backend/layout/layout_test_grid_handle.xml @@ -55,12 +55,12 @@ <item name="option_id1" xsi:type="array"> <item name="label" xsi:type="string" translate="true">Option One</item> <item name="url" xsi:type="string">*/*/option1</item> - <item name="complete" xsi:type="string">Test</item> + <item name="complete" xsi:type="string" translate="true">Test</item> </item> <item name="option_id2" xsi:type="array"> <item name="label" xsi:type="string" translate="true">Option Two</item> <item name="url" xsi:type="string">*/*/option2</item> - <item name="confirm" xsi:type="string">Are you sure?</item> + <item name="confirm" xsi:type="string" translate="true">Are you sure?</item> </item> <item name="option_id3" xsi:type="array"> <item name="label" xsi:type="string" translate="true">Option Three</item> diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/_files/design/adminhtml/Magento/test_default/registration.php b/dev/tests/integration/testsuite/Magento/Backend/Block/_files/design/adminhtml/Magento/test_default/registration.php index 89cf740bbd4f4..31ad793efca38 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Block/_files/design/adminhtml/Magento/test_default/registration.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Block/_files/design/adminhtml/Magento/test_default/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'adminhtml/BackendTest/test_default', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/Dashboard/AjaxBlockTest.php b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/Dashboard/AjaxBlockTest.php new file mode 100644 index 0000000000000..3deb225cead18 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/Dashboard/AjaxBlockTest.php @@ -0,0 +1,69 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Backend\Controller\Adminhtml\Dashboard; + +use Magento\TestFramework\TestCase\AbstractBackendController; +use Magento\Framework\App\Request\Http as HttpRequest; + +/** + * @magentoAppArea adminhtml + */ +class AjaxBlockTest extends AbstractBackendController +{ + /** + * Test execute to check render block + * + * @dataProvider ajaxBlockDataProvider + */ + public function testExecute($block, $expectedResult) + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setParam('block', $block); + + $this->dispatch('backend/admin/dashboard/ajaxBlock/'); + + $this->assertEquals(200, $this->getResponse()->getHttpResponseCode()); + + $actual = $this->getResponse()->getBody(); + + $this->assertContains($expectedResult, $actual); + } + + /** + * Provides POST data and Expected Result + * + * @return array + */ + public function ajaxBlockDataProvider() + { + return [ + [ + 'tab_orders', + 'order_orders_period' + ], + [ + 'tab_amounts', + 'order_amounts_period' + ], + [ + 'totals', + 'dashboard_diagram_totals' + ], + [ + '', + '' + ], + [ + 'test_block', + '' + ] + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/Dashboard/ProductsViewedTest.php b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/Dashboard/ProductsViewedTest.php index bd4dd0c8daf0c..4466434caef0e 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/Dashboard/ProductsViewedTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Controller/Adminhtml/Dashboard/ProductsViewedTest.php @@ -15,6 +15,7 @@ class ProductsViewedTest extends \Magento\TestFramework\TestCase\AbstractBackend /** * @magentoAppArea adminhtml * @magentoDataFixture Magento/Reports/_files/viewed_products.php + * @magentoConfigFixture default/reports/options/enabled 1 */ public function testExecute() { diff --git a/dev/tests/integration/testsuite/Magento/Backend/Model/Auth/SessionTest.php b/dev/tests/integration/testsuite/Magento/Backend/Model/Auth/SessionTest.php index 5ef518fd2152f..42b1c10ee301e 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Model/Auth/SessionTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Model/Auth/SessionTest.php @@ -22,15 +22,10 @@ class SessionTest extends \PHPUnit\Framework\TestCase private $auth; /** - * @var Session + * @var \Magento\Backend\Model\Auth\Session */ private $authSession; - /** - * @var SessionFactory - */ - private $authSessionFactory; - /** * @var \Magento\Framework\ObjectManagerInterface */ @@ -43,8 +38,7 @@ protected function setUp() $this->objectManager->get(\Magento\Framework\Config\ScopeInterface::class) ->setCurrentScope(\Magento\Backend\App\Area\FrontNameResolver::AREA_CODE); $this->auth = $this->objectManager->create(\Magento\Backend\Model\Auth::class); - $this->authSession = $this->objectManager->create(Session::class); - $this->authSessionFactory = $this->objectManager->get(SessionFactory::class); + $this->authSession = $this->objectManager->create(\Magento\Backend\Model\Auth\Session::class); $this->auth->setAuthStorage($this->authSession); $this->auth->logout(); } @@ -73,55 +67,4 @@ public function loginDataProvider() { return [[false], [true]]; } - - /** - * Check that persisting user data is working. - */ - public function testStorage() - { - $this->auth->login(TestHelper::ADMIN_NAME, TestHelper::ADMIN_PASSWORD); - $user = $this->authSession->getUser(); - $acl = $this->authSession->getAcl(); - /** @var Session $session */ - $session = $this->authSessionFactory->create(); - $persistedUser = $session->getUser(); - $persistedAcl = $session->getAcl(); - - $this->assertEquals($user->getData(), $persistedUser->getData()); - $this->assertEquals($user->getAclRole(), $persistedUser->getAclRole()); - $this->assertEquals($acl->getRoles(), $persistedAcl->getRoles()); - $this->assertEquals($acl->getResources(), $persistedAcl->getResources()); - } - - /** - * Check that session manager can work with user storage in the old way. - */ - public function testInnerStorage(): void - { - /** @var \Magento\Framework\Session\StorageInterface $innerStorage */ - $innerStorage = Bootstrap::getObjectManager()->get(\Magento\Framework\Session\StorageInterface::class); - $this->authSession = $this->authSessionFactory->create(['storage' => $innerStorage]); - $this->auth->login(TestHelper::ADMIN_NAME, TestHelper::ADMIN_PASSWORD); - $user = $this->auth->getAuthStorage()->getUser(); - $acl = $this->auth->getAuthStorage()->getAcl(); - $this->assertNotEmpty($user); - $this->assertNotEmpty($acl); - $this->auth->logout(); - $this->assertEmpty($this->auth->getAuthStorage()->getUser()); - $this->assertEmpty($this->auth->getAuthStorage()->getAcl()); - $this->authSession->setUser($user); - $this->authSession->setAcl($acl); - $this->assertTrue($user === $this->authSession->getUser()); - $this->assertTrue($acl === $this->authSession->getAcl()); - $this->authSession->destroy(); - $innerStorage->setUser($user); - $innerStorage->setAcl($acl); - $this->assertTrue($user === $this->authSession->getUser()); - $this->assertTrue($acl === $this->authSession->getAcl()); - /** @var Session $newSession */ - $newSession = $this->authSessionFactory->create(['storage' => $innerStorage]); - $this->assertTrue($newSession->hasUser()); - $this->assertTrue($newSession->hasAcl()); - $this->assertEquals($user->getId(), $newSession->getUser()->getId()); - } } diff --git a/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php b/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php index a930244238efa..2754e055b71fe 100644 --- a/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php +++ b/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php @@ -3,12 +3,10 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Backend\Model\Locale; use Magento\Framework\Locale\Resolver; use Magento\TestFramework\Helper\Bootstrap; -use Magento\User\Model\User; /** * @magentoAppArea adminhtml @@ -20,9 +18,6 @@ class ResolverTest extends \PHPUnit\Framework\TestCase */ protected $_model; - /** - * {@inheritDoc} - */ protected function setUp() { parent::setUp(); @@ -32,7 +27,7 @@ protected function setUp() } /** - * Tests setLocale() with default locale + * @covers \Magento\Backend\Model\Locale\Resolver::setLocale */ public function testSetLocaleWithDefaultLocale() { @@ -40,11 +35,11 @@ public function testSetLocaleWithDefaultLocale() } /** - * Tests setLocale() with interface locale + * @covers \Magento\Backend\Model\Locale\Resolver::setLocale */ public function testSetLocaleWithBaseInterfaceLocale() { - $user = Bootstrap::getObjectManager()->create(User::class); + $user = new \Magento\Framework\DataObject(); $session = Bootstrap::getObjectManager()->get( \Magento\Backend\Model\Auth\Session::class ); @@ -58,7 +53,7 @@ public function testSetLocaleWithBaseInterfaceLocale() } /** - * Tests setLocale() with session locale + * @covers \Magento\Backend\Model\Locale\Resolver::setLocale */ public function testSetLocaleWithSessionLocale() { @@ -71,7 +66,7 @@ public function testSetLocaleWithSessionLocale() } /** - * Tests setLocale() with post parameter + * @covers \Magento\Backend\Model\Locale\Resolver::setLocale */ public function testSetLocaleWithRequestLocale() { @@ -119,7 +114,7 @@ public function setLocaleWithParameterDataProvider(): array * @param string $localeCodeToCheck * @return void */ - private function _checkSetLocale($localeCodeToCheck) + protected function _checkSetLocale($localeCodeToCheck) { $this->_model->setLocale(); $localeCode = $this->_model->getLocale(); diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/assign_items_per_address.php b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/assign_items_per_address.php index 91cea7dc96602..56bf2c53f6728 100644 --- a/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/assign_items_per_address.php +++ b/dev/tests/integration/testsuite/Magento/Braintree/Fixtures/assign_items_per_address.php @@ -30,7 +30,7 @@ // assign virtual product to the billing address $billingAddress = $quote->getBillingAddress(); -$virtualItem = $items[sizeof($items) - 1]; +$virtualItem = $items[count($items) - 1]; $billingAddress->setTotalQty(1); $billingAddress->addItem($virtualItem); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/BundlePriceAbstract.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/BundlePriceAbstract.php index e30916810b1e0..2a7b80e62797d 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/BundlePriceAbstract.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/BundlePriceAbstract.php @@ -3,11 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Bundle\Model\Product; /** * Abstract class for testing bundle prices + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ abstract class BundlePriceAbstract extends \PHPUnit\Framework\TestCase @@ -29,6 +31,14 @@ abstract class BundlePriceAbstract extends \PHPUnit\Framework\TestCase */ protected $productCollectionFactory; + /** + * @var \Magento\CatalogRule\Model\RuleFactory + */ + private $ruleFactory; + + /** + * @inheritdoc + */ protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); @@ -42,15 +52,19 @@ protected function setUp() true, \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); + $this->ruleFactory = $this->objectManager->get(\Magento\CatalogRule\Model\RuleFactory::class); } /** - * Get test cases + * Get test cases. + * * @return array */ abstract public function getTestCases(); /** + * Prepare fixture. + * * @param array $strategyModifiers * @param string $productSku * @return void @@ -61,11 +75,14 @@ abstract public function getTestCases(); */ protected function prepareFixture($strategyModifiers, $productSku) { + $this->ruleFactory->create()->clearPriceRulesData(); + $bundleProduct = $this->productRepository->get($productSku); foreach ($strategyModifiers as $modifier) { if (method_exists($this, $modifier['modifierName'])) { array_unshift($modifier['data'], $bundleProduct); + // phpcs:ignore Magento2.Functions.DiscouragedFunction $bundleProduct = call_user_func_array([$this, $modifier['modifierName']], $modifier['data']); } else { throw new \Magento\Framework\Exception\InputException( @@ -113,6 +130,8 @@ protected function addSimpleProduct(\Magento\Catalog\Model\Product $bundleProduc } /** + * Add custom option. + * * @param \Magento\Catalog\Model\Product $bundleProduct * @param array $optionsData * @return \Magento\Catalog\Model\Product diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundleWithCatalogPriceRuleCalculatorTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundleWithCatalogPriceRuleCalculatorTest.php index b3c46c75bcfb7..2b0e8c75a15e0 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundleWithCatalogPriceRuleCalculatorTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/FixedBundleWithCatalogPriceRuleCalculatorTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Bundle\Model\Product; diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options.php index 03949115ea62c..c79e943ba4be3 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options.php @@ -8,7 +8,7 @@ use Magento\TestFramework\Helper\Bootstrap; -require __DIR__ . 'product_with_multiple_options.php'; +require __DIR__ . '/product_with_multiple_options.php'; $objectManager = Bootstrap::getObjectManager(); @@ -49,6 +49,14 @@ $cart->getQuote()->setReservedOrderId('test_cart_with_bundle_and_options'); $cart->save(); +/** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */ +$quoteIdMask = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Quote\Model\QuoteIdMaskFactory::class) + ->create(); +$quoteIdMask->setQuoteId($cart->getQuote()->getId()); +$quoteIdMask->setDataChanges(true); +$quoteIdMask->save(); + /** @var $objectManager \Magento\TestFramework\ObjectManager */ $objectManager = Bootstrap::getObjectManager(); $objectManager->removeSharedInstance(\Magento\Checkout\Model\Session::class); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options_rollback.php index d32d6fab33319..591aec9190f9f 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/quote_with_bundle_and_options_rollback.php @@ -22,7 +22,7 @@ $quoteIdMask = $objectManager->create(\Magento\Quote\Model\QuoteIdMask::class); $quoteIdMask->delete($quote->getId()); -require __DIR__ . 'product_with_multiple_options_rollback.php'; +require __DIR__ . '/product_with_multiple_options_rollback.php'; $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/BreadcrumbsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/BreadcrumbsTest.php new file mode 100644 index 0000000000000..050ac6d0f55f9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/BreadcrumbsTest.php @@ -0,0 +1,72 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block; + +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Theme\Block\Html\Breadcrumbs as ThemeBreadcrumbs; +use PHPUnit\Framework\TestCase; + +/** + * Checks the behavior of breadcrumbs on the category view page. + * + * @magentoAppArea frontend + */ +class BreadcrumbsTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var LayoutInterface */ + private $layout; + + /** @var CategoryRepositoryInterface */ + private $categoryRepository; + + /** @var Registry */ + private $registry; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->categoryRepository = $this->objectManager->create(CategoryRepositoryInterface::class); + $this->registry = $this->objectManager->get(Registry::class); + $this->layout = $this->objectManager->get(LayoutInterface::class); + } + + /** + * Checks the order of categories in breadcrumbs. + * + * @magentoDataFixture Magento/Catalog/_files/category_tree.php + * @return void + */ + public function testCategoriesSequence(): void + { + $category = $this->categoryRepository->get(402); + $this->registry->register('current_category', $category); + $themeBreadcrumbs = $this->layout->createBlock(ThemeBreadcrumbs::class, 'breadcrumbs'); + $this->layout->createBlock(Breadcrumbs::class); + $html = $themeBreadcrumbs->toHtml(); + + $actualCategories = preg_replace('/\s+/', '', strip_tags($html)); + $expectedCategories = __('Home') . 'Category1' . 'Category1.1' . 'Category1.1.1'; + self::assertEquals( + $expectedCategories, + $actualCategories, + 'The order of categories in breadcrumbs is not correct!' + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Category/TopMenuTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Category/TopMenuTest.php new file mode 100644 index 0000000000000..00ac61272bddc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Category/TopMenuTest.php @@ -0,0 +1,528 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Category; + +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\CategoryFactory; +use Magento\Catalog\Model\ResourceModel\Category\Collection; +use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; +use Magento\Framework\Data\Tree\Node; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Theme\Block\Html\Topmenu; +use PHPUnit\Framework\TestCase; + +/** + * Class checks top menu link behaviour. + * + * @magentoAppArea frontend + * @magentoDbIsolation enabled + */ +class TopMenuTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var LayoutInterface */ + private $layout; + + /** @var Topmenu */ + private $block; + + /** @var CategoryFactory */ + private $categoryFactory; + + /** @var CategoryRepositoryInterface */ + private $categoryRepository; + + /** @var StoreManagerInterface */ + private $storeManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->categoryFactory = $this->objectManager->get(CategoryFactory::class); + $this->categoryRepository = $this->objectManager->create(CategoryRepositoryInterface::class); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->block = $this->layout->createBlock(Topmenu::class); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + } + + /** + * Checks menu item displaying. + * + * @magentoDataFixture Magento/Catalog/_files/category.php + * @return void + */ + public function testTopMenuItemDisplay(): void + { + $output = $this->block->getHtml('level-top', 'submenu', 0); + $this->assertContains('Category 1', $output); + } + + /** + * Checks that menu item is not displayed if the category is disabled or include in menu is disabled. + * + * @dataProvider invisibilityDataProvider + * @param array $data + * @return void + */ + public function testTopMenuItemInvisibility(array $data): void + { + $category = $this->categoryFactory->create(); + $category->setData($data); + $this->categoryRepository->save($category); + $output = $this->block->getHtml('level-top', 'submenu', 0); + $this->assertEmpty($output, 'The category is displayed in top menu navigation'); + } + + /** + * @return array + */ + public function invisibilityDataProvider(): array + { + return [ + 'include_in_menu_disable' => [ + 'data' => [ + 'name' => 'Test Category', + 'path' => '1/2/', + 'is_active' => '1', + 'include_in_menu' => false, + ], + ], + 'category_disable' => [ + 'data' => [ + 'name' => 'Test Category 2', + 'path' => '1/2/', + 'is_active' => false, + 'include_in_menu' => true, + ], + ], + ]; + } + + /** + * Check category visibility in the category tree in the menu + * + * @dataProvider categoriesVisibleInTreeProvider + * @magentoDataFixture Magento/Catalog/_files/category_tree.php + * @magentoAppIsolation enabled + * @param array $categories + * @param array $expectedCategories + * @return void + */ + public function testCategoriesInTreeVisible(array $categories, array $expectedCategories): void + { + $this->updateCategories($categories); + $output = $this->block->getHtml('level-top', 'submenu', 0); + foreach ($expectedCategories as $data) { + $this->assertContains( + $data['name'], + $output, + 'Category ' . $data['name'] . ' should appear in the menu!' + ); + } + } + + /** + * @return array + */ + public function categoriesVisibleInTreeProvider(): array + { + return [ + 'add_in_tree_visible' => [ + 'categories' => [ + [ + 'is_new_category' => true, + 'parent_name' => 'Category 1.1.1', + Category::KEY_NAME => 'Sub Category 1', + Category::KEY_IS_ACTIVE => true, + ], + ], + 'expectedCategories' => [ + [ + 'name' => 'Sub Category 1', + ], + ], + ], + 'child_visible_in_tree' => [ + 'categories' => [ + [ + 'is_new_category' => true, + 'parent_name' => 'Category 1.1', + Category::KEY_NAME => 'Sub Category 1', + Category::KEY_IS_ACTIVE => true, + ], + [ + 'is_new_category' => false, + 'category_name' => 'Category 1.1', + Category::KEY_IS_ACTIVE => false, + ], + ], + 'expectedCategories' => [ + [ + 'name' => 'Sub Category 1', + ], + [ + 'name' => 'Category 1.1.1', + ], + ], + ], + ]; + } + + /** + * Check invisibility of a category in the category tree in the menu + * + * @dataProvider categoriesInTreeInvisibleProvider + * @magentoDataFixture Magento/Catalog/_files/category_tree.php + * @magentoAppIsolation enabled + * @param array $categories + * @param array $expectedCategories + * @return void + */ + public function testCategoriesInTreeInvisible(array $categories, array $expectedCategories): void + { + $this->updateCategories($categories); + $output = $this->block->getHtml('level-top', 'submenu', 0); + foreach ($expectedCategories as $data) { + $this->assertNotContains( + $data['name'], + $output, + 'Category ' . $data['name'] . ' should not appear in the menu!' + ); + } + } + + /** + * @return array + */ + public function categoriesInTreeInvisibleProvider(): array + { + return [ + 'add_in_tree_category_disable' => [ + 'categories' => [ + [ + 'is_new_category' => true, + 'parent_name' => 'Category 1.1.1', + Category::KEY_NAME => 'Sub Category 1', + Category::KEY_IS_ACTIVE => false, + Category::KEY_INCLUDE_IN_MENU => true, + ], + ], + 'expectedCategories' => [ + [ + 'name' => 'Sub Category 1', + ], + ], + ], + 'add_in_tree_include_in_menu_disable' => [ + 'categories' => [ + [ + 'is_new_category' => true, + 'parent_name' => 'Category 1.1.1', + Category::KEY_NAME => 'Sub Category 1', + Category::KEY_IS_ACTIVE => true, + Category::KEY_INCLUDE_IN_MENU => false, + ], + ], + 'expectedCategories' => [ + [ + 'name' => 'Sub Category 1', + ], + ], + ], + 'child_invisible_in_tree' => [ + 'categories' => [ + [ + 'is_new_category' => true, + 'parent_name' => 'Default Category', + Category::KEY_NAME => 'Sub Category 1', + Category::KEY_IS_ACTIVE => true, + ], + [ + 'is_new_category' => false, + 'category_name' => 'Category 1', + Category::KEY_IS_ACTIVE => false, + ], + ], + 'expectedCategories' => [ + [ + 'name' => 'Category 1.1', + ], + [ + 'name' => 'Category 1.1.1', + ], + ], + ], + ]; + } + + /** + * Check menu structure after moving category or changing position + * + * @dataProvider menuStructureProvider + * @magentoDataFixture Magento/Catalog/_files/categories_no_products_with_two_tree.php + * @magentoAppIsolation enabled + * @param array $moveCategory + * @param array $expectedMenuTree + * @return void + */ + public function testMenuStructure(array $moveCategory, array $expectedMenuTree): void + { + /** @var Category $category */ + $category = $this->categoryRepository->get($this->getCategoryIdByName($moveCategory['name'])); + $category->move( + $this->getCategoryIdByName($moveCategory['parent_name']), + $this->getCategoryIdByName($moveCategory['after_category_name']) + ); + + $this->block->getHtml('level-top', 'submenu', 0); + + $menuTree = $this->getMenuTree($this->block->getMenu()); + $topLevelKeys = array_flip(array_keys($expectedMenuTree)); + $actualMenuTree = array_intersect_key($menuTree, $topLevelKeys); + $this->assertEquals( + $expectedMenuTree, + $actualMenuTree, + 'Error in displaying the menu tree after moving a category!' + ); + } + + /** + * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function menuStructureProvider(): array + { + return [ + 'move_to_default' => [ + 'moveCategory' => [ + 'name' => 'Category 1.1.1', + 'parent_name' => 'Default Category', + 'after_category_name' => '', + ], + 'expectedMenuTree' => [ + 'Category 1.1.1' => ['position' => '1'], + 'Category 1' => [ + 'position' => '2', + 'Category 1.1' => ['position' => '2-1'], + ], + ], + ], + 'move_to_not_default' => [ + 'moveCategory' => [ + 'name' => 'Movable Position 3', + 'parent_name' => 'Movable Position 2', + 'after_category_name' => '', + ], + 'expectedMenuTree' => [ + 'Movable Position 2' => [ + 'position' => '5', + 'Movable Position 3' => ['position' => '5-1'], + ], + 'Category 12' => ['position' => '6'], + ], + ], + 'move_tree_to_default' => [ + 'moveCategory' => [ + 'name' => 'Category 1.1', + 'parent_name' => 'Default Category', + 'after_category_name' => '', + ], + 'expectedMenuTree' => [ + 'Category 1.1' => [ + 'position' => '1', + 'Category 1.1.1' => ['position' => '1-1'], + ], + 'Category 1' => ['position' => '2'], + ], + ], + 'move_tree_to_other_tree' => [ + 'moveCategory' => [ + 'name' => 'Category 2.2', + 'parent_name' => 'Category 1.1', + 'after_category_name' => 'Category 1.1.1', + ], + 'expectedMenuTree' => [ + 'Category 1' => [ + 'position' => '1', + 'Category 1.1' => [ + 'position' => '1-1', + 'Category 1.1.1' => ['position' => '1-1-1'], + 'Category 2.2' => [ + 'position' => '1-1-2', + 'Category 2.2.1' => ['position' => '1-1-2-1'], + ], + ], + ], + 'Category 2' => [ + 'position' => '2', + 'Category 2.1' => ['position' => '2-1'], + ], + ], + ], + 'position_of_categories_in_default' => [ + 'moveCategory' => [ + 'name' => 'Category 12', + 'parent_name' => 'Default Category', + 'after_category_name' => 'Movable', + ], + 'expectedMenuTree' => [ + 'Movable' => ['position' => '3'], + 'Category 12' => ['position' => '4'], + 'Movable Position 1' => ['position' => '5'], + 'Movable Position 2' => ['position' => '6'], + 'Movable Position 3' => ['position' => '7'], + ], + ], + 'position_of_categories_in_tree' => [ + 'moveCategory' => [ + 'name' => 'Movable', + 'parent_name' => 'Category 2', + 'after_category_name' => 'Category 2.1', + ], + 'expectedMenuTree' => [ + 'Category 2' => [ + 'position' => '2', + 'Category 2.1' => ['position' => '2-1'], + 'Movable' => ['position' => '2-2'], + 'Category 2.2' => [ + 'position' => '2-3', + 'Category 2.2.1' => ['position' => '2-3-1'], + ], + ], + 'Movable Position 1' => ['position' => '3'], + ], + ], + ]; + } + + /** + * Test the display of category in menu on different websites + * + * @dataProvider multipleWebsitesCategoryDisplayProvider + * @magentoDataFixture Magento/Catalog/_files/category.php + * @magentoDataFixture Magento/Catalog/_files/category_in_second_root_category.php + * @param string $storeCode + * @param string $expectedCategory + * @param string $notExpectedCategory + * @return void + */ + public function testMultipleWebsitesCategoryDisplay( + string $storeCode, + string $expectedCategory, + string $notExpectedCategory + ): void { + $this->storeManager->setCurrentStore($storeCode); + $output = $this->block->getHtml('level-top', 'submenu', 0); + $this->assertContains( + $expectedCategory, + $output, + 'Category "' . $expectedCategory . '" should appear in the menu!' + ); + $this->assertNotContains( + $notExpectedCategory, + $output, + 'Category "' . $notExpectedCategory . '" should not appear in the menu!' + ); + } + + /** + * Provide test data to verify the display of category in menu on different websites. + * + * @return array + */ + public function multipleWebsitesCategoryDisplayProvider(): array + { + return [ + 'first_website' => [ + 'storeCode' => 'default', + 'expectedCategory' => '>Category 1<', + 'notExpectedCategory' => '>Root2 Category 1<', + ], + 'second_website' => [ + 'storeCode' => 'test_store_1', + 'expectedCategory' => '>Root2 Category 1<', + 'notExpectedCategory' => '>Category 1<', + ], + ]; + } + + /** + * Update existing categories or create new ones + * + * @param array $categories + * @return void + */ + private function updateCategories(array $categories): void + { + foreach ($categories as $categoryData) { + if (!$categoryData['is_new_category']) { + $category = $this->categoryRepository->get($this->getCategoryIdByName($categoryData['category_name'])); + unset($categoryData['category_name']); + } else { + $categoryData[Category::KEY_PARENT_ID] = $this->getCategoryIdByName($categoryData['parent_name']); + unset($categoryData['parent_name']); + $category = $this->categoryFactory->create(); + } + unset($categoryData['is_new_category']); + $category->addData($categoryData); + $this->categoryRepository->save($category); + } + } + + /** + * Get an array from the menu tree with category identifiers and their position + * + * @param Node $node + * @return array + */ + private function getMenuTree(Node $node): array + { + $nodes = []; + if (!is_null($node->getId())) { + $nodes['position'] = str_replace('nav-', '', $node->getData('position_class')); + } + $childrenNodes = $node->getChildren()->getNodes(); + /** @var Node $childNode */ + foreach ($childrenNodes as $childNode) { + $name = $childNode->getName(); + $nodes[$name] = $this->getMenuTree($childNode); + } + + return $nodes; + } + + /** + * @param string $name + * @return string|null + */ + private function getCategoryIdByName(string $name): ?string + { + $categoryCollectionFactory = $this->objectManager->get(CollectionFactory::class); + /** @var Collection $categoryCollection */ + $categoryCollection = $categoryCollectionFactory->create(); + /** @var $category Category */ + $category = $categoryCollection + ->addAttributeToFilter(CategoryInterface::KEY_NAME, $name) + ->setPageSize(1) + ->getFirstItem(); + + return $category->getId(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/ProductInCategoriesViewTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/ProductInCategoriesViewTest.php new file mode 100644 index 0000000000000..48f6e455a5b9f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/ProductInCategoriesViewTest.php @@ -0,0 +1,310 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Product\ListProduct; + +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Block\Product\ListProduct; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Eav\Model\Entity\Collection\AbstractCollection; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Checks products displaying on category page + * + * @magentoDbIsolation disabled + * @magentoAppIsolation enabled + */ +class ProductInCategoriesViewTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var ListProduct */ + private $block; + + /** @var CategoryRepositoryInterface */ + private $categoryRepository; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var StoreManagerInterface */ + private $storeManager; + + /** @var LayoutInterface */ + private $layout; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->categoryRepository = $this->objectManager->create(CategoryRepositoryInterface::class); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->block = $this->layout->createBlock(ListProduct::class); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/category_with_two_products.php + * @dataProvider productDataProvider + * @param array $data + * @return void + */ + public function testCategoryProductView(array $data): void + { + $this->updateProduct($data['sku'], $data); + $collection = $this->getCategoryProductCollection(333); + + $this->assertEquals(1, $collection->getSize()); + $this->assertEquals('simple333', $collection->getFirstItem()->getSku()); + } + + /** + * @return array + */ + public function productDataProvider(): array + { + return [ + 'simple_product_enabled_disabled' => [ + [ + 'sku' => 'simple2', + 'status' => 0, + ], + ], + 'simple_product_in_stock_out_of_stock' => [ + [ + 'sku' => 'simple2', + 'stock_data' => [ + 'use_config_manage_stock' => 1, + 'qty' => 0, + 'is_qty_decimal' => 0, + 'is_in_stock' => 0, + ], + ], + ], + ]; + } + + /** + * @magentoConfigFixture default_store cataloginventory/options/show_out_of_stock 1 + * @magentoDataFixture Magento/Catalog/_files/out_of_stock_product_with_category.php + * @return void + */ + public function testCategoryOutOfStockProductView(): void + { + $collection = $this->getCategoryProductCollection(333); + + $this->assertEquals(1, $collection->getSize()); + $this->assertEquals('out-of-stock-product', $collection->getFirstItem()->getSku()); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/category_product.php + * @dataProvider productVisibilityProvider + * @param array $data + * @return void + */ + public function testCategoryProductVisibility(array $data): void + { + $this->updateProduct($data['data']['sku'], $data['data']); + $collection = $this->getCategoryProductCollection(333); + + $this->assertEquals($data['expected_count'], $collection->getSize()); + } + + /** + * @return array + */ + public function productVisibilityProvider(): array + { + return [ + 'not_visible' => [ + [ + 'data' => [ + 'sku' => 'simple333', + 'visibility' => Visibility::VISIBILITY_NOT_VISIBLE, + ], + 'expected_count' => 0, + ], + + ], + 'catalog_search' => [ + [ + 'data' => [ + 'sku' => 'simple333', + 'visibility' => Visibility::VISIBILITY_BOTH, + ], + 'expected_count' => 1, + ], + + ], + 'search' => [ + [ + 'data' => [ + 'sku' => 'simple333', + 'visibility' => Visibility::VISIBILITY_IN_SEARCH, + ], + 'expected_count' => 0, + ], + ], + 'catalog' => [ + [ + 'data' => [ + 'sku' => 'simple333', + 'visibility' => Visibility::VISIBILITY_IN_CATALOG, + ], + 'expected_count' => 1, + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/Catalog/_files/category_tree.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @return void + */ + public function testAnchorCategoryProductVisibility(): void + { + $this->updateCategoryIsAnchor(400, true); + $this->assignProductCategories('simple2', [402]); + $parentCategoryCollection = $this->getCategoryProductCollection(400); + $childCategoryCollection = $this->getCategoryProductCollection(402, true); + + $this->assertEquals(1, $parentCategoryCollection->getSize()); + $this->assertEquals( + $childCategoryCollection->getAllIds(), + $parentCategoryCollection->getAllIds() + ); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/category_tree.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @return void + */ + public function testNonAnchorCategoryProductVisibility(): void + { + $this->updateCategoryIsAnchor(400, false); + $this->assignProductCategories('simple2', [402]); + $parentCategoryCollectionSize = $this->getCategoryProductCollection(400)->getSize(); + $childCategoryCollectionSize = $this->getCategoryProductCollection(402, true)->getSize(); + + $this->assertEquals(0, $parentCategoryCollectionSize); + $this->assertEquals(1, $childCategoryCollectionSize); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/products_with_websites_and_stores.php + * @magentoDataFixture Magento/Catalog/_files/category.php + * @return void + */ + public function testCategoryProductViewOnMultiWebsite(): void + { + $this->assignProductCategories(['simple-1', 'simple-2'], [3, 333]); + $store = $this->storeManager->getStore('fixture_second_store'); + $currentStore = $this->storeManager->getStore(); + + try { + $this->storeManager->setCurrentStore($store->getId()); + $collection = $this->block->getLoadedProductCollection(); + $collectionSize = $collection->getSize(); + } finally { + $this->storeManager->setCurrentStore($currentStore); + } + + $this->assertEquals(1, $collectionSize); + $this->assertNull($collection->getItemByColumnValue('sku', 'simple-1')); + $this->assertNotNull($collection->getItemByColumnValue('sku', 'simple-2')); + } + + /** + * Set categories to the products + * + * @param string|array $sku + * @param $categoryIds + * @return void + */ + private function assignProductCategories($sku, array $categoryIds): void + { + $skus = !is_array($sku) ? [$sku] : $sku; + foreach ($skus as $sku) { + $product = $this->productRepository->get($sku); + $product->setCategoryIds($categoryIds); + $this->productRepository->save($product); + } + } + + /** + * Update product + * + * @param string $sku + * @param array $data + * @return void + */ + private function updateProduct(string $sku, array $data): void + { + $product = $this->productRepository->get($sku); + $product->addData($data); + $this->productRepository->save($product); + } + + /** + * Returns category collection by category id + * + * @param int $categoryId + * @param bool $refreshBlock + * @return AbstractCollection + */ + private function getCategoryProductCollection(int $categoryId, bool $refreshBlock = false): AbstractCollection + { + $block = $this->getListingBlock($refreshBlock); + $block->getLayer()->setCurrentCategory($categoryId); + + return $block->getLoadedProductCollection(); + } + + /** + * Update is_anchor attribute of the category + * + * @param int $categoryId + * @param bool $isAnchor + * @return void + */ + private function updateCategoryIsAnchor(int $categoryId, bool $isAnchor): void + { + $category = $this->categoryRepository->get($categoryId); + $category->setIsAnchor($isAnchor); + $this->categoryRepository->save($category); + } + + /** + * Get product listing block + * + * @param bool $refresh + * @return ListProduct + */ + private function getListingBlock(bool $refresh): ListProduct + { + if ($refresh) { + $this->block = $this->layout->createBlock(ListProduct::class); + } + + return $this->block; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/SortingTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/SortingTest.php new file mode 100644 index 0000000000000..d3c7972453a4b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ListProduct/SortingTest.php @@ -0,0 +1,321 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Product\ListProduct; + +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\Data\CategoryInterface; +use Magento\Catalog\Block\Product\ListProduct; +use Magento\Catalog\Block\Product\ProductList\Toolbar; +use Magento\Catalog\Model\Config; +use Magento\Catalog\Model\ResourceModel\Category\Collection; +use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; +use Magento\Framework\App\Config\MutableScopeConfigInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Tests for products sorting on category page. + * + * @magentoDbIsolation disabled + * @magentoAppIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class SortingTest extends TestCase +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var ListProduct + */ + private $block; + + /** + * @var CollectionFactory + */ + private $categoryCollectionFactory; + + /** + * @var CategoryRepositoryInterface + */ + private $categoryRepository; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var LayoutInterface + */ + private $layout; + + /** + * @var MutableScopeConfigInterface + */ + private $scopeConfig; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->layout->createBlock(Toolbar::class, 'product_list_toolbar'); + $this->block = $this->layout->createBlock(ListProduct::class)->setToolbarBlockName('product_list_toolbar'); + $this->categoryCollectionFactory = $this->objectManager->get(CollectionFactory::class); + $this->categoryRepository = $this->objectManager->get(CategoryRepositoryInterface::class); + $this->scopeConfig = $this->objectManager->get(MutableScopeConfigInterface::class); + parent::setUp(); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/products_with_not_empty_layered_navigation_attribute.php + * @dataProvider productListSortOrderDataProvider + * @param string $sortBy + * @param string $direction + * @param array $expectation + * @return void + */ + public function testProductListSortOrder(string $sortBy, string $direction, array $expectation): void + { + $category = $this->updateCategorySortBy('Category 1', Store::DEFAULT_STORE_ID, $sortBy); + $this->renderBlock($category, $direction); + $this->assertBlockSorting($sortBy, $expectation); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/products_with_not_empty_layered_navigation_attribute.php + * @dataProvider productListSortOrderDataProvider + * @param string $sortBy + * @param string $direction + * @param array $expectation + * @return void + */ + public function testProductListSortOrderWithConfig(string $sortBy, string $direction, array $expectation): void + { + $this->objectManager->removeSharedInstance(Config::class); + $this->scopeConfig->setValue( + Config::XML_PATH_LIST_DEFAULT_SORT_BY, + $sortBy, + ScopeInterface::SCOPE_STORE, + Store::DEFAULT_STORE_ID + ); + $category = $this->updateCategorySortBy('Category 1', Store::DEFAULT_STORE_ID, null); + $this->renderBlock($category, $direction); + $this->assertBlockSorting($sortBy, $expectation); + } + + /** + * @return array + */ + public function productListSortOrderDataProvider(): array + { + return [ + 'default_order_price_asc' => [ + 'sort' => 'price', + 'direction' => 'asc', + 'expectation' => ['simple1', 'simple2', 'simple3'], + ], + 'default_order_price_desc' => [ + 'sort' => 'price', + 'direction' => 'desc', + 'expectation' => ['simple3', 'simple2', 'simple1'], + ], + 'default_order_position_asc' => [ + 'sort' => 'position', + 'direction' => 'asc', + 'expectation' => ['simple1', 'simple2', 'simple3'], + ], + 'default_order_position_desc' => [ + 'sort' => 'position', + 'direction' => 'desc', + 'expectation' => ['simple3', 'simple2', 'simple1'], + ], + 'default_order_name_asc' => [ + 'sort' => 'name', + 'direction' => 'asc', + 'expectation' => ['simple1', 'simple2', 'simple3'], + ], + 'default_order_name_desc' => [ + 'sort' => 'name', + 'direction' => 'desc', + 'expectation' => ['simple3', 'simple2', 'simple1'], + ], + 'default_order_custom_attribute_asc' => [ + 'sort' => 'test_configurable', + 'direction' => 'asc', + 'expectation' => ['simple1', 'simple3', 'simple2'], + ], + 'default_order_custom_attribute_desc' => [ + 'sort' => 'test_configurable', + 'direction' => 'desc', + 'expectation' => ['simple3', 'simple2', 'simple1'], + ], + ]; + } + + /** + * @magentoDataFixture Magento/Store/_files/second_store.php + * @magentoDataFixture Magento/Catalog/_files/products_with_not_empty_layered_navigation_attribute.php + * @dataProvider productListSortOrderDataProviderOnStoreView + * @param string $sortBy + * @param string $direction + * @param array $expectation + * @param string $defaultSortBy + * @return void + */ + public function testProductListSortOrderOnStoreView( + string $sortBy, + string $direction, + array $expectation, + string $defaultSortBy + ): void { + $secondStoreId = (int)$this->storeManager->getStore('fixture_second_store')->getId(); + $this->updateCategorySortBy('Category 1', Store::DEFAULT_STORE_ID, $defaultSortBy); + $category = $this->updateCategorySortBy('Category 1', $secondStoreId, $sortBy); + $this->renderBlock($category, $direction); + $this->assertBlockSorting($sortBy, $expectation); + } + + /** + * @magentoDataFixture Magento/Store/_files/second_store.php + * @magentoDataFixture Magento/Catalog/_files/products_with_not_empty_layered_navigation_attribute.php + * @dataProvider productListSortOrderDataProviderOnStoreView + * @param string $sortBy + * @param string $direction + * @param array $expectation + * @param string $defaultSortBy + * @return void + */ + public function testProductListSortOrderWithConfigOnStoreView( + string $sortBy, + string $direction, + array $expectation, + string $defaultSortBy + ): void { + $this->objectManager->removeSharedInstance(Config::class); + $secondStoreId = (int)$this->storeManager->getStore('fixture_second_store')->getId(); + $this->scopeConfig->setValue( + Config::XML_PATH_LIST_DEFAULT_SORT_BY, + $defaultSortBy, + ScopeInterface::SCOPE_STORE, + Store::DEFAULT_STORE_ID + ); + $this->scopeConfig->setValue( + Config::XML_PATH_LIST_DEFAULT_SORT_BY, + $sortBy, + ScopeInterface::SCOPE_STORE, + 'fixture_second_store' + ); + $this->updateCategorySortBy('Category 1', Store::DEFAULT_STORE_ID, null); + $category = $this->updateCategorySortBy('Category 1', $secondStoreId, null); + $this->renderBlock($category, $direction); + $this->assertBlockSorting($sortBy, $expectation); + } + + /** + * @return array + */ + public function productListSortOrderDataProviderOnStoreView(): array + { + return array_merge_recursive( + $this->productListSortOrderDataProvider(), + [ + 'default_order_price_asc' => ['default_sort' => 'position'], + 'default_order_price_desc' => ['default_sort' => 'position'], + 'default_order_position_asc' => ['default_sort' => 'price'], + 'default_order_position_desc' => ['default_sort' => 'price'], + 'default_order_name_asc' => ['default_sort' => 'price'], + 'default_order_name_desc' => ['default_sort' => 'price'], + 'default_order_custom_attribute_asc' => ['default_sort' => 'price'], + 'default_order_custom_attribute_desc' => ['default_sort' => 'price'], + ] + ); + } + + /** + * Renders block to apply sorting. + * + * @param CategoryInterface $category + * @param string $direction + * @return void + */ + private function renderBlock(CategoryInterface $category, string $direction): void + { + $this->block->getLayer()->setCurrentCategory($category); + $this->block->setDefaultDirection($direction); + $this->block->toHtml(); + } + + /** + * Checks product list block correct sorting. + * + * @param string $sortBy + * @param array $expectation + * @return void + */ + private function assertBlockSorting(string $sortBy, array $expectation): void + { + $this->assertArrayHasKey($sortBy, $this->block->getAvailableOrders()); + $this->assertEquals($sortBy, $this->block->getSortBy()); + $this->assertEquals($expectation, $this->block->getLoadedProductCollection()->getColumnValues('sku')); + } + + /** + * Loads category by name. + * + * @param string $categoryName + * @param int $storeId + * @return CategoryInterface + */ + private function loadCategory(string $categoryName, int $storeId): CategoryInterface + { + /** @var Collection $categoryCollection */ + $categoryCollection = $this->categoryCollectionFactory->create(); + $categoryId = $categoryCollection->setStoreId($storeId) + ->addAttributeToFilter(CategoryInterface::KEY_NAME, $categoryName) + ->setPageSize(1) + ->getFirstItem() + ->getId(); + + return $this->categoryRepository->get($categoryId, $storeId); + } + + /** + * Updates category default sort by field. + * + * @param string $categoryName + * @param int $storeId + * @param string|null $sortBy + * @return CategoryInterface + */ + private function updateCategorySortBy( + string $categoryName, + int $storeId, + ?string $sortBy + ): CategoryInterface { + $oldStoreId = $this->storeManager->getStore()->getId(); + $this->storeManager->setCurrentStore($storeId); + $category = $this->loadCategory($categoryName, $storeId); + $category->addData(['default_sort_by' => $sortBy]); + $category = $this->categoryRepository->save($category); + $this->storeManager->setCurrentStore($oldStoreId); + + return $category; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/AbstractLinksTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/AbstractLinksTest.php new file mode 100644 index 0000000000000..f8e778498211d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/AbstractLinksTest.php @@ -0,0 +1,323 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Product\ProductList; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\Data\ProductLinkInterface; +use Magento\Catalog\Api\Data\ProductLinkInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Block\Product\AbstractProduct; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductLink\Link; +use Magento\Framework\View\LayoutInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Class AbstractLinks - abstract class when testing blocks of linked products + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +abstract class AbstractLinksTest extends TestCase +{ + /** @var ObjectManager */ + protected $objectManager; + + /** @var ProductRepositoryInterface */ + protected $productRepository; + + /** @var LayoutInterface */ + protected $layout; + + /** @var ProductInterface|Product */ + protected $product; + + /** @var ProductLinkInterfaceFactory */ + protected $productLinkInterfaceFactory; + + /** @var StoreManagerInterface */ + protected $storeManager; + + /** @var array */ + protected $existingProducts = [ + 'wrong-simple' => [ + 'position' => 1, + ], + 'simple-249' => [ + 'position' => 2, + ], + 'simple-156' => [ + 'position' => 3, + ], + ]; + + /** @var AbstractProduct */ + protected $block; + + /** @var string */ + protected $linkType; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->productLinkInterfaceFactory = $this->objectManager->get(ProductLinkInterfaceFactory::class); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + } + + /** + * Provide test data to verify the display of linked products. + * + * @return array + */ + public function displayLinkedProductsProvider(): array + { + return [ + 'product_all_displayed' => [ + 'data' => [ + 'updateProducts' => [], + 'expectedProductLinks' => [ + 'wrong-simple', + 'simple-249', + 'simple-156', + ], + ], + ], + 'product_disabled' => [ + 'data' => [ + 'updateProducts' => [ + 'wrong-simple' => ['status' => Status::STATUS_DISABLED], + ], + 'expectedProductLinks' => [ + 'simple-249', + 'simple-156', + ], + ], + ], + 'product_invisibility' => [ + 'data' => [ + 'updateProducts' => [ + 'simple-249' => ['visibility' => Visibility::VISIBILITY_NOT_VISIBLE], + ], + 'expectedProductLinks' => [ + 'wrong-simple', + 'simple-156', + ], + ], + ], + 'product_invisible_in_catalog' => [ + 'data' => [ + 'updateProducts' => [ + 'simple-249' => ['visibility' => Visibility::VISIBILITY_IN_SEARCH], + ], + 'expectedProductLinks' => [ + 'wrong-simple', + 'simple-156', + ], + ], + ], + 'product_out_of_stock' => [ + 'data' => [ + 'updateProducts' => [ + 'simple-156' => [ + 'stock_data' => [ + 'use_config_manage_stock' => 1, + 'qty' => 0, + 'is_qty_decimal' => 0, + 'is_in_stock' => 0, + ], + ], + ], + 'expectedProductLinks' => [ + 'wrong-simple', + 'simple-249', + ], + ], + ], + ]; + } + + /** + * Provide test data to verify the display of linked products on different websites. + * + * @return array + */ + public function multipleWebsitesLinkedProductsProvider(): array + { + return [ + 'first_website' => [ + 'data' => [ + 'storeCode' => 'default', + 'productLinks' => [ + 'simple-2' => ['position' => 4], + ], + 'expectedProductLinks' => [ + 'wrong-simple', + 'simple-2', + ], + ], + ], + 'second_website' => [ + 'data' => [ + 'storeCode' => 'fixture_second_store', + 'productLinks' => [ + 'simple-2' => ['position' => 4], + ], + 'expectedProductLinks' => [ + 'simple-249', + 'simple-2', + ], + ], + ], + ]; + } + + /** + * Get test data to check position of related, up-sells and cross-sells products + * + * @return array + */ + protected function getPositionData(): array + { + return [ + 'productLinks' => array_replace_recursive( + $this->existingProducts, + [ + 'wrong-simple' => ['position' => 2], + 'simple-249' => ['position' => 3], + 'simple-156' => ['position' => 1], + ] + ), + 'expectedProductLinks' => [ + 'simple-156', + 'wrong-simple', + 'simple-249', + ], + ]; + } + + /** + * Prepare a block of linked products + * + * @return void + */ + protected function prepareBlock(): void + { + $this->block->setLayout($this->layout); + $this->block->setTemplate('Magento_Catalog::product/list/items.phtml'); + $this->block->setType($this->linkType); + } + + /** + * Set linked products by link type for current product received from array + * + * @param ProductInterface $product + * @param array $productData + * @return void + */ + private function setCustomProductLinks(ProductInterface $product, array $productData): void + { + $productLinks = []; + foreach ($productData as $sku => $data) { + /** @var ProductLinkInterface|Link $productLink */ + $productLink = $this->productLinkInterfaceFactory->create(); + $productLink->setSku($product->getSku()); + $productLink->setLinkedProductSku($sku); + if (isset($data['position'])) { + $productLink->setPosition($data['position']); + } + $productLink->setLinkType($this->linkType); + $productLinks[] = $productLink; + } + $product->setProductLinks($productLinks); + } + + /** + * Update product attributes + * + * @param array $products + * @return void + */ + protected function updateProducts(array $products): void + { + foreach ($products as $sku => $data) { + /** @var ProductInterface|Product $product */ + $product = $this->productRepository->get($sku); + $product->addData($data); + $this->productRepository->save($product); + } + } + + /** + * Get an array of received linked products + * + * @param array $items + * @return array + */ + protected function getActualLinks(array $items): array + { + $actualLinks = []; + /** @var ProductInterface $productItem */ + foreach ($items as $productItem) { + $actualLinks[] = $productItem->getSku(); + } + + return $actualLinks; + } + + /** + * Link products to an existing product + * + * @param string $sku + * @param array $productLinks + * @return void + */ + protected function linkProducts(string $sku, array $productLinks): void + { + $product = $this->productRepository->get($sku); + $this->setCustomProductLinks($product, $productLinks); + $this->productRepository->save($product); + } + + /** + * Prepare the necessary websites for all products + * + * @return array + */ + protected function prepareWebsiteIdsProducts(): array + { + $websiteId = $this->storeManager->getWebsite('test')->getId(); + $defaultWebsiteId = $this->storeManager->getWebsite('base')->getId(); + + return [ + 'simple-1' => [ + 'website_ids' => [$defaultWebsiteId, $websiteId], + ], + 'simple-2' => [ + 'website_ids' => [$defaultWebsiteId, $websiteId], + ], + 'wrong-simple' => [ + 'website_ids' => [$defaultWebsiteId], + ], + 'simple-249' => [ + 'website_ids' => [$websiteId], + ], + 'simple-156' => [ + 'website_ids' => [], + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/RelatedTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/RelatedTest.php index 9f0f04508e468..c71a481a79379 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/RelatedTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/RelatedTest.php @@ -3,84 +3,150 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Block\Product\ProductList; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\ResourceModel\Product\Link\Product\Collection as LinkProductCollection; + /** - * Test class for \Magento\Catalog\Block\Product\List\Related. + * Check the correct behavior of related products on the product view page * - * @magentoDataFixture Magento/Catalog/_files/products_related.php + * @see \Magento\Catalog\Block\Product\ProductList\Related * @magentoDbIsolation disabled + * @magentoAppArea frontend */ -class RelatedTest extends \PHPUnit\Framework\TestCase +class RelatedTest extends AbstractLinksTest { - /** - * @var \Magento\Catalog\Block\Product\ProductList\Related - */ + /** @var Related */ protected $block; /** - * @var \Magento\Catalog\Api\Data\ProductInterface - */ - protected $product; - - /** - * @var \Magento\Catalog\Api\Data\ProductInterface + * @inheritdoc */ - protected $relatedProduct; - protected function setUp() { - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - \Magento\TestFramework\Helper\Bootstrap::getInstance()->loadArea(\Magento\Framework\App\Area::AREA_FRONTEND); - - /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ - $productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); - - $this->relatedProduct = $productRepository->get('simple'); - $this->product = $productRepository->get('simple_with_cross'); - $objectManager->get(\Magento\Framework\Registry::class)->register('product', $this->product); + parent::setUp(); - $this->block = $objectManager->get(\Magento\Framework\View\LayoutInterface::class) - ->createBlock(\Magento\Catalog\Block\Product\ProductList\Related::class); - - $this->block->setLayout($objectManager->get(\Magento\Framework\View\LayoutInterface::class)); - $this->block->setTemplate('Magento_Catalog::product/list/items.phtml'); - $this->block->setType('related'); - $this->block->addChild('addto', \Magento\Catalog\Block\Product\ProductList\Item\Container::class); - $this->block->getChildBlock( - 'addto' - )->addChild( - 'compare', - \Magento\Catalog\Block\Product\ProductList\Item\AddTo\Compare::class, - ['template' => 'Magento_Catalog::product/list/addto/compare.phtml'] - ); + $this->block = $this->layout->createBlock(Related::class); + $this->linkType = 'related'; } /** - * @magentoAppIsolation enabled + * Checks for a related product when block code is generated + * + * @magentoDataFixture Magento/Catalog/_files/products_related.php + * @return void */ - public function testAll() + public function testAll(): void { + /** @var ProductInterface $relatedProduct */ + $relatedProduct = $this->productRepository->get('simple'); + $this->product = $this->productRepository->get('simple_with_cross'); + $this->block->setProduct($this->product); + $this->prepareBlock(); $html = $this->block->toHtml(); $this->assertNotEmpty($html); - $this->assertContains('Simple Related Product', $html); + $this->assertContains($relatedProduct->getName(), $html); /* name */ - $this->assertContains('"product":"' . $this->relatedProduct->getId() . '"', $html); + $this->assertContains('id="related-checkbox' . $relatedProduct->getId() . '"', $html); /* part of url */ $this->assertInstanceOf( - \Magento\Catalog\Model\ResourceModel\Product\Link\Product\Collection::class, + LinkProductCollection::class, $this->block->getItems() ); } /** - * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/products_related.php + * @return void */ - public function testGetIdentities() + public function testGetIdentities(): void { - $expectedTags = ['cat_p_' . $this->relatedProduct->getId(), 'cat_p']; + /** @var ProductInterface $relatedProduct */ + $relatedProduct = $this->productRepository->get('simple'); + $this->product = $this->productRepository->get('simple_with_cross'); + $this->block->setProduct($this->product); + $this->prepareBlock(); + $expectedTags = ['cat_p_' . $relatedProduct->getId(), 'cat_p']; $tags = $this->block->getIdentities(); $this->assertEquals($expectedTags, $tags); } + + /** + * Test the display of related products in the block + * + * @dataProvider displayLinkedProductsProvider + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Catalog/_files/products_list.php + * @param array $data + * @return void + */ + public function testDisplayRelatedProducts(array $data): void + { + $this->updateProducts($data['updateProducts']); + $this->linkProducts('simple', $this->existingProducts); + $this->product = $this->productRepository->get('simple'); + $this->block->setProduct($this->product); + $items = $this->block->getItems()->getItems(); + + $this->assertEquals( + $data['expectedProductLinks'], + $this->getActualLinks($items), + 'Expected related products do not match actual related products!' + ); + } + + /** + * Test the position of related products in the block + * + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Catalog/_files/products_list.php + * @return void + */ + public function testPositionRelatedProducts(): void + { + $data = $this->getPositionData(); + $this->linkProducts('simple', $data['productLinks']); + $this->product = $this->productRepository->get('simple'); + $this->block->setProduct($this->product); + $items = $this->block->getItems()->getItems(); + + $this->assertEquals( + $data['expectedProductLinks'], + $this->getActualLinks($items), + 'Expected related products do not match actual related products!' + ); + } + + /** + * Test the display of related products in the block on different websites + * + * @dataProvider multipleWebsitesLinkedProductsProvider + * @magentoDataFixture Magento/Catalog/_files/products_with_websites_and_stores.php + * @magentoDataFixture Magento/Catalog/_files/products_list.php + * @magentoAppIsolation enabled + * @param array $data + * @return void + */ + public function testMultipleWebsitesRelatedProducts(array $data): void + { + $this->updateProducts($this->prepareWebsiteIdsProducts()); + $productLinks = array_replace_recursive($this->existingProducts, $data['productLinks']); + $this->linkProducts('simple-1', $productLinks); + $this->product = $this->productRepository->get( + 'simple-1', + false, + $this->storeManager->getStore($data['storeCode'])->getId() + ); + $this->block->setProduct($this->product); + $items = $this->block->getItems()->getItems(); + + $this->assertEquals( + $data['expectedProductLinks'], + $this->getActualLinks($items), + 'Expected related products do not match actual related products!' + ); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/UpsellTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/UpsellTest.php index 57ccb48b8df69..f989393d5da63 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/UpsellTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ProductList/UpsellTest.php @@ -3,65 +3,44 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Block\Product\ProductList; /** - * Test class for \Magento\Catalog\Block\Product\List\Upsell. + * Check the correct behavior of up-sell products on the product view page * - * @magentoDataFixture Magento/Catalog/_files/products_upsell.php + * @see \Magento\Catalog\Block\Product\ProductList\Upsell * @magentoDbIsolation disabled + * @magentoAppArea frontend */ -class UpsellTest extends \PHPUnit\Framework\TestCase +class UpsellTest extends AbstractLinksTest { - /** - * @var \Magento\Catalog\Block\Product\ProductList\Upsell - */ + /** @var Upsell */ protected $block; /** - * @var \Magento\Catalog\Api\Data\ProductInterface + * @inheritdoc */ - protected $product; - - /** - * @var \Magento\Catalog\Api\Data\ProductInterface - */ - protected $upsellProduct; - protected function setUp() { - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - \Magento\TestFramework\Helper\Bootstrap::getInstance()->loadArea(\Magento\Framework\App\Area::AREA_FRONTEND); - - /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ - $productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); - - $this->upsellProduct = $productRepository->get('simple'); - $this->product = $productRepository->get('simple_with_upsell'); - $objectManager->get(\Magento\Framework\Registry::class)->register('product', $this->product); + parent::setUp(); - $this->block = $objectManager->get(\Magento\Framework\View\LayoutInterface::class) - ->createBlock(\Magento\Catalog\Block\Product\ProductList\Upsell::class); - - $this->block->setLayout($objectManager->get(\Magento\Framework\View\LayoutInterface::class)); - $this->block->setTemplate('Magento_Catalog::product/list/items.phtml'); - $this->block->setType('upsell'); - $this->block->addChild('addto', \Magento\Catalog\Block\Product\ProductList\Item\Container::class); - $this->block->getChildBlock( - 'addto' - )->addChild( - 'compare', - \Magento\Catalog\Block\Product\ProductList\Item\AddTo\Compare::class, - ['template' => 'Magento_Catalog::product/list/addto/compare.phtml'] - ); + $this->block = $this->layout->createBlock(Upsell::class); + $this->linkType = 'upsell'; } /** - * @magentoAppIsolation enabled + * Checks for a up-sell product when block code is generated + * + * @magentoDataFixture Magento/Catalog/_files/products_upsell.php + * @return void */ - public function testAll() + public function testAll(): void { + $this->product = $this->productRepository->get('simple_with_upsell'); + $this->block->setProduct($this->product); + $this->prepareBlock(); $html = $this->block->toHtml(); $this->assertNotEmpty($html); $this->assertContains('Simple Up Sell', $html); @@ -69,12 +48,93 @@ public function testAll() } /** - * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/products_upsell.php + * @return void */ - public function testGetIdentities() + public function testGetIdentities(): void { - $expectedTags = ['cat_p_' . $this->upsellProduct->getId(), 'cat_p']; + $upsellProduct = $this->productRepository->get('simple'); + $this->product = $this->productRepository->get('simple_with_upsell'); + $this->block->setProduct($this->product); + $this->prepareBlock(); + $expectedTags = ['cat_p_' . $upsellProduct->getId(), 'cat_p']; $tags = $this->block->getIdentities(); $this->assertEquals($expectedTags, $tags); } + + /** + * Test the display of up-sell products in the block + * + * @dataProvider displayLinkedProductsProvider + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Catalog/_files/products_list.php + * @param array $data + * @return void + */ + public function testDisplayUpsellProducts(array $data): void + { + $this->updateProducts($data['updateProducts']); + $this->linkProducts('simple', $this->existingProducts); + $this->product = $this->productRepository->get('simple'); + $this->block->setProduct($this->product); + $items = $this->block->getItems(); + + $this->assertEquals( + $data['expectedProductLinks'], + $this->getActualLinks($items), + 'Expected up-sell products do not match actual up-sell products!' + ); + } + + /** + * Test the position of up-sell products in the block + * + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Catalog/_files/products_list.php + * @return void + */ + public function testPositionUpsellProducts(): void + { + $data = $this->getPositionData(); + $this->linkProducts('simple', $data['productLinks']); + $this->product = $this->productRepository->get('simple'); + $this->block->setProduct($this->product); + $items = $this->block->getItems(); + + $this->assertEquals( + $data['expectedProductLinks'], + $this->getActualLinks($items), + 'Expected up-sell products do not match actual up-sell products!' + ); + } + + /** + * Test the display of up-sell products in the block on different websites + * + * @dataProvider multipleWebsitesLinkedProductsProvider + * @magentoDataFixture Magento/Catalog/_files/products_with_websites_and_stores.php + * @magentoDataFixture Magento/Catalog/_files/products_list.php + * @magentoAppIsolation enabled + * @param array $data + * @return void + */ + public function testMultipleWebsitesUpsellProducts(array $data): void + { + $this->updateProducts($this->prepareWebsiteIdsProducts()); + $productLinks = array_replace_recursive($this->existingProducts, $data['productLinks']); + $this->linkProducts('simple-1', $productLinks); + $this->product = $this->productRepository->get( + 'simple-1', + false, + $this->storeManager->getStore($data['storeCode'])->getId() + ); + $this->block->setProduct($this->product); + $items = $this->block->getItems(); + + $this->assertEquals( + $data['expectedProductLinks'], + $this->getActualLinks($items), + 'Expected up-sell products do not match actual up-sell products!' + ); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/AbstractAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/AbstractAttributeTest.php new file mode 100644 index 0000000000000..80e2ac52cecd6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/AbstractAttributeTest.php @@ -0,0 +1,270 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Product\View\Attribute; + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Block\Product\View\Attributes; +use Magento\Catalog\Helper\Output; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\View\LayoutInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Class consist of base logic for custom attributes view tests + */ +abstract class AbstractAttributeTest extends TestCase +{ + /** @var ObjectManagerInterface */ + protected $objectManager; + + /** @var LayoutInterface */ + private $layout; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var ProductAttributeRepositoryInterface */ + private $attributeRepository; + + /** @var Registry */ + private $registry; + + /** @var Attributes */ + private $block; + + /** @var ProductAttributeInterface */ + private $attribute; + + /** @var Output */ + private $outputHelper; + + /** @var StoreManagerInterface */ + private $storeManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->attributeRepository = $this->objectManager->create(ProductAttributeRepositoryInterface::class); + $this->registry = $this->objectManager->get(Registry::class); + $this->block = $this->layout->createBlock(Attributes::class); + $this->outputHelper = $this->objectManager->create(Output::class); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + } + + /** + * Process custom attribute view + * + * @param string $sku + * @param string $attributeValue + * @param string $expectedAttributeValue + * @return void + */ + protected function processAttributeView( + string $sku, + string $attributeValue, + string $expectedAttributeValue + ): void { + $this->updateAttribute(['is_visible_on_front' => true]); + $product = $this->updateProduct($sku, $attributeValue); + $this->registerProduct($product); + $data = $this->block->getAdditionalData(); + $this->assertEquals($this->prepareExpectedData($expectedAttributeValue), $data); + } + + /** + * Process custom attribute with default value view when new value set + * + * @param string $sku + * @param string $attributeValue + * @param string $expectedAttributeValue + * @return void + */ + protected function processNonDefaultAttributeValueView( + string $sku, + string $attributeValue, + string $expectedAttributeValue + ): void { + $this->updateAttribute(['is_visible_on_front' => true, 'default_value' => $this->getDefaultAttributeValue()]); + $product = $this->updateProduct($sku, $attributeValue); + $this->registerProduct($product); + $data = $this->block->getAdditionalData(); + $this->assertEquals($this->prepareExpectedData($expectedAttributeValue), $data); + } + + /** + * Procces custom attribute view with default value + * + * @param string $sku + * @param string $expectedAttributeValue + * @return void + */ + protected function processDefaultValueAttributeView(string $sku, string $expectedAttributeValue): void + { + $this->updateAttribute(['is_visible_on_front' => true, 'default_value' => $this->getDefaultAttributeValue()]); + $product = $this->productRepository->save($this->productRepository->get($sku)); + $this->registerProduct($product); + $data = $this->block->getAdditionalData(); + $this->assertEquals($this->prepareExpectedData($expectedAttributeValue), $data); + } + + /** + * Procces attribute value view with html tags + * + * @param string $sku + * @param bool $allowHtmlTags + * @param string $attributeValue + * @param string $expectedAttributeValue + * @return void + */ + protected function processAttributeHtmlOutput( + string $sku, + bool $allowHtmlTags, + string $attributeValue, + string $expectedAttributeValue + ): void { + $this->updateAttribute(['is_visible_on_front' => true, 'is_html_allowed_on_front' => $allowHtmlTags]); + $product = $this->updateProduct($sku, $attributeValue); + $this->registerProduct($product); + $data = $this->block->getAdditionalData(); + $dataItem = $data[$this->getAttributeCode()] ?? null; + $this->assertNotNull($dataItem); + $output = $this->outputHelper->productAttribute($product, $dataItem['value'], $dataItem['code']); + $this->assertEquals($expectedAttributeValue, $output); + } + + /** + * Process attribute view per store views + * + * @param string $sku + * @param int $attributeScopeValue + * @param string $attributeValue + * @param string $expectedAttributeValue + * @param string $storeCode + * @return void + */ + protected function processMultiStoreView( + string $sku, + int $attributeScopeValue, + string $attributeValue, + string $storeCode + ): void { + $currentStore = $this->storeManager->getStore(); + $this->updateAttribute(['is_global' => $attributeScopeValue, 'is_visible_on_front' => true]); + $this->storeManager->setCurrentStore($storeCode); + + try { + $product = $this->updateProduct($sku, $attributeValue); + $this->registerProduct($product); + $this->assertEquals($this->prepareExpectedData($attributeValue), $this->block->getAdditionalData()); + } finally { + $this->storeManager->setCurrentStore($currentStore); + } + } + + /** + * Get attribute + * + * @return ProductAttributeInterface + */ + protected function getAttribute(): ProductAttributeInterface + { + if ($this->attribute === null) { + $this->attribute = $this->attributeRepository->get($this->getAttributeCode()); + } + + return $this->attribute; + } + + /** + * Prepare expected data + * + * @param string $expectedValue + * @return array + */ + private function prepareExpectedData(string $expectedValue): array + { + return [ + $this->getAttributeCode() => [ + 'label' => $this->getAttribute()->getStoreLabel(), + 'value' => $expectedValue, + 'code' => $this->getAttributeCode(), + ], + ]; + } + + /** + * Update product + * + * @param string $productSku + * @param string $attributeValue + * @return ProductInterface + */ + private function updateProduct(string $productSku, string $attributeValue): ProductInterface + { + $value = $this->getAttribute()->usesSource() + ? $this->attribute->getSource()->getOptionId($attributeValue) + : $attributeValue; + $product = $this->productRepository->get($productSku); + $product->addData([$this->getAttributeCode() => $value]); + + return $this->productRepository->save($product); + } + + /** + * Register product + * + * @param ProductInterface $product + * @return void + */ + private function registerProduct(ProductInterface $product): void + { + $this->registry->unregister('product'); + $this->registry->register('product', $product); + } + + /** + * Update attribute + * + * @param array $data + * @return void + */ + private function updateAttribute(array $data): void + { + $attribute = $this->getAttribute(); + $attribute->addData($data); + + $this->attributeRepository->save($attribute); + } + + /** + * Get attribute code for current test + * + * @return string + */ + abstract protected function getAttributeCode(): string; + + /** + * Get default value for current attribute + * + * @return string + */ + abstract protected function getDefaultAttributeValue(): string; +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/DateAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/DateAttributeTest.php new file mode 100644 index 0000000000000..ec63fdc4806cf --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/DateAttributeTest.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Product\View\Attribute; + +/** + * Class checks date attribute displaying on frontend + * + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/Catalog/_files/product_date_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ +class DateAttributeTest extends AbstractAttributeTest +{ + /** + * @magentoConfigFixture default_store general/locale/timezone UTC + * @return void + */ + public function testAttributeView(): void + { + $attributeValue = $this->getAttribute()->getBackend()->formatDate('12/20/19'); + $this->processAttributeView('simple2', $attributeValue, 'Dec 20, 2019'); + } + + /** + * @magentoConfigFixture default_store general/locale/timezone UTC + * @return void + */ + public function testAttributeWithNonDefaultValueView(): void + { + $attributeValue = $this->getAttribute()->getBackend()->formatDate('12/20/19'); + $this->processNonDefaultAttributeValueView('simple2', $attributeValue, 'Dec 20, 2019'); + } + + /** + * @magentoConfigFixture default_store general/locale/timezone UTC + * @return void + */ + public function testAttributeWithDefaultValueView(): void + { + $this->markTestSkipped('Test is blocked by issue MC-28950'); + $this->processDefaultValueAttributeView('simple2', $this->getDefaultAttributeValue()); + } + + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'date_attribute'; + } + + /** + * @inheritdoc + */ + protected function getDefaultAttributeValue(): string + { + return $this->getAttribute()->getBackend()->formatDate('11/20/19'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/DropdownAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/DropdownAttributeTest.php new file mode 100644 index 0000000000000..f6c7e81b13a23 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/DropdownAttributeTest.php @@ -0,0 +1,134 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Product\View\Attribute; + +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; + +/** + * Class checks dropdown attribute displaying on frontend + * + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ +class DropdownAttributeTest extends AbstractAttributeTest +{ + /** @var string */ + private $attributeCode; + + /** + * @return void + */ + public function testAttributeView(): void + { + $attributeValue = $this->getAttribute()->getSource()->getOptionId('Option 2'); + $this->processAttributeView('simple2', $attributeValue, 'Option 2'); + } + + /** + * @return void + */ + public function testAttributeWithNonDefaultValueView(): void + { + $attributeValue = $this->getAttribute()->getSource()->getOptionId('Option 2'); + $this->processNonDefaultAttributeValueView('simple2', $attributeValue, 'Option 2'); + } + + /** + * @dataProvider attributeWithTagsProvider + * @magentoAppArea frontend + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute_with_html.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @param bool $allowHtmlTags + * @param string $attributeValue + * @param string $expectedAttributeValue + * @return void + */ + public function testAttributeWithHtmlTags( + bool $allowHtmlTags, + string $attributeValue, + string $expectedAttributeValue + ): void { + $this->attributeCode = 'dropdown_attribute_with_html'; + $attributeValue = $this->getAttribute()->getSource()->getOptionId($attributeValue); + $this->processAttributeHtmlOutput('simple2', $allowHtmlTags, $attributeValue, $expectedAttributeValue); + } + + /** + * @return array + */ + public function attributeWithTagsProvider(): array + { + return [ + 'allow_html_tags' => [ + 'allow_html_tags' => true, + 'attribute_value' => '<h2>Option 2</h2>', + 'expected_attribute_value' => '<h2>Option 2</h2>', + ], + 'disallow_html_tags' => [ + 'allow_html_tags' => false, + 'attribute_value' => '<h2>Option 2</h2>', + 'expected_attribute_value' => '<h2>Option 2</h2>', + ], + ]; + } + + /** + * @magentoDbIsolation disabled + * + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testAttributePerStoreView(): void + { + $this->processMultiStoreView( + 'simple2', + ScopedAttributeInterface::SCOPE_STORE, + 'Option 3', + 'fixturestore' + ); + } + + /** + * @magentoDbIsolation disabled + * + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + * @magentoDataFixture Magento/Catalog/_files/product_two_websites.php + * + * @return void + */ + public function testAttributePerWebsites(): void + { + $this->processMultiStoreView( + 'simple-on-two-websites', + ScopedAttributeInterface::SCOPE_WEBSITE, + 'Option 3', + 'fixture_second_store' + ); + } + + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return $this->attributeCode ?? 'dropdown_attribute'; + } + + /** + * @inheritdoc + */ + protected function getDefaultAttributeValue(): string + { + return $this->getAttribute()->getSource()->getOptionId('Option 1'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/MultiSelectAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/MultiSelectAttributeTest.php new file mode 100644 index 0000000000000..758de8f53ba93 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/MultiSelectAttributeTest.php @@ -0,0 +1,103 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Product\View\Attribute; + +/** + * Class checks multi select attribute displaying on frontend + * + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/Catalog/_files/multiselect_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ +class MultiSelectAttributeTest extends AbstractAttributeTest +{ + /** @var string */ + private $attributeCode; + + /** + * @return void + */ + public function testAttributeView(): void + { + $attributeValue = $this->getAttribute()->getSource()->getOptionId('Option 2'); + $this->processAttributeView('simple2', $attributeValue, 'Option 2'); + } + + /** + * @return void + */ + public function testAttributeWithNonDefaultValueView(): void + { + $attributeValue = $this->getAttribute()->getSource()->getOptionId('Option 2'); + $this->processNonDefaultAttributeValueView('simple2', $attributeValue, 'Option 2'); + } + + /** + * @return void + */ + public function testAttributeWithDefaultValueView(): void + { + $this->markTestSkipped('Test is blocked by issue MC-29019'); + $this->processDefaultValueAttributeView('simple2', 'Option 1'); + } + + /** + * @dataProvider attributeWithTagsProvider + * @magentoAppArea frontend + * @magentoDataFixture Magento/Catalog/_files/multiselect_attribute_with_html.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @param bool $allowHtmlTags + * @param string $attributeValue + * @param string $expectedAttributeValue + * @return void + */ + public function testAttributeWithHtmlTags( + bool $allowHtmlTags, + string $attributeValue, + string $expectedAttributeValue + ): void { + $this->attributeCode = 'multiselect_attribute_with_html'; + $attributeValue = $this->getAttribute()->getSource()->getOptionId($attributeValue); + $this->processAttributeHtmlOutput('simple2', $allowHtmlTags, $attributeValue, $expectedAttributeValue); + } + + /** + * @return array + */ + public function attributeWithTagsProvider(): array + { + return [ + 'allow_html_tags' => [ + 'allow_html_tags' => true, + 'attribute_value' => '<h2>Option 2</h2>', + 'expected_attribute_value' => '<h2>Option 2</h2>', + ], + 'disallow_html_tags' => [ + 'allow_html_tags' => false, + 'attribute_value' => '<h2>Option 2</h2>', + 'expected_attribute_value' => '<h2>Option 2</h2>', + ], + ]; + } + + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return $this->attributeCode ?? 'multiselect_attribute'; + } + + /** + * @inheritdoc + */ + protected function getDefaultAttributeValue(): string + { + return $this->getAttribute()->getSource()->getOptionId('Option 1'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/PriceAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/PriceAttributeTest.php new file mode 100644 index 0000000000000..ae3c145294b56 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/PriceAttributeTest.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Product\View\Attribute; + +use Magento\Directory\Model\PriceCurrency; + +/** + * Class checks price attribute displaying on frontend + * + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/product_decimal_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ +class PriceAttributeTest extends AbstractAttributeTest +{ + /** @var PriceCurrency */ + private $priceCurrency; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->priceCurrency = $this->objectManager->create(PriceCurrency::class); + } + + /** + * @dataProvider pricesDataProvider + * @param string $price + * @return void + */ + public function testAttributeView(string $price): void + { + $this->processAttributeView('simple2', $price, $this->priceCurrency->convertAndFormat($price)); + } + + /** + * @return array + */ + public function pricesDataProvider(): array + { + return [ + 'zero_price' => [ + 'price' => '0', + ], + 'positive_price' => [ + 'price' => '150', + ], + ]; + } + + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'decimal_attribute'; + } + + /** + * @inheritdoc + */ + protected function getDefaultAttributeValue(): string + { + return ''; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/TextAreaAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/TextAreaAttributeTest.php new file mode 100644 index 0000000000000..45414c8788922 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/TextAreaAttributeTest.php @@ -0,0 +1,60 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Product\View\Attribute; + +/** + * Class checks textarea attribute displaying on frontend + * + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/product_text_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ +class TextAreaAttributeTest extends AbstractAttributeTest +{ + /** + * @return void + */ + public function testAttributeView(): void + { + $attributeValue = 'Value for text area attribute'; + $this->processAttributeView('simple2', $attributeValue, $attributeValue); + } + + /** + * @return void + */ + public function testAttributeWithNonDefaultValueView(): void + { + $attributeValue = 'Text area attribute value'; + $this->processNonDefaultAttributeValueView('simple2', $attributeValue, $attributeValue); + } + + /** + * @return void + */ + public function testAttributeWithDefaultValueView(): void + { + $this->processDefaultValueAttributeView('simple2', $this->getDefaultAttributeValue()); + } + + /** + * @inheritdic + */ + protected function getAttributeCode(): string + { + return 'text_attribute'; + } + + /** + * @inheritdic + */ + protected function getDefaultAttributeValue(): string + { + return 'Default value for text area attribute'; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/TextAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/TextAttributeTest.php new file mode 100644 index 0000000000000..dae5fad160128 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/TextAttributeTest.php @@ -0,0 +1,134 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Product\View\Attribute; + +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; + +/** + * Class checks text attribute displaying on frontend + * + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/product_varchar_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ +class TextAttributeTest extends AbstractAttributeTest +{ + /** + * @return void + */ + public function testAttributeView(): void + { + $attributeValue = 'Text attribute value'; + $this->processAttributeView('simple2', $attributeValue, $attributeValue); + } + + /** + * @return void + */ + public function testAttributeWithNonDefaultValueView(): void + { + $attributeValue = 'Non default text attribute value'; + $this->processNonDefaultAttributeValueView('simple2', $attributeValue, $attributeValue); + } + + /** + * @return void + */ + public function testAttributeWithDefaultValueView(): void + { + $this->processDefaultValueAttributeView('simple2', $this->getDefaultAttributeValue()); + } + + /** + * @dataProvider attributeWithTagsProvider + * @magentoAppArea frontend + * @param bool $allowHtmlTags + * @param string $attributeValue + * @param string $expectedAttributeValue + * @return void + */ + public function testAttributeWithHtmlTags( + bool $allowHtmlTags, + string $attributeValue, + string $expectedAttributeValue + ): void { + $this->processAttributeHtmlOutput('simple2', $allowHtmlTags, $attributeValue, $expectedAttributeValue); + } + + /** + * @return array + */ + public function attributeWithTagsProvider(): array + { + return [ + 'allow_html_tags' => [ + 'allow_html_tags' => true, + 'attribute_value' => '<h2>Text with <p>html inside</p></h2>', + 'expected_attribute_value' => '<h2>Text with <p>html inside</p></h2>', + ], + 'disallow_html_tags' => [ + 'allow_html_tags' => false, + 'attribute_value' => '<h2>Text with <p>html inside</p></h2>', + 'expected_attribute_value' => '<h2>Text with <p>html inside</p></h2>', + ], + ]; + } + + /** + * @magentoDbIsolation disabled + * + * @magentoDataFixture Magento/Catalog/_files/product_varchar_attribute.php + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testAttributePerStoreView(): void + { + $this->processMultiStoreView( + 'simple2', + ScopedAttributeInterface::SCOPE_STORE, + 'second store view value', + 'fixturestore' + ); + } + + /** + * @magentoDbIsolation disabled + * + * @magentoDataFixture Magento/Catalog/_files/product_two_websites.php + * @magentoDataFixture Magento/Catalog/_files/product_varchar_attribute.php + * + * @return void + */ + public function testAttributePerWebsites(): void + { + $this->processMultiStoreView( + 'simple-on-two-websites', + ScopedAttributeInterface::SCOPE_WEBSITE, + 'second website value', + 'fixture_second_store' + ); + } + + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'varchar_attribute'; + } + + /** + * @inheritdoc + */ + protected function getDefaultAttributeValue(): string + { + return 'Default value for text attribute'; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/YesNoAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/YesNoAttributeTest.php new file mode 100644 index 0000000000000..8d95b8d02e2fc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Attribute/YesNoAttributeTest.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Product\View\Attribute; + +/** + * Class checks boolean attribute displaying on frontend + * + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/product_boolean_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ +class YesNoAttributeTest extends AbstractAttributeTest +{ + /** + * @return void + */ + public function testAttributeWithNonDefaultValueView(): void + { + $this->processNonDefaultAttributeValueView('simple2', '0', 'No'); + } + + /** + * @return void + */ + public function testAttributeWithDefaultValueView(): void + { + $this->processDefaultValueAttributeView('simple2', 'Yes'); + } + + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'boolean_attribute'; + } + + /** + * @inheritdoc + */ + protected function getDefaultAttributeValue(): string + { + return '1'; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php new file mode 100644 index 0000000000000..9bcdb00eebe7c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php @@ -0,0 +1,357 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Product\View; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\View\LayoutInterface; +use Magento\Store\Api\StoreRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + +/** + * Provide tests for displaying images on product page. + * + * @magentoAppArea frontend + */ +class GalleryTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var ProductResource + */ + private $productResource; + + /** + * @var StoreRepositoryInterface + */ + private $storeRepository; + + /** + * @var Json + */ + private $serializer; + + /** + * @var Gallery + */ + private $block; + + /** + * @var array + */ + private $imageExpectation = [ + 'thumb' => '/m/a/magento_image.jpg', + 'img' => '/m/a/magento_image.jpg', + 'full' => '/m/a/magento_image.jpg', + 'caption' => 'Image Alt Text', + 'position' => '1', + 'isMain' => false, + 'type' => 'image', + 'videoUrl' => null, + ]; + + /** + * @var array + */ + private $thumbnailExpectation = [ + 'thumb' => '/m/a/magento_thumbnail.jpg', + 'img' => '/m/a/magento_thumbnail.jpg', + 'full' => '/m/a/magento_thumbnail.jpg', + 'caption' => 'Thumbnail Image', + 'position' => '2', + 'isMain' => false, + 'type' => 'image', + 'videoUrl' => null, + ]; + + /** + * @var array + */ + private $placeholderExpectation = [ + 'thumb' => '/placeholder/thumbnail.jpg', + 'img' => '/placeholder/image.jpg', + 'full' => '/placeholder/image.jpg', + 'caption' => '', + 'position' => '0', + 'isMain' => true, + 'type' => 'image', + 'videoUrl' => null, + ]; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->productResource = $this->objectManager->get(ProductResource::class); + $this->storeRepository = $this->objectManager->create(StoreRepositoryInterface::class); + $this->serializer = $this->objectManager->get(Json::class); + $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Gallery::class); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDbIsolation enabled + * @return void + */ + public function testGetGalleryImagesJsonWithoutImages(): void + { + $this->block->setData('product', $this->getProduct()); + $result = $this->serializer->unserialize($this->block->getGalleryImagesJson()); + $this->assertImages(reset($result), $this->placeholderExpectation); + } + + /** + * @dataProvider galleryDisabledImagesDataProvider + * @magentoDataFixture Magento/Catalog/_files/product_with_multiple_images.php + * @magentoDbIsolation enabled + * @param array $images + * @param array $expectation + * @return void + */ + public function testGetGalleryImagesJsonWithDisabledImage(array $images, array $expectation): void + { + $product = $this->getProduct(); + $this->setGalleryImages($product, $images); + $this->block->setData('product', $this->getProduct()); + $firstImage = $this->serializer->unserialize($this->block->getGalleryImagesJson()); + $this->assertImages(reset($firstImage), $expectation); + } + + /** + * @dataProvider galleryDisabledImagesDataProvider + * @magentoDataFixture Magento/Catalog/_files/product_with_multiple_images.php + * @magentoDataFixture Magento/Store/_files/second_store.php + * @magentoDbIsolation disabled + * @param array $images + * @param array $expectation + * @return void + */ + public function testGetGalleryImagesJsonOnStoreWithDisabledImage(array $images, array $expectation): void + { + $secondStoreId = (int)$this->storeRepository->get('fixture_second_store')->getId(); + $product = $this->getProduct($secondStoreId); + $this->setGalleryImages($product, $images); + $this->block->setData('product', $this->getProduct($secondStoreId)); + $firstImage = $this->serializer->unserialize($this->block->getGalleryImagesJson()); + $this->assertImages(reset($firstImage), $expectation); + } + + /** + * @return array + */ + public function galleryDisabledImagesDataProvider(): array + { + return [ + [ + 'images' => [ + '/m/a/magento_image.jpg' => ['disabled' => true], + '/m/a/magento_thumbnail.jpg' => [], + ], + 'expectation' => $this->thumbnailExpectation, + ], + ]; + } + + /** + * @dataProvider galleryImagesDataProvider + * @magentoDataFixture Magento/Catalog/_files/product_with_multiple_images.php + * @magentoDbIsolation enabled + * @param array $images + * @param array $expectation + * @return void + */ + public function testGetGalleryImagesJson(array $images, array $expectation): void + { + $product = $this->getProduct(); + $this->setGalleryImages($product, $images); + $this->block->setData('product', $this->getProduct()); + [$firstImage, $secondImage] = $this->serializer->unserialize($this->block->getGalleryImagesJson()); + [$firstExpectedImage, $secondExpectedImage] = $expectation; + $this->assertImages($firstImage, $firstExpectedImage); + $this->assertImages($secondImage, $secondExpectedImage); + } + + /** + * @return array + */ + public function galleryImagesDataProvider(): array + { + return [ + 'with_main_image' => [ + 'images' => [ + '/m/a/magento_image.jpg' => [], + '/m/a/magento_thumbnail.jpg' => ['main' => true], + ], + 'expectation' => [ + $this->imageExpectation, + array_merge($this->thumbnailExpectation, ['isMain' => true]), + ], + ], + 'without_main_image' => [ + 'images' => [ + '/m/a/magento_image.jpg' => [], + '/m/a/magento_thumbnail.jpg' => [], + ], + 'expectation' => [ + array_merge($this->imageExpectation, ['isMain' => true]), + $this->thumbnailExpectation, + ], + ], + 'with_changed_position' => [ + 'images' => [ + '/m/a/magento_image.jpg' => ['position' => '2'], + '/m/a/magento_thumbnail.jpg' => ['position' => '1'], + ], + 'expectation' => [ + array_merge($this->thumbnailExpectation, ['position' => '1']), + array_merge($this->imageExpectation, ['position' => '2', 'isMain' => true]), + ], + ], + ]; + } + + /** + * @dataProvider galleryImagesOnStoreViewDataProvider + * @magentoDataFixture Magento/Catalog/_files/product_with_multiple_images.php + * @magentoDataFixture Magento/Store/_files/second_store.php + * @magentoDbIsolation disabled + * @param array $images + * @param array $expectation + * @return void + */ + public function testGetGalleryImagesJsonOnStoreView(array $images, array $expectation): void + { + $secondStoreId = (int)$this->storeRepository->get('fixture_second_store')->getId(); + $product = $this->getProduct($secondStoreId); + $this->setGalleryImages($product, $images); + $this->block->setData('product', $this->getProduct($secondStoreId)); + [$firstImage, $secondImage] = $this->serializer->unserialize($this->block->getGalleryImagesJson()); + [$firstExpectedImage, $secondExpectedImage] = $expectation; + $this->assertImages($firstImage, $firstExpectedImage); + $this->assertImages($secondImage, $secondExpectedImage); + } + + /** + * @return array + */ + public function galleryImagesOnStoreViewDataProvider(): array + { + return [ + 'with_store_labels' => [ + 'images' => [ + '/m/a/magento_image.jpg' => ['label' => 'Some store label'], + '/m/a/magento_thumbnail.jpg' => [], + ], + 'expectation' => [ + array_merge($this->imageExpectation, ['isMain' => true, 'caption' => 'Some store label']), + $this->thumbnailExpectation, + ], + ], + 'with_changed_position' => [ + 'images' => [ + '/m/a/magento_image.jpg' => ['position' => '3'], + '/m/a/magento_thumbnail.jpg' => [], + ], + 'expectation' => [ + array_merge($this->thumbnailExpectation, ['position' => '2']), + array_merge($this->imageExpectation, ['position' => '3', 'isMain' => true]), + ], + ], + 'with_main_store_image' => [ + 'images' => [ + '/m/a/magento_image.jpg' => [], + '/m/a/magento_thumbnail.jpg' => ['main' => true], + ], + 'expectation' => [ + $this->imageExpectation, + array_merge($this->thumbnailExpectation, ['isMain' => true]), + ], + ], + ]; + } + + /** + * Updates product gallery images and saves product. + * + * @param ProductInterface $product + * @param array $images + * @param int|null $storeId + * @return void + */ + private function setGalleryImages(ProductInterface $product, array $images, int $storeId = null): void + { + $product->setImage(null); + foreach ($images as $file => $data) { + $mediaGalleryData = $product->getData('media_gallery'); + foreach ($mediaGalleryData['images'] as &$image) { + if ($image['file'] == $file) { + foreach ($data as $key => $value) { + $image[$key] = $value; + } + } + } + + $product->setData('media_gallery', $mediaGalleryData); + + if (!empty($data['main'])) { + $product->setImage($file); + } + } + + if ($storeId) { + $product->setStoreId($storeId); + } + + $this->productResource->save($product); + } + + /** + * Returns current product. + * + * @param int|null $storeId + * @return ProductInterface + */ + private function getProduct(?int $storeId = null): ProductInterface + { + return $this->productRepository->get('simple', false, $storeId, true); + } + + /** + * Asserts gallery image data. + * + * @param array $image + * @param array $expectedImage + * @return void + */ + private function assertImages(array $image, array $expectedImage): void + { + $this->assertStringEndsWith($expectedImage['thumb'], $image['thumb']); + $this->assertStringEndsWith($expectedImage['img'], $image['img']); + $this->assertStringEndsWith($expectedImage['full'], $image['full']); + $this->assertEquals($expectedImage['caption'], $image['caption']); + $this->assertEquals($expectedImage['position'], $image['position']); + $this->assertEquals($expectedImage['isMain'], $image['isMain']); + $this->assertEquals($expectedImage['type'], $image['type']); + $this->assertEquals($expectedImage['videoUrl'], $image['videoUrl']); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php new file mode 100644 index 0000000000000..e83563a6ad474 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php @@ -0,0 +1,295 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Product\View\Options; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory; +use Magento\Catalog\Api\Data\ProductCustomOptionValuesInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Block\Product\View\Options; +use Magento\Catalog\Model\Product\Option; +use Magento\Catalog\Model\Product\Option\Value; +use Magento\Framework\View\Element\Template; +use Magento\Framework\View\Result\Page; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\CacheCleaner; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Assert that product custom options render as expected. + * + * @magentoDbIsolation disabled + * @magentoAppArea frontend + */ +class RenderOptionsTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var ProductCustomOptionInterfaceFactory + */ + private $productCustomOptionFactory; + + /** + * @var ProductCustomOptionValuesInterfaceFactory + */ + private $productCustomOptionValuesFactory; + + /** + * @var Page + */ + private $page; + + /** + * @inheritdoc + */ + protected function setUp() + { + CacheCleaner::cleanAll(); + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->productCustomOptionFactory = $this->objectManager->get(ProductCustomOptionInterfaceFactory::class); + $this->productCustomOptionValuesFactory = $this->objectManager->get( + ProductCustomOptionValuesInterfaceFactory::class + ); + $this->page = $this->objectManager->create(Page::class); + parent::setUp(); + } + + /** + * Check that options from text group(field, area) render as expected. + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options_with_stock_data.php + * @dataProvider \Magento\TestFramework\Catalog\Block\Product\View\Options\TextGroupDataProvider::getData() + * + * @param array $optionData + * @param array $checkArray + * @return void + */ + public function testRenderCustomOptionsFromTextGroup(array $optionData, array $checkArray): void + { + $option = $this->addOptionToProduct($optionData); + $optionHtml = $this->getOptionHtml(); + $this->baseOptionAsserts($option, $optionHtml, $checkArray); + + if ($optionData[Option::KEY_MAX_CHARACTERS] > 0) { + $this->assertContains($checkArray['max_characters'], $optionHtml); + } else { + $this->assertNotContains('class="character-counter', $optionHtml); + } + } + + /** + * Check that options from file group(file) render as expected. + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options_with_stock_data.php + * @dataProvider \Magento\TestFramework\Catalog\Block\Product\View\Options\FileGroupDataProvider::getData() + * + * @param array $optionData + * @param array $checkArray + * @return void + */ + public function testRenderCustomOptionsFromFileGroup(array $optionData, array $checkArray): void + { + $option = $this->addOptionToProduct($optionData); + $optionHtml = $this->getOptionHtml(); + $this->baseOptionAsserts($option, $optionHtml, $checkArray); + $this->assertContains($checkArray['file_extension'], $optionHtml); + + if (isset($checkArray['file_width'])) { + $checkArray['file_width'] = sprintf($checkArray['file_width'], __('Maximum image width')); + $this->assertRegExp($checkArray['file_width'], $optionHtml); + } + + if (isset($checkArray['file_height'])) { + $checkArray['file_height'] = sprintf($checkArray['file_height'], __('Maximum image height')); + $this->assertRegExp($checkArray['file_height'], $optionHtml); + } + } + + /** + * Check that options from select group(drop-down, radio buttons, checkbox, multiple select) render as expected. + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options_with_stock_data.php + * @dataProvider \Magento\TestFramework\Catalog\Block\Product\View\Options\SelectGroupDataProvider::getData() + * + * @param array $optionData + * @param array $optionValueData + * @param array $checkArray + * @return void + */ + public function testRenderCustomOptionsFromSelectGroup( + array $optionData, + array $optionValueData, + array $checkArray + ): void { + $option = $this->addOptionToProduct($optionData, $optionValueData); + $optionValues = $option->getValues(); + $optionValue = reset($optionValues); + $optionHtml = $this->getOptionHtml(); + $this->baseOptionAsserts($option, $optionHtml, $checkArray); + + if (isset($checkArray['not_contain_arr'])) { + foreach ($checkArray['not_contain_arr'] as $notContainPattern) { + $this->assertNotRegExp($notContainPattern, $optionHtml); + } + } + + if (isset($checkArray['option_value_item'])) { + $checkArray['option_value_item'] = sprintf( + $checkArray['option_value_item'], + $optionValue->getOptionTypeId(), + $optionValueData[Value::KEY_TITLE] + ); + $this->assertRegExp($checkArray['option_value_item'], $optionHtml); + } + } + + /** + * Check that options from date group(date, date & time, time) render as expected. + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options_with_stock_data.php + * @dataProvider \Magento\TestFramework\Catalog\Block\Product\View\Options\DateGroupDataProvider::getData() + * + * @param array $optionData + * @param array $checkArray + * @return void + */ + public function testRenderCustomOptionsFromDateGroup(array $optionData, array $checkArray): void + { + $option = $this->addOptionToProduct($optionData); + $optionHtml = $this->getOptionHtml(); + $this->baseOptionAsserts($option, $optionHtml, $checkArray); + + switch ($optionData[Option::KEY_TYPE]) { + case ProductCustomOptionInterface::OPTION_TYPE_DATE: + $this->assertContains("<select name=\"options[{$option->getOptionId()}][month]\"", $optionHtml); + $this->assertContains("<select name=\"options[{$option->getOptionId()}][day]\"", $optionHtml); + $this->assertContains("<select name=\"options[{$option->getOptionId()}][year]\"", $optionHtml); + $this->assertNotContains("<select name=\"options[{$option->getOptionId()}][hour]\"", $optionHtml); + $this->assertNotContains("<select name=\"options[{$option->getOptionId()}][minute]\"", $optionHtml); + $this->assertNotContains("<select name=\"options[{$option->getOptionId()}][day_part]\"", $optionHtml); + break; + case ProductCustomOptionInterface::OPTION_TYPE_DATE_TIME: + $this->assertContains("<select name=\"options[{$option->getOptionId()}][month]\"", $optionHtml); + $this->assertContains("<select name=\"options[{$option->getOptionId()}][day]\"", $optionHtml); + $this->assertContains("<select name=\"options[{$option->getOptionId()}][year]\"", $optionHtml); + $this->assertContains("<select name=\"options[{$option->getOptionId()}][hour]\"", $optionHtml); + $this->assertContains("<select name=\"options[{$option->getOptionId()}][minute]\"", $optionHtml); + $this->assertContains("<select name=\"options[{$option->getOptionId()}][day_part]\"", $optionHtml); + break; + case ProductCustomOptionInterface::OPTION_TYPE_TIME: + $this->assertNotContains("<select name=\"options[{$option->getOptionId()}][month]\"", $optionHtml); + $this->assertNotContains("<select name=\"options[{$option->getOptionId()}][day]\"", $optionHtml); + $this->assertNotContains("<select name=\"options[{$option->getOptionId()}][year]\"", $optionHtml); + $this->assertContains("<select name=\"options[{$option->getOptionId()}][hour]\"", $optionHtml); + $this->assertContains("<select name=\"options[{$option->getOptionId()}][minute]\"", $optionHtml); + $this->assertContains("<select name=\"options[{$option->getOptionId()}][day_part]\"", $optionHtml); + break; + } + } + + /** + * Base asserts for rendered options. + * + * @param ProductCustomOptionInterface $option + * @param string $optionHtml + * @param array $checkArray + * @return void + */ + private function baseOptionAsserts( + ProductCustomOptionInterface $option, + string $optionHtml, + array $checkArray + ): void { + $this->assertContains($checkArray['block_with_required_class'], $optionHtml); + $this->assertContains($checkArray['title'], $optionHtml); + + if (isset($checkArray['label_for_created_option'])) { + $checkArray['label_for_created_option'] = sprintf( + $checkArray['label_for_created_option'], + $option->getOptionId() + ); + $this->assertContains($checkArray['label_for_created_option'], $optionHtml); + } + + if (isset($checkArray['price'])) { + $this->assertContains($checkArray['price'], $optionHtml); + } + + if (isset($checkArray['required_element'])) { + $this->assertRegExp($checkArray['required_element'], $optionHtml); + } + } + + /** + * Add custom option to product with data. + * + * @param array $optionData + * @param array $optionValueData + * @return ProductCustomOptionInterface + */ + private function addOptionToProduct(array $optionData, array $optionValueData = []): ProductCustomOptionInterface + { + $product = $this->productRepository->get('simple'); + $optionData[Option::KEY_PRODUCT_SKU] = $product->getSku(); + + if (!empty($optionValueData)) { + $optionValueData = $this->productCustomOptionValuesFactory->create(['data' => $optionValueData]); + $optionData['values'] = [$optionValueData]; + } + + $option = $this->productCustomOptionFactory->create(['data' => $optionData]); + $product->setOptions([$option]); + $createdOptions = $this->productRepository->save($product)->getOptions(); + + return reset($createdOptions); + } + + /** + * Render custom options block. + * + * @return string + */ + private function getOptionHtml(): string + { + $product = $this->productRepository->get('simple'); + $optionsBlock = $this->getOptionsBlock(); + $optionsBlock->setProduct($product); + + return $optionsBlock->toHtml(); + } + + /** + * Get options block. + * + * @return Options + */ + private function getOptionsBlock(): Options + { + $this->page->addHandle([ + 'default', + 'catalog_product_view', + ]); + $this->page->getLayout()->generateXml(); + /** @var Template $productInfoFormOptionsBlock */ + $productInfoFormOptionsBlock = $this->page->getLayout()->getBlock('product.info.form.options'); + $optionsWrapperBlock = $productInfoFormOptionsBlock->getChildBlock('product_options_wrapper'); + + return $optionsWrapperBlock->getChildBlock('product_options'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/OptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/OptionsTest.php index 152ad6a6286f4..3d767502dd784 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/OptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/OptionsTest.php @@ -3,8 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Block\Product\View; +use Magento\CatalogRule\Model\Indexer\IndexBuilder; + /** * Test class for \Magento\Catalog\Block\Product\View\Options. */ @@ -30,12 +34,19 @@ class OptionsTest extends \PHPUnit\Framework\TestCase */ protected $productRepository; + /** + * @var IndexBuilder + */ + private $indexBuilder; + protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); $this->productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $this->indexBuilder = $this->objectManager->create(IndexBuilder::class); + try { $this->product = $this->productRepository->get('simple'); } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { @@ -113,24 +124,63 @@ private function getExpectedJsonConfig() { return [ 0 => [ - 'prices' => - ['oldPrice' => - ['amount' => 10, 'adjustments' => []], + 'prices' => ['oldPrice' => ['amount' => 10, 'adjustments' => []], 'basePrice' => ['amount' => 10], 'finalPrice' => ['amount' => 10] ], - 'type' => 'fixed', - 'name' => 'drop_down option 1', + 'type' => 'fixed', + 'name' => 'drop_down option 1', ], 1 => [ - 'prices' => - ['oldPrice' => - ['amount' => 40, 'adjustments' => []], + 'prices' => ['oldPrice' => ['amount' => 40, 'adjustments' => []], 'basePrice' => ['amount' => 40], 'finalPrice' => ['amount' => 40], ], - 'type' => 'percent', - 'name' => 'drop_down option 2', + 'type' => 'percent', + 'name' => 'drop_down option 2', + ], + ]; + } + + /** + * Test option prices with catalog price rules applied. + * + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/CatalogRule/_files/two_rules.php + * @magentoDataFixture Magento/Catalog/_files/product_with_dropdown_option.php + */ + public function testGetJsonConfigWithCatalogRules() + { + $this->indexBuilder->reindexFull(); + sleep(1); + $config = json_decode($this->block->getJsonConfig(), true); + $configValues = array_values($config); + $this->assertEquals($this->getExpectedJsonConfigWithCatalogRules(), array_values($configValues[0])); + } + + /** + * Expected data for testGetJsonConfigWithCatalogRules + * + * @return array + */ + private function getExpectedJsonConfigWithCatalogRules() + { + return [ + 0 => [ + 'prices' => ['oldPrice' => ['amount' => 10, 'adjustments' => []], + 'basePrice' => ['amount' => 9.5], + 'finalPrice' => ['amount' => 9.5], + ], + 'type' => 'fixed', + 'name' => 'drop_down option 1', + ], + 1 => [ + 'prices' => ['oldPrice' => ['amount' => 40, 'adjustments' => []], + 'basePrice' => ['amount' => 38], + 'finalPrice' => ['amount' => 38], + ], + 'type' => 'percent', + 'name' => 'drop_down option 2', ], ]; } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ViewTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ViewTest.php index 01398213b854a..59582f313cf55 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ViewTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/ViewTest.php @@ -3,94 +3,273 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Block\Product; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Block\Product\View\Description; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\View\LayoutInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + /** - * Test class for \Magento\Catalog\Block\Product\View. + * Checks product view block. * + * @see \Magento\Catalog\Block\Product\View * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDbIsolation enabled */ -class ViewTest extends \PHPUnit\Framework\TestCase +class ViewTest extends TestCase { + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var View */ + private $block; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var Registry */ + private $registry; + + /** @var LayoutInterface */ + private $layout; + + /** @var Json */ + private $json; + + /** @var StoreManagerInterface */ + private $storeManager; + + /** @var Description */ + private $descriptionBlock; + + /** @var array */ + private const SHORT_DESCRIPTION_BLOCK_DATA = [ + 'at_call' => 'getShortDescription', + 'at_code' => 'short_description', + 'overview' => 'overview', + 'at_label' => 'none', + 'title' => 'Overview', + 'add_attribute' => 'description', + ]; + /** - * @var \Magento\Catalog\Block\Product\View + * @inheritdoc */ - protected $_block; + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->block = $this->layout->createBlock(View::class); + $this->registry = $this->objectManager->get(Registry::class); + $this->json = $this->objectManager->get(Json::class); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + $this->descriptionBlock = $this->layout->createBlock(Description::class); + } /** - * @var \Magento\Catalog\Model\Product + * @return void */ - protected $_product; + public function testSetLayout(): void + { + $productView = $this->layout->createBlock(View::class); - protected function setUp() + $this->assertInstanceOf(LayoutInterface::class, $productView->getLayout()); + } + + /** + * @return void + */ + public function testGetProduct(): void { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->_block = $objectManager->create(\Magento\Catalog\Block\Product\View::class); + $product = $this->productRepository->get('simple'); + $this->registerProduct($product); + + $this->assertNotEmpty($this->block->getProduct()->getId()); + $this->assertEquals($product->getId(), $this->block->getProduct()->getId()); - /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ - $productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); - $this->_product = $productRepository->get('simple'); + $this->registry->unregister('product'); + $this->block->setProductId($product->getId()); - $objectManager->get(\Magento\Framework\Registry::class)->unregister('product'); - $objectManager->get(\Magento\Framework\Registry::class)->register('product', $this->_product); + $this->assertEquals($product->getId(), $this->block->getProduct()->getId()); } - public function testSetLayout() + /** + * @return void + */ + public function testCanEmailToFriend(): void + { + $this->assertFalse($this->block->canEmailToFriend()); + } + + /** + * @return void + */ + public function testGetAddToCartUrl(): void { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $product = $this->productRepository->get('simple'); + $url = $this->block->getAddToCartUrl($product); - /** @var $layout \Magento\Framework\View\Layout */ - $layout = $objectManager->get(\Magento\Framework\View\LayoutInterface::class); + $this->assertStringMatchesFormat( + '%scheckout/cart/add/%sproduct/' . $product->getId() . '/', + $url + ); + } - $productView = $layout->createBlock(\Magento\Catalog\Block\Product\View::class); + /** + * @return void + */ + public function testGetJsonConfig(): void + { + $product = $this->productRepository->get('simple'); + $this->registerProduct($product); + $config = $this->json->unserialize($this->block->getJsonConfig()); - $this->assertInstanceOf(\Magento\Framework\View\LayoutInterface::class, $productView->getLayout()); + $this->assertNotEmpty($config); + $this->assertArrayHasKey('productId', $config); + $this->assertEquals($product->getId(), $config['productId']); } - public function testGetProduct() + /** + * @return void + */ + public function testHasOptions(): void { - $this->assertNotEmpty($this->_block->getProduct()->getId()); - $this->assertEquals($this->_product->getId(), $this->_block->getProduct()->getId()); - - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $objectManager->get(\Magento\Framework\Registry::class)->unregister('product'); - $this->_block->setProductId($this->_product->getId()); - $this->assertEquals($this->_product->getId(), $this->_block->getProduct()->getId()); + $product = $this->productRepository->get('simple'); + $this->registerProduct($product); + + $this->assertTrue($this->block->hasOptions()); } - public function testCanEmailToFriend() + /** + * @return void + */ + public function testHasRequiredOptions(): void { - $this->assertFalse($this->_block->canEmailToFriend()); + $product = $this->productRepository->get('simple'); + $this->registerProduct($product); + + $this->assertTrue($this->block->hasRequiredOptions()); + } + + /** + * @return void + */ + public function testStartBundleCustomization(): void + { + $this->markTestSkipped("Functionality not implemented in Magento 1.x. Implemented in Magento 2"); + + $this->assertFalse($this->block->startBundleCustomization()); } - public function testGetAddToCartUrl() + /** + * @magentoAppArea frontend + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php + */ + public function testAddToCartBlockInvisibility(): void { - $url = $this->_block->getAddToCartUrl($this->_product); - $this->assertStringMatchesFormat('%scheckout/cart/add/%sproduct/' . $this->_product->getId() . '/', $url); + $outOfStockProduct = $this->productRepository->get('simple-out-of-stock'); + $this->registerProduct($outOfStockProduct); + $this->block->setTemplate('Magento_Catalog::product/view/addtocart.phtml'); + $output = $this->block->toHtml(); + + $this->assertNotContains((string)__('Add to Cart'), $output); } - public function testGetJsonConfig() + /** + * @magentoAppArea frontend + */ + public function testAddToCartBlockVisibility(): void { - $config = (array)json_decode($this->_block->getJsonConfig()); - $this->assertNotEmpty($config); - $this->assertArrayHasKey('productId', $config); - $this->assertEquals($this->_product->getId(), $config['productId']); + $product = $this->productRepository->get('simple'); + $this->registerProduct($product); + $this->block->setTemplate('Magento_Catalog::product/view/addtocart.phtml'); + $output = $this->block->toHtml(); + + $this->assertContains((string)__('Add to Cart'), $output); } - public function testHasOptions() + /** + * @magentoDbIsolation disabled + * @magentoAppArea frontend + * @magentoDataFixture Magento/Catalog/_files/product_multistore_different_short_description.php + * @return void + */ + public function testProductShortDescription(): void + { + $product = $this->productRepository->get('simple-different-short-description'); + $currentStoreId = $this->storeManager->getStore()->getId(); + $output = $this->renderDescriptionBlock($product); + + $this->assertContains('First store view short description', $output); + + $secondStore = $this->storeManager->getStore('fixturestore'); + $this->storeManager->setCurrentStore($secondStore->getId()); + + try { + $product = $this->productRepository->get( + 'simple-different-short-description', + false, + $secondStore->getId(), + true + ); + $newBlockOutput = $this->renderDescriptionBlock($product, true); + + $this->assertContains('Second store view short description', $newBlockOutput); + } finally { + $this->storeManager->setCurrentStore($currentStoreId); + } + } + + /** + * @param ProductInterface $product + * @param bool $refreshBlock + * @return string + */ + private function renderDescriptionBlock(ProductInterface $product, bool $refreshBlock = false): string { - $this->assertTrue($this->_block->hasOptions()); + $this->registerProduct($product); + $descriptionBlock = $this->getDescriptionBlock($refreshBlock); + $descriptionBlock->addData(self::SHORT_DESCRIPTION_BLOCK_DATA); + $descriptionBlock->setTemplate('Magento_Catalog::product/view/attribute.phtml'); + + return $this->descriptionBlock->toHtml(); } - public function testHasRequiredOptions() + /** + * Get description block + * + * @param bool $refreshBlock + * @return Description + */ + private function getDescriptionBlock(bool $refreshBlock): Description { - $this->assertTrue($this->_block->hasRequiredOptions()); + if ($refreshBlock) { + $this->descriptionBlock = $this->layout->createBlock(Description::class); + } + + return $this->descriptionBlock; } - public function testStartBundleCustomization() + /** + * Register the product + * + * @param ProductInterface $product + * @return void + */ + private function registerProduct(ProductInterface $product): void { - $this->markTestSkipped("Functionality not implemented in Magento 1.x. Implemented in Magento 2"); - $this->assertFalse($this->_block->startBundleCustomization()); + $this->registry->unregister('product'); + $this->registry->register('product', $product); } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Category/DeleteTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Category/DeleteTest.php new file mode 100644 index 0000000000000..8db16fa2c4546 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Category/DeleteTest.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Category; + +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Test for class \Magento\Catalog\Controller\Adminhtml\Category\Delete + * + * @magentoAppArea adminhtml + */ +class DeleteTest extends AbstractBackendController +{ + /** + * @return void + */ + public function testDeleteMissingCategory(): void + { + $incorrectId = 825852; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue(['id' => $incorrectId]); + $this->dispatch('backend/catalog/category/delete'); + $this->assertSessionMessages( + $this->equalTo([(string)__(sprintf('No such entity with id = %s', $incorrectId))]), + MessageInterface::TYPE_ERROR + ); + } + + /** + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/category.php + */ + public function testDeleteCategory(): void + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue(['id' => 333]); + $this->dispatch('backend/catalog/category/delete'); + $this->assertSessionMessages($this->equalTo([(string)__('You deleted the category.')])); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php index c0eeb75592a5d..e89e5aae92cf5 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php @@ -7,21 +7,27 @@ namespace Magento\Catalog\Controller\Adminhtml; +use Magento\Framework\Acl\Builder; use Magento\Backend\App\Area\FrontNameResolver; use Magento\Catalog\Api\CategoryRepositoryInterface; -use Magento\Catalog\Model\Category as Category; -use Magento\Catalog\Model\ResourceModel\Product as ProductResource; use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Registry; +use Magento\TestFramework\Catalog\Model\CategoryLayoutUpdateManager; use Magento\Framework\Serialize\Serializer\Json; use Magento\Store\Api\StoreRepositoryInterface; use Magento\TestFramework\TestCase\AbstractBackendController; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Store\Model\Store; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\Catalog\Model\Category as CategoryModel; +use Magento\Catalog\Model\CategoryFactory as CategoryModelFactory; /** - * Test for category backend actions + * Test for admin category functionality. * * @magentoAppArea adminhtml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CategoryTest extends AbstractBackendController { @@ -29,24 +35,46 @@ class CategoryTest extends AbstractBackendController * @var ProductResource */ protected $productResource; + /** + * @var Builder + */ + private $aclBuilder; - /** @var CategoryRepositoryInterface */ + /** + * @var CategoryModelFactory + */ + private $categoryFactory; + + /** + * @var CategoryRepositoryInterface + */ private $categoryRepository; - /** @var StoreRepositoryInterface */ + /** + * @var StoreRepositoryInterface + */ private $storeRepository; - /** @var Json */ + /** + * @var Json + */ private $json; /** - * @inheritdoc + * @inheritDoc + * + * @throws \Magento\Framework\Exception\AuthenticationException */ protected function setUp() { parent::setUp(); + /** @var ProductResource $productResource */ - $this->productResource = $this->_objectManager->get(ProductResource::class); + $this->productResource = Bootstrap::getObjectManager()->get( + ProductResource::class + ); + $this->aclBuilder = Bootstrap::getObjectManager()->get(Builder::class); + $this->categoryFactory = Bootstrap::getObjectManager()->get(CategoryModelFactory::class); $this->categoryRepository = $this->_objectManager->get(CategoryRepositoryInterface::class); $this->storeRepository = $this->_objectManager->get(StoreRepositoryInterface::class); $this->json = $this->_objectManager->get(Json::class); @@ -63,6 +91,7 @@ protected function setUp() * @param array $defaultAttributes * @param array $attributesSaved * @return void + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testSaveAction(array $inputData, array $defaultAttributes, array $attributesSaved = []): void { @@ -107,6 +136,8 @@ public function testSaveAction(array $inputData, array $defaultAttributes, array * @magentoDbIsolation enabled * @magentoDataFixture Magento/CatalogUrlRewrite/_files/categories.php * @return void + * @throws \Magento\Framework\Exception\CouldNotSaveException + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function testDefaultValueForCategoryUrlPath(): void { @@ -125,11 +156,12 @@ public function testDefaultValueForCategoryUrlPath(): void // set default url_path and check it $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $postData = $category->getData(); - $postData['use_default'] = [ - 'available_sort_by' => 1, - 'default_sort_by' => 1, - 'url_key' => 1, - ]; + $postData['use_default'] = + [ + 'available_sort_by' => 1, + 'default_sort_by' => 1, + 'url_key' => 1, + ]; $this->getRequest()->setPostValue($postData); $this->dispatch('backend/catalog/category/save'); $this->assertSessionMessages( @@ -137,7 +169,7 @@ public function testDefaultValueForCategoryUrlPath(): void MessageInterface::TYPE_SUCCESS ); $category = $this->categoryRepository->get($categoryId); - $this->assertEquals($defaultUrlPath, $category->getData('url_path')); + $this->assertEquals($defaultUrlPath, $category->getData('url_key')); } /** @@ -260,7 +292,7 @@ public function saveActionDataProvider(): array 'custom_design_from' => 1, 'custom_design_to' => 1, 'page_layout' => 1, - 'custom_layout_update' => 1, + 'custom_layout_update' => null, ], ], [ @@ -306,7 +338,6 @@ public function saveActionDataProvider(): array 'custom_design_from' => '5/21/2015', 'custom_design_to' => '5/29/2015', 'page_layout' => '', - 'custom_layout_update' => '', 'use_config' => [ 'available_sort_by' => 1, 'default_sort_by' => 1, @@ -328,7 +359,6 @@ public function saveActionDataProvider(): array 'description' => true, 'meta_keywords' => true, 'meta_description' => true, - 'custom_layout_update' => true, 'custom_design_from' => true, 'custom_design_to' => true, 'filter_price_range' => false @@ -348,7 +378,6 @@ public function saveActionDataProvider(): array 'description' => 'Custom Description', 'meta_keywords' => 'Custom keywords', 'meta_description' => 'Custom meta description', - 'custom_layout_update' => null, 'custom_design_from' => '2015-05-21 00:00:00', 'custom_design_to' => '2015-05-29 00:00:00', 'filter_price_range' => null @@ -437,7 +466,7 @@ public function testMoveAction( ]; foreach ($urlKeys as $categoryId => $urlKey) { /** @var $category Category */ - $category = $this->_objectManager->create(Category::class); + $category = $this->categoryFactory->create(); if ($categoryId > 0) { $category->load($categoryId) ->setUrlKey($urlKey) @@ -598,14 +627,246 @@ private function getCategoryProductsCount(): int ); } + /** + * Check whether additional authorization is required for the design fields. + * + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @throws \Throwable + * @return void + */ + public function testSaveDesign(): void + { + /** @var $store \Magento\Store\Model\Store */ + $store = Bootstrap::getObjectManager()->create(Store::class); + $store->load('fixturestore', 'code'); + $storeId = $store->getId(); + $requestData = [ + 'id' => '2', + 'entity_id' => '2', + 'path' => '1/2', + 'name' => 'Custom Name', + 'is_active' => '0', + 'description' => 'Custom Description', + 'meta_title' => 'Custom Title', + 'meta_keywords' => 'Custom keywords', + 'meta_description' => 'Custom meta description', + 'include_in_menu' => '0', + 'url_key' => 'default-test-category', + 'display_mode' => 'PRODUCTS', + 'landing_page' => '1', + 'is_anchor' => true, + 'store_id' => $storeId, + 'use_config' => [ + 'available_sort_by' => 1, + 'default_sort_by' => 1, + 'filter_price_range' => 1, + ], + ]; + $uri = 'backend/catalog/category/save'; + + //Trying to update the category's design settings without proper permissions. + //Expected list of sessions messages collected throughout the controller calls. + $sessionMessages = ['Not allowed to edit the category\'s design attributes']; + $this->aclBuilder->getAcl()->deny(null, 'Magento_Catalog::edit_category_design'); + $requestData['custom_layout_update_file'] = 'test-file'; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->getRequest()->setParam('store', $requestData['store_id']); + $this->getRequest()->setParam('id', $requestData['id']); + $this->dispatch($uri); + $this->assertSessionMessages( + self::equalTo($sessionMessages), + MessageInterface::TYPE_ERROR + ); + + //Trying again with the permissions. + $requestData['custom_layout_update_file'] = null; + $requestData['page_layout'] = '2columns-left'; + $this->aclBuilder->getAcl() + ->allow(null, ['Magento_Catalog::categories', 'Magento_Catalog::edit_category_design']); + $this->getRequest()->setDispatched(false); + $this->getRequest()->setPostValue($requestData); + $this->getRequest()->setParam('store', $requestData['store_id']); + $this->getRequest()->setParam('id', $requestData['id']); + $this->dispatch($uri); + /** @var CategoryModel $category */ + $category = $this->categoryFactory->create(); + $category->load(2); + $this->assertEquals('2columns-left', $category->getData('page_layout')); + //No new error messages + $this->assertSessionMessages( + self::equalTo($sessionMessages), + MessageInterface::TYPE_ERROR + ); + + //Trying to save special value without the permissions. + $requestData['custom_layout_update_file'] = CategoryModel\Attribute\Backend\LayoutUpdate::VALUE_USE_UPDATE_XML; + $requestData['description'] = 'test'; + $this->aclBuilder->getAcl()->deny(null, ['Magento_Catalog::edit_category_design']); + $this->getRequest()->setDispatched(false); + $this->getRequest()->setPostValue($requestData); + $this->getRequest()->setParam('store', $requestData['store_id']); + $this->getRequest()->setParam('id', $requestData['id']); + $this->dispatch($uri); + /** @var CategoryModel $category */ + $category = $this->categoryFactory->create(); + $category->load(2); + $this->assertEquals('2columns-left', $category->getData('page_layout')); + $this->assertEmpty($category->getData('custom_layout_update_file')); + $this->assertEquals('test', $category->getData('description')); + //No new error messages + $this->assertSessionMessages( + self::equalTo($sessionMessages), + MessageInterface::TYPE_ERROR + ); + } + + /** + * Save design attributes with default values without design permissions. + * + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @return void + * @throws \Throwable + */ + public function testSaveDesignWithDefaults(): void + { + /** @var $store \Magento\Store\Model\Store */ + $store = Bootstrap::getObjectManager()->create(Store::class); + $store->load('fixturestore', 'code'); + $storeId = $store->getId(); + /** @var CategoryModel $category */ + $category = $this->categoryFactory->create(); + $category->load(2); + $attributes = $category->getAttributes(); + $attributes['custom_design']->setDefaultValue('1'); + $attributes['custom_design']->save(); + $requestData = [ + 'name' => 'Test name', + 'parent_id' => '2', + 'is_active' => '0', + 'description' => 'Custom Description', + 'meta_title' => 'Custom Title', + 'meta_keywords' => 'Custom keywords', + 'meta_description' => 'Custom meta description', + 'include_in_menu' => '0', + 'url_key' => 'default-test-category-test', + 'display_mode' => 'PRODUCTS', + 'landing_page' => '1', + 'is_anchor' => true, + 'store_id' => $storeId, + 'use_config' => [ + 'available_sort_by' => 1, + 'default_sort_by' => 1, + 'filter_price_range' => 1, + ], + 'custom_design' => '1', + 'custom_apply_to_products' => '0' + ]; + $uri = 'backend/catalog/category/save'; + + //Updating the category's design settings without proper permissions. + $this->aclBuilder->getAcl()->deny(null, 'Magento_Catalog::edit_category_design'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->getRequest()->setParam('store', $requestData['store_id']); + $this->dispatch($uri); + + //Verifying that category was saved. + /** @var Registry $registry */ + $registry = Bootstrap::getObjectManager()->get(Registry::class); + $id = $registry->registry('current_category')->getId(); + /** @var CategoryModel $category */ + $category = $this->categoryFactory->create(); + $category->load($id); + $this->assertNotEmpty($category->getId()); + $this->assertEquals('1', $category->getData('custom_design')); + } + + /** + * Test custom update files functionality. + * + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @throws \Throwable + * @return void + */ + public function testSaveCustomLayout(): void + { + $file = 'test_file'; + /** @var $store \Magento\Store\Model\Store */ + $store = Bootstrap::getObjectManager()->create(Store::class); + /** @var CategoryLayoutUpdateManager $layoutManager */ + $layoutManager = Bootstrap::getObjectManager()->get(CategoryLayoutUpdateManager::class); + $layoutManager->setCategoryFakeFiles(2, [$file]); + $store->load('fixturestore', 'code'); + $storeId = $store->getId(); + $requestData = [ + 'id' => '2', + 'entity_id' => '2', + 'path' => '1/2', + 'name' => 'Custom Name', + 'is_active' => '0', + 'description' => 'Custom Description', + 'meta_title' => 'Custom Title', + 'meta_keywords' => 'Custom keywords', + 'meta_description' => 'Custom meta description', + 'include_in_menu' => '0', + 'url_key' => 'default-test-category', + 'display_mode' => 'PRODUCTS', + 'landing_page' => '1', + 'is_anchor' => true, + 'store_id' => $storeId, + 'use_config' => [ + 'available_sort_by' => 1, + 'default_sort_by' => 1, + 'filter_price_range' => 1, + ], + ]; + $uri = 'backend/catalog/category/save'; + + //Saving a wrong file + $requestData['custom_layout_update_file'] = $file . 'INVALID'; + $this->getRequest()->setDispatched(false); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->getRequest()->setParam('store', $requestData['store_id']); + $this->getRequest()->setParam('id', $requestData['id']); + $this->dispatch($uri); + + //Checking that the value is not saved + /** @var CategoryModel $category */ + $category = $this->categoryFactory->create(); + $category->load($requestData['entity_id']); + $this->assertEmpty($category->getData('custom_layout_update_file')); + + //Saving the correct file + $requestData['custom_layout_update_file'] = $file; + $this->getRequest()->setDispatched(false); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->getRequest()->setParam('store', $requestData['store_id']); + $this->getRequest()->setParam('id', $requestData['id']); + $this->dispatch($uri); + + //Checking that the value is saved + /** @var CategoryModel $category */ + $category = $this->categoryFactory->create(); + $category->load($requestData['entity_id']); + $this->assertEquals($file, $category->getData('custom_layout_update_file')); + } + /** * Verify that the category cannot be saved if the category url matches the admin url. * + * @return void * @magentoConfigFixture admin/url/use_custom_path 1 * @magentoConfigFixture admin/url/custom_path backend */ - public function testSaveWithCustomBackendNameAction() + public function testSaveWithCustomBackendNameAction(): void { + /** @var FrontNameResolver $frontNameResolver */ $frontNameResolver = Bootstrap::getObjectManager()->create(FrontNameResolver::class); $urlKey = $frontNameResolver->getFrontName(); $inputData = [ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php index 3ec8c806dcbb1..2f35a5fdafc3a 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php @@ -22,12 +22,15 @@ class AttributeTest extends \Magento\TestFramework\TestCase\AbstractBackendContr protected function setUp() { - $this->publisherConsumerController = Bootstrap::getObjectManager()->create(PublisherConsumerController::class, [ - 'consumers' => $this->consumers, - 'logFilePath' => TESTS_TEMP_DIR . "/MessageQueueTestLog.txt", - 'maxMessages' => null, - 'appInitParams' => Bootstrap::getInstance()->getAppInitParams() - ]); + $this->publisherConsumerController = Bootstrap::getObjectManager()->create( + PublisherConsumerController::class, + [ + 'consumers' => $this->consumers, + 'logFilePath' => TESTS_TEMP_DIR . "/MessageQueueTestLog.txt", + 'maxMessages' => null, + 'appInitParams' => Bootstrap::getInstance()->getAppInitParams() + ] + ); try { $this->publisherConsumerController->startConsumers(); @@ -124,7 +127,7 @@ public function testSaveActionChangeVisibility($attributes) $this->publisherConsumerController->waitForAsynchronousResult( function () use ($repository) { - sleep(3); + sleep(10); // Should be refactored in the scope of MC-22947 return $repository->get( 'simple', false, diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete/AbstractDeleteAttributeControllerTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete/AbstractDeleteAttributeControllerTest.php new file mode 100644 index 0000000000000..6f1ff8567349f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete/AbstractDeleteAttributeControllerTest.php @@ -0,0 +1,83 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product\Attribute\Delete; + +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Framework\App\Request\Http; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Abstract delete attribute test using catalog/product_attribute/delete controller action. + */ +abstract class AbstractDeleteAttributeControllerTest extends AbstractBackendController +{ + /** + * @var string + */ + protected $uri = 'backend/catalog/product_attribute/delete/attribute_id/%s'; + + /** + * @var ProductAttributeRepositoryInterface + */ + private $productAttributeRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->productAttributeRepository = $this->_objectManager->get(ProductAttributeRepositoryInterface::class); + } + + /** + * Delete attribute via controller action. + * + * @param string $attributeCode + * @return void + */ + protected function dispatchDeleteAttribute(string $attributeCode): void + { + $attribute = $this->productAttributeRepository->get($attributeCode); + $this->getRequest()->setMethod(Http::METHOD_POST); + $this->dispatch(sprintf($this->uri, $attribute->getAttributeId())); + $this->assertSessionMessages( + $this->equalTo([(string)__('You deleted the product attribute.')]), + MessageInterface::TYPE_SUCCESS + ); + } + + /** + * Assert that attribute is deleted from DB. + * + * @param string $attributeCode + * @return void + */ + protected function assertAttributeIsDeleted(string $attributeCode): void + { + $this->expectExceptionObject( + new NoSuchEntityException( + __( + 'The attribute with a "%1" attributeCode doesn\'t exist. Verify the attribute and try again.', + $attributeCode + ) + ) + ); + $this->productAttributeRepository->get($attributeCode); + } + + /** + * @inheritdoc + */ + public function testAclHasAccess() + { + $this->markTestIncomplete('AclHasAccess test is not complete'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete/CatalogAttributesControllerTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete/CatalogAttributesControllerTest.php new file mode 100644 index 0000000000000..e95737209cb7f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete/CatalogAttributesControllerTest.php @@ -0,0 +1,145 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product\Attribute\Delete; + +/** + * Delete catalog product attributes with input types like "media_image", "price", + * "date", "select", "multiselect", "textarea", "texteditor", "text" and "boolean". + * Attributes from Magento_Catalog and Magento_Eav modules. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + */ +class CatalogAttributesControllerTest extends AbstractDeleteAttributeControllerTest +{ + /** + * Assert that attribute with input type "media_image" will be deleted + * after dispatch delete product attribute action. + * + * @magentoDataFixture Magento/Catalog/_files/product_image_attribute.php + * + * @return void + */ + public function testDeleteMediaImageAttribute(): void + { + $this->dispatchDeleteAttribute('image_attribute'); + $this->assertAttributeIsDeleted('image_attribute'); + } + + /** + * Assert that attribute with input type "price" will be deleted + * after dispatch delete product attribute action. + * + * @magentoDataFixture Magento/Catalog/_files/product_decimal_attribute.php + * + * @return void + */ + public function testDeletePriceAttribute(): void + { + $this->dispatchDeleteAttribute('decimal_attribute'); + $this->assertAttributeIsDeleted('decimal_attribute'); + } + + /** + * Assert that attribute with input type "date" will be deleted + * after dispatch delete product attribute action. + * + * @magentoDataFixture Magento/Catalog/_files/product_date_attribute.php + * + * @return void + */ + public function testDeleteDateAttribute(): void + { + $this->dispatchDeleteAttribute('date_attribute'); + $this->assertAttributeIsDeleted('date_attribute'); + } + + /** + * Assert that attribute with input type "select" will be deleted + * after dispatch delete product attribute action. + * + * @magentoDataFixture Magento/Catalog/_files/product_dropdown_attribute.php + * + * @return void + */ + public function testDeleteSelectAttribute(): void + { + $this->dispatchDeleteAttribute('dropdown_attribute'); + $this->assertAttributeIsDeleted('dropdown_attribute'); + } + + /** + * Assert that attribute with input type "multiselect" will be deleted + * after dispatch delete product attribute action. + * + * @magentoDataFixture Magento/Catalog/_files/multiselect_attribute.php + * + * @return void + */ + public function testDeleteMultiselectAttribute(): void + { + $this->dispatchDeleteAttribute('multiselect_attribute'); + $this->assertAttributeIsDeleted('multiselect_attribute'); + } + + /** + * Assert that attribute with input type "textarea" will be deleted + * after dispatch delete product attribute action. + * + * @magentoDataFixture Magento/Catalog/_files/product_text_attribute.php + * + * @return void + */ + public function testDeleteTextareaAttribute(): void + { + $this->dispatchDeleteAttribute('text_attribute'); + $this->assertAttributeIsDeleted('text_attribute'); + } + + /** + * Assert that attribute with input type "texteditor" will be deleted + * after dispatch delete product attribute action. + * + * @magentoDataFixture Magento/Eav/_files/product_texteditor_attribute.php + * + * @return void + */ + public function testDeleteTextEditorAttribute(): void + { + $this->dispatchDeleteAttribute('text_editor_attribute'); + $this->assertAttributeIsDeleted('text_editor_attribute'); + } + + /** + * Assert that attribute with input type "text" will be deleted + * after dispatch delete product attribute action. + * + * @magentoDataFixture Magento/Catalog/_files/product_varchar_attribute.php + * + * @return void + */ + public function testDeleteTextAttribute(): void + { + $this->dispatchDeleteAttribute('varchar_attribute'); + $this->assertAttributeIsDeleted('varchar_attribute'); + } + + /** + * Assert that attribute with input type "boolean" will be deleted + * after dispatch delete product attribute action. + * + * @magentoDataFixture Magento/Catalog/_files/product_boolean_attribute.php + * + * @return void + */ + public function testDeleteBooleanAttribute(): void + { + $this->dispatchDeleteAttribute('boolean_attribute'); + $this->assertAttributeIsDeleted('boolean_attribute'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete/DeleteAttributeControllerErrorTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete/DeleteAttributeControllerErrorTest.php new file mode 100644 index 0000000000000..31aef9c85b9bd --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Delete/DeleteAttributeControllerErrorTest.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product\Attribute\Delete; + +use Magento\Catalog\Model\Category; +use Magento\Eav\Api\AttributeRepositoryInterface; +use Magento\Framework\App\Request\Http; +use Magento\Framework\Escaper; +use Magento\Framework\Message\MessageInterface; + +/** + * Error during delete attribute using catalog/product_attribute/delete controller action. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + */ +class DeleteAttributeControllerErrorTest extends AbstractDeleteAttributeControllerTest +{ + /** + * @var Escaper + */ + private $escaper; + + /** + * @var AttributeRepositoryInterface + */ + private $attributeRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->escaper = $this->_objectManager->get(Escaper::class); + $this->attributeRepository = $this->_objectManager->get(AttributeRepositoryInterface::class); + } + + /** + * Try to delete attribute via controller action without attribute ID. + * + * @return void + */ + public function testDispatchWithoutAttributeId(): void + { + $this->getRequest()->setMethod(Http::METHOD_POST); + $this->dispatch(sprintf($this->uri, '')); + $this->assertSessionMessages( + $this->equalTo([$this->escaper->escapeHtml((string)__('We can\'t find an attribute to delete.'))]), + MessageInterface::TYPE_ERROR + ); + } + + /** + * Try to delete category attribute via controller action. + * + * @magentoDataFixture Magento/Catalog/_files/category_attribute.php + * + * @return void + */ + public function testDispatchWithNonProductAttribute(): void + { + $categoryAttribute = $this->attributeRepository->get( + Category::ENTITY, + 'test_attribute_code_666' + ); + $this->getRequest()->setMethod(Http::METHOD_POST); + $this->dispatch(sprintf($this->uri, $categoryAttribute->getAttributeId())); + $this->assertSessionMessages( + $this->equalTo([$this->escaper->escapeHtml((string)__('We can\'t delete the attribute.'))]), + MessageInterface::TYPE_ERROR + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/AbstractSaveAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/AbstractSaveAttributeTest.php new file mode 100644 index 0000000000000..d0f1256f1fdb7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/AbstractSaveAttributeTest.php @@ -0,0 +1,217 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\InputType; + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\ProductAttributeOptionManagementInterface; +use Magento\Eav\Api\AttributeRepositoryInterface; +use Magento\Eav\Api\Data\AttributeInterface; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Escaper; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Store\Model\Store; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Base create and assert attribute data. + */ +abstract class AbstractSaveAttributeTest extends AbstractBackendController +{ + /** + * @var AttributeRepositoryInterface + */ + protected $attributeRepository; + + /** + * @var Escaper + */ + protected $escaper; + + /** + * @var Json + */ + protected $jsonSerializer; + + /** + * @var ProductAttributeOptionManagementInterface + */ + protected $productAttributeOptionManagement; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->attributeRepository = $this->_objectManager->get(AttributeRepositoryInterface::class); + $this->escaper = $this->_objectManager->get(Escaper::class); + $this->jsonSerializer = $this->_objectManager->get(Json::class); + $this->productAttributeOptionManagement = $this->_objectManager->get( + ProductAttributeOptionManagementInterface::class + ); + } + + /** + * Create attribute via save product attribute controller and assert that attribute + * created correctly. + * + * @param array $attributeData + * @param array $checkArray + * @return void + */ + protected function createAttributeUsingDataAndAssert(array $attributeData, array $checkArray): void + { + $attributeCode = $this->getAttributeCodeFromAttributeData($attributeData); + if (isset($attributeData['serialized_options_arr'])) { + $attributeData['serialized_options'] = $this->serializeOptions($attributeData['serialized_options_arr']); + } + $this->createAttributeViaController($attributeData); + $this->assertSessionMessages( + $this->equalTo([(string)__('You saved the product attribute.')]), + MessageInterface::TYPE_SUCCESS + ); + try { + $attribute = $this->attributeRepository->get(ProductAttributeInterface::ENTITY_TYPE_CODE, $attributeCode); + $this->assertAttributeData($attribute, $attributeData, $checkArray); + $this->attributeRepository->delete($attribute); + } catch (NoSuchEntityException $e) { + $this->fail("Attribute with code {$attributeCode} was not created."); + } + } + + /** + * Create attribute via save product attribute controller and assert that we have error during save process. + * + * @param array $attributeData + * @param string $errorMessage + * @return void + */ + protected function createAttributeUsingDataWithErrorAndAssert(array $attributeData, string $errorMessage): void + { + if (isset($attributeData['serialized_options_arr']) + && count($attributeData['serialized_options_arr']) + ) { + $attributeData['serialized_options'] = $this->serializeOptions($attributeData['serialized_options_arr']); + } + $this->createAttributeViaController($attributeData); + $this->assertSessionMessages( + $this->equalTo([$this->escaper->escapeHtml($errorMessage)]), + MessageInterface::TYPE_ERROR + ); + $attributeCode = $this->getAttributeCodeFromAttributeData($attributeData); + try { + $attribute = $this->attributeRepository->get(ProductAttributeInterface::ENTITY_TYPE_CODE, $attributeCode); + $this->attributeRepository->delete($attribute); + } catch (NoSuchEntityException $e) { + //Attribute already deleted. + } + } + + /** + * Assert that options was created. + * + * @param AttributeInterface $attribute + * @param array $optionsData + * @return void + */ + protected function assertAttributeOptions(AttributeInterface $attribute, array $optionsData): void + { + $attributeOptions = $this->productAttributeOptionManagement->getItems($attribute->getAttributeCode()); + foreach ($optionsData as $optionData) { + $valueItemArr = $optionData['option']['value']; + $optionLabel = reset($valueItemArr)[1]; + $optionFounded = false; + foreach ($attributeOptions as $attributeOption) { + if ($attributeOption->getLabel() === $optionLabel) { + $optionFounded = true; + break; + } + } + $this->assertTrue($optionFounded); + } + } + + /** + * Compare attribute data with data which we use for create attribute. + * + * @param AttributeInterface|AbstractAttribute $attribute + * @param array $attributeData + * @param array $checkData + * @return void + */ + private function assertAttributeData( + AttributeInterface $attribute, + array $attributeData, + array $checkData + ): void { + $frontendInput = $checkData['frontend_input'] ?? $attributeData['frontend_input']; + $this->assertEquals('Test attribute name', $attribute->getDefaultFrontendLabel()); + $this->assertEquals($frontendInput, $attribute->getFrontendInput()); + + if (isset($attributeData['serialized_options'])) { + $this->assertAttributeOptions($attribute, $attributeData['serialized_options_arr']); + } + + //Additional asserts + foreach ($checkData as $valueKey => $value) { + $this->assertEquals($value, $attribute->getDataUsingMethod($valueKey)); + } + } + + /** + * Get attribute code from attribute data. If attribute code doesn't exist in + * attribute data get attribute using default frontend label. + * + * @param array $attributeData + * @return string + */ + private function getAttributeCodeFromAttributeData(array $attributeData): string + { + $attributeCode = $attributeData['attribute_code'] ?? null; + if (!$attributeCode) { + $attributeCode = strtolower( + str_replace(' ', '_', $attributeData['frontend_label'][Store::DEFAULT_STORE_ID]) + ); + } + + return $attributeCode; + } + + /** + * Create attribute using catalog/product_attribute/save action. + * + * @param array $attributeData + * @return void + */ + private function createAttributeViaController(array $attributeData): void + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($attributeData); + $this->dispatch('backend/catalog/product_attribute/save'); + } + + /** + * Create serialized options string. + * + * @param array $optionsArr + * @return string + */ + private function serializeOptions(array $optionsArr): string + { + $resultArr = []; + + foreach ($optionsArr as $option) { + $resultArr[] = http_build_query($option); + } + + return $this->jsonSerializer->serialize($resultArr); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/MediaImageTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/MediaImageTest.php new file mode 100644 index 0000000000000..f8adac2872773 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/MediaImageTest.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\InputType; + +/** + * Test cases related to create attribute with input type media image. + * + * @magentoDbIsolation enabled + */ +class MediaImageTest extends AbstractSaveAttributeTest +{ + /** + * Test create attribute and compare attribute data and input data. + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider\MediaImage::getAttributeDataWithCheckArray() + * + * @param array $attributePostData + * @param array $checkArray + * @return void + */ + public function testCreateAttribute(array $attributePostData, array $checkArray): void + { + $this->createAttributeUsingDataAndAssert($attributePostData, $checkArray); + } + + /** + * Test create attribute with error. + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider\MediaImage::getAttributeDataWithErrorMessage() + * + * @param array $attributePostData + * @param string $errorMessage + * @return void + */ + public function testCreateAttributeWithError(array $attributePostData, string $errorMessage): void + { + $this->createAttributeUsingDataWithErrorAndAssert($attributePostData, $errorMessage); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/PriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/PriceTest.php new file mode 100644 index 0000000000000..fb71f0a4d9d76 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save/InputType/PriceTest.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product\Attribute\Save\InputType; + +/** + * Test cases related to create attribute with input type price. + * + * @magentoDbIsolation enabled + */ +class PriceTest extends AbstractSaveAttributeTest +{ + /** + * Test create attribute and compare attribute data and input data. + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider\Price::getAttributeDataWithCheckArray() + * + * @param array $attributePostData + * @param array $checkArray + * @return void + */ + public function testCreateAttribute(array $attributePostData, array $checkArray): void + { + $this->createAttributeUsingDataAndAssert($attributePostData, $checkArray); + } + + /** + * Test create attribute with error. + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Attribute\DataProvider\Price::getAttributeDataWithErrorMessage() + * + * @param array $attributePostData + * @param string $errorMessage + * @return void + */ + public function testCreateAttributeWithError(array $attributePostData, string $errorMessage): void + { + $this->createAttributeUsingDataWithErrorAndAssert($attributePostData, $errorMessage); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php index e1d3e960593a9..967208ef800ff 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php @@ -136,32 +136,6 @@ public function testAttributeWithoutId() $this->assertEquals('You saved the product attribute.', $message->getText()); } - /** - * @return void - */ - public function testWrongAttributeCode() - { - $postData = $this->_getAttributeData() + ['attribute_code' => '_()&&&?']; - $this->getRequest()->setPostValue($postData); - $this->getRequest()->setMethod(HttpRequest::METHOD_POST); - $this->dispatch('backend/catalog/product_attribute/save'); - $this->assertEquals(302, $this->getResponse()->getHttpResponseCode()); - $this->assertContains( - 'catalog/product_attribute/edit', - $this->getResponse()->getHeader('Location')->getFieldValue() - ); - /** @var \Magento\Framework\Message\Collection $messages */ - $messages = $this->_objectManager->create(\Magento\Framework\Message\ManagerInterface::class)->getMessages(); - $this->assertEquals(1, $messages->getCountByType('error')); - /** @var \Magento\Framework\Message\Error $message */ - $message = $messages->getItemsByType('error')[0]; - $this->assertEquals( - 'Attribute code "_()&&&?" is invalid. Please use only letters (a-z or A-Z),' - . ' numbers (0-9) or underscore (_) in this field, and the first character should be a letter.', - $message->getText() - ); - } - /** * @return void */ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Gallery/UploadTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Gallery/UploadTest.php index a786e7fa821b6..683fbc1a358c1 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Gallery/UploadTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Gallery/UploadTest.php @@ -154,8 +154,6 @@ public function uploadActionDataProvider(): array */ public function testUploadActionWithErrors(array $file, array $expectation): void { - $this->markTestSkipped('MC-21994'); - if (!empty($file['create_file'])) { $this->createFileInSysTmpDir($file['name']); } elseif (!empty($file['copy_file'])) { @@ -165,8 +163,8 @@ public function testUploadActionWithErrors(array $file, array $expectation): voi $this->getRequest()->setMethod($this->httpMethod); $this->dispatch($this->uri); $jsonBody = $this->serializer->unserialize($this->getResponse()->getBody()); - $this->assertEquals($jsonBody['error'], $expectation['message']); - $this->assertEquals($jsonBody['errorcode'], $expectation['errorcode']); + $this->assertEquals($expectation['message'], $jsonBody['error']); + $this->assertEquals($expectation['errorcode'], $jsonBody['errorcode']); if (!empty($expectation['tmp_media_path'])) { $this->assertFileNotExists( diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/AdvancedPricingTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/AdvancedPricingTest.php new file mode 100644 index 0000000000000..da4cf6335fb05 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/AdvancedPricingTest.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product\Save; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Customer\Api\Data\GroupInterface; +use Magento\Framework\App\Request\Http; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Test cases for set advanced price to product. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + */ +class AdvancedPricingTest extends AbstractBackendController +{ + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + } + + /** + * Assert that special price correctly saved to product. + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @return void + */ + public function testAddSpecialPriceToProduct(): void + { + $product = $this->productRepository->get('simple'); + $postData = [ + 'product' => [ + 'special_price' => 8, + ], + ]; + $this->assertNull($product->getSpecialPrice()); + $this->dispatchWithData((int)$product->getEntityId(), $postData); + $product = $this->productRepository->get('simple', false, null, true); + $this->assertEquals(8, $product->getSpecialPrice()); + } + + /** + * Assert that tier price correctly saved to product. + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @return void + */ + public function testAddTierPriceToProduct(): void + { + $product = $this->productRepository->get('simple'); + $postData = [ + 'product' => [ + 'tier_price' => [ + [ + 'website_id' => '0', + 'cust_group' => GroupInterface::CUST_GROUP_ALL, + 'price_qty' => '100', + 'price' => 5, + 'value_type' => 'fixed', + ] + ], + ], + ]; + $this->assertEquals(10, $product->getTierPrice(100)); + $this->dispatchWithData((int)$product->getEntityId(), $postData); + $product = $this->productRepository->get('simple', false, null, true); + $this->assertEquals(5, $product->getTierPrice(100)); + } + + /** + * Dispatch product save with data. + * + * @param int $productId + * @param array $productPostData + * @return void + */ + private function dispatchWithData(int $productId, array $productPostData): void + { + $this->getRequest()->setPostValue($productPostData); + $this->getRequest()->setMethod(Http::METHOD_POST); + $this->dispatch('backend/catalog/product/save/id/' . $productId); + $this->assertSessionMessages( + $this->contains('You saved the product.'), + MessageInterface::TYPE_SUCCESS + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php index 80f15da647b25..f979bad9d0f76 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php @@ -51,6 +51,8 @@ protected function setUp() * @dataProvider productWithNewOptionsDataProvider * * @param array $productPostData + * + * @magentoDbIsolation enabled */ public function testSaveCustomOptionWithTypeField(array $productPostData): void { diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/DeleteCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/DeleteCustomOptionsTest.php new file mode 100644 index 0000000000000..f1af6e6e41cff --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/DeleteCustomOptionsTest.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product\Save; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory; +use Magento\Catalog\Api\ProductCustomOptionRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Base test cases for delete product custom option with type "field". + * Option deleting via product controller action save. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + */ +class DeleteCustomOptionsTest extends AbstractBackendController +{ + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var ProductCustomOptionRepositoryInterface + */ + private $optionRepository; + + /** + * @var ProductCustomOptionInterfaceFactory + */ + private $optionRepositoryFactory; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + $this->optionRepository = $this->_objectManager->get(ProductCustomOptionRepositoryInterface::class); + $this->optionRepositoryFactory = $this->_objectManager->get(ProductCustomOptionInterfaceFactory::class); + } + + /** + * Test delete custom option with type "field". + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\Field::getDataForCreateOptions + * + * @param array $optionData + * @return void + */ + public function testDeleteCustomOptionWithTypeField(array $optionData): void + { + $product = $this->productRepository->get('simple'); + /** @var ProductCustomOptionInterface $option */ + $option = $this->optionRepositoryFactory->create(['data' => $optionData]); + $option->setProductSku($product->getSku()); + $product->setOptions([$option]); + $this->productRepository->save($product); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('backend/catalog/product/save/id/' . $product->getEntityId()); + $this->assertSessionMessages( + $this->equalTo([(string)__('You saved the product.')]), + MessageInterface::TYPE_SUCCESS + ); + $this->assertCount(0, $this->optionRepository->getProductOptions($product)); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/LinksTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/LinksTest.php new file mode 100644 index 0000000000000..665d45921d435 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/LinksTest.php @@ -0,0 +1,144 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product\Save; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Saving product with linked products + * + * @magentoAppArea adminhtml + */ +class LinksTest extends AbstractBackendController +{ + /** @var array */ + private $linkTypes = [ + 'upsell', + 'crosssell', + 'related', + ]; + + /** @var ProductRepositoryInterface $productRepository */ + private $productRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->productRepository = $this->_objectManager->create(ProductRepositoryInterface::class); + } + + /** + * Test add simple related, up-sells, cross-sells product + * + * @magentoDataFixture Magento/Catalog/_files/multiple_products.php + * @magentoDbIsolation enabled + * @return void + */ + public function testAddRelatedUpSellCrossSellProducts(): void + { + $postData = $this->getPostData(); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($postData); + $this->dispatch('backend/catalog/product/save'); + $this->assertSessionMessages( + $this->equalTo(['You saved the product.']), + MessageInterface::TYPE_SUCCESS + ); + $product = $this->productRepository->get('simple'); + $this->assertEquals( + $this->getExpectedLinks($postData['links']), + $this->getActualLinks($product), + "Expected linked products do not match actual linked products!" + ); + } + + /** + * Get post data for the request + * + * @return array + */ + private function getPostData(): array + { + return [ + 'product' => [ + 'attribute_set_id' => '4', + 'status' => '1', + 'name' => 'Simple Product', + 'sku' => 'simple', + 'url_key' => 'simple-product', + 'type_id' => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE + ], + 'links' => [ + 'upsell' => [ + ['id' => '10'], + ], + 'crosssell' => [ + ['id' => '11'], + ], + 'related' => [ + ['id' => '12'], + ], + ] + ]; + } + + /** + * Set an array of expected related, up-sells, cross-sells product identifiers + * + * @param array $links + * @return array + */ + private function getExpectedLinks(array $links): array + { + $expectedLinks = []; + foreach ($this->linkTypes as $linkType) { + $expectedLinks[$linkType] = []; + foreach ($links[$linkType] as $productData) { + $expectedLinks[$linkType][] = $productData['id']; + } + } + + return $expectedLinks; + } + + /** + * Get an array of received related, up-sells, cross-sells products + * + * @param ProductInterface|Product $product + * @return array + */ + private function getActualLinks(ProductInterface $product): array + { + $actualLinks = []; + foreach ($this->linkTypes as $linkType) { + $ids = []; + switch ($linkType) { + case 'upsell': + $ids = $product->getUpSellProductIds(); + break; + case 'crosssell': + $ids = $product->getCrossSellProductIds(); + break; + case 'related': + $ids = $product->getRelatedProductIds(); + break; + } + $actualLinks[$linkType] = $ids; + } + + return $actualLinks; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/UpdateCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/UpdateCustomOptionsTest.php new file mode 100644 index 0000000000000..a45c21444a5d7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/UpdateCustomOptionsTest.php @@ -0,0 +1,130 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product\Save; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory; +use Magento\Catalog\Api\ProductCustomOptionRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Option; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Base test cases for update product custom options with type "field". + * Option updating via dispatch product controller action save with updated options data in POST data. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + */ +class UpdateCustomOptionsTest extends AbstractBackendController +{ + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var ProductCustomOptionRepositoryInterface + */ + private $optionRepository; + + /** + * @var ProductCustomOptionInterfaceFactory + */ + private $optionRepositoryFactory; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + $this->optionRepository = $this->_objectManager->get(ProductCustomOptionRepositoryInterface::class); + $this->optionRepositoryFactory = $this->_objectManager->get(ProductCustomOptionInterfaceFactory::class); + } + + /** + * Test add to product custom option with type "field". + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\Field::getDataForUpdateOptions + * + * @param array $optionData + * @param array $updateData + * @return void + */ + public function testUpdateCustomOptionWithTypeField(array $optionData, array $updateData): void + { + $product = $this->productRepository->get('simple'); + /** @var ProductCustomOptionInterface|Option $option */ + $option = $this->optionRepositoryFactory->create(['data' => $optionData]); + $option->setProductSku($product->getSku()); + $product->setOptions([$option]); + $this->productRepository->save($product); + $currentProductOptions = $this->optionRepository->getProductOptions($product); + $this->assertCount(1, $currentProductOptions); + /** @var ProductCustomOptionInterface $currentOption */ + $currentOption = reset($currentProductOptions); + $postData = [ + 'product' => [ + 'options' => [ + [ + 'option_id' => $currentOption->getOptionId(), + 'product_id' => $product->getId(), + 'type' => $currentOption->getType(), + 'is_require' => $currentOption->getIsRequire(), + 'sku' => $currentOption->getSku(), + 'max_characters' => $currentOption->getMaxCharacters(), + 'title' => $currentOption->getTitle(), + 'sort_order' => $currentOption->getSortOrder(), + 'price' => $currentOption->getPrice(), + 'price_type' => $currentOption->getPriceType(), + 'is_use_default' => false, + ], + ], + ], + ]; + + foreach ($updateData as $methodKey => $newValue) { + $postData = array_replace_recursive( + $postData, + [ + 'product' => [ + 'options' => [ + 0 => [ + $methodKey => $newValue, + ], + ], + ], + ] + ); + $this->getRequest()->setPostValue($postData); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('backend/catalog/product/save/id/' . $product->getEntityId()); + $this->assertSessionMessages( + $this->contains('You saved the product.'), + MessageInterface::TYPE_SUCCESS + ); + $updatedOptions = $this->optionRepository->getProductOptions($product); + $this->assertCount(1, $updatedOptions); + /** @var ProductCustomOptionInterface|Option $updatedOption */ + $updatedOption = reset($updatedOptions); + $this->assertEquals($newValue, $updatedOption->getDataUsingMethod($methodKey)); + $this->assertEquals($option->getOptionId(), $updatedOption->getOptionId()); + $this->assertNotEquals( + $option->getDataUsingMethod($methodKey), + $updatedOption->getDataUsingMethod($methodKey) + ); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/DeleteTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/DeleteTest.php index 7e034b8b3cb7e..5cb1f862054ba 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/DeleteTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/DeleteTest.php @@ -3,42 +3,146 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Controller\Adminhtml\Product\Set; -use Magento\Framework\Message\MessageInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Eav\Api\AttributeSetRepositoryInterface; use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Escaper; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\Eav\Model\GetAttributeSetByName; +use Magento\TestFramework\TestCase\AbstractBackendController; -class DeleteTest extends \Magento\TestFramework\TestCase\AbstractBackendController +/** + * Test for attribute set deleting. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + */ +class DeleteTest extends AbstractBackendController { /** + * @var GetAttributeSetByName + */ + private $getAttributeSetByName; + + /** + * @var ProductInterface|Product + */ + private $product; + + /** + * @var AttributeSetRepositoryInterface + */ + private $attributeSetRepository; + + /** + * @var Escaper + */ + private $escaper; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->getAttributeSetByName = $this->_objectManager->get(GetAttributeSetByName::class); + $this->product = $this->_objectManager->get(ProductInterface::class); + $this->attributeSetRepository = $this->_objectManager->get(AttributeSetRepositoryInterface::class); + $this->escaper = $this->_objectManager->get(Escaper::class); + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + } + + /** + * Assert that default attribute set is not deleted. + * + * @return void + */ + public function testDefaultAttributeSetIsNotDeleted(): void + { + $productDefaultAttrSetId = (int)$this->product->getDefaultAttributeSetId(); + $this->performDeleteAttributeSetRequest($productDefaultAttrSetId); + $expectedSessionMessage = $this->escaper->escapeHtml((string)__('We can\'t delete this set right now.')); + $this->assertSessionMessages( + $this->equalTo([$expectedSessionMessage]), + MessageInterface::TYPE_ERROR + ); + try { + $this->attributeSetRepository->get($productDefaultAttrSetId); + } catch (NoSuchEntityException $e) { + $this->fail(sprintf('Default attribute set was deleted. Message: %s', $e->getMessage())); + } + } + + /** + * Assert that custom attribute set deleting properly. + * * @magentoDataFixture Magento/Eav/_files/empty_attribute_set.php + * + * @return void */ - public function testDeleteById() + public function testDeleteCustomAttributeSetById(): void { - $attributeSet = $this->getAttributeSetByName('empty_attribute_set'); - $this->getRequest()->setParam('id', $attributeSet->getId())->setMethod(HttpRequest::METHOD_POST); + $this->deleteAttributeSetByNameAndAssert('empty_attribute_set'); + } - $this->dispatch('backend/catalog/product_set/delete/'); + /** + * Assert that product will be deleted if delete attribute set which the product is attached. + * + * @magentoDataFixture Magento/Catalog/_files/product_with_test_attribute_set.php + * + * @return void + */ + public function testProductIsDeletedAfterDeleteItsAttributeSet(): void + { + $this->deleteAttributeSetByNameAndAssert('new_attribute_set'); + $this->expectExceptionObject( + new NoSuchEntityException( + __('The product that was requested doesn\'t exist. Verify the product and try again.') + ) + ); + $this->productRepository->get('simple'); + } - $this->assertNull($this->getAttributeSetByName('empty_attribute_set')); + /** + * Perform request to delete attribute set and assert that attribute set is deleted. + * + * @param string $attributeSetName + * @return void + */ + private function deleteAttributeSetByNameAndAssert(string $attributeSetName): void + { + $attributeSet = $this->getAttributeSetByName->execute($attributeSetName); + $this->performDeleteAttributeSetRequest((int)$attributeSet->getAttributeSetId()); $this->assertSessionMessages( - $this->equalTo(['The attribute set has been removed.']), + $this->equalTo([(string)__('The attribute set has been removed.')]), MessageInterface::TYPE_SUCCESS ); - $this->assertRedirect($this->stringContains('catalog/product_set/index/')); + $this->assertNull($this->getAttributeSetByName->execute($attributeSetName)); } /** - * Retrieve attribute set based on given name. + * Perform "catalog/product_set/delete" controller dispatch. * - * @param string $attributeSetName - * @return \Magento\Eav\Model\Entity\Attribute\Set|null + * @param int $attributeSetId + * @return void */ - protected function getAttributeSetByName($attributeSetName) + private function performDeleteAttributeSetRequest(int $attributeSetId): void { - $attributeSet = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Eav\Model\Entity\Attribute\Set::class - )->load($attributeSetName, 'attribute_set_name'); - return $attributeSet->getId() === null ? null : $attributeSet; + $this->getRequest() + ->setParam('id', $attributeSetId) + ->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('backend/catalog/product_set/delete/'); } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/SaveTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/SaveTest.php index 187fddae1ce4f..1edd494dabbe3 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/SaveTest.php @@ -7,26 +7,32 @@ namespace Magento\Catalog\Controller\Adminhtml\Product\Set; +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Repository; +use Magento\Developer\Model\Logger\Handler\Syslog; +use Magento\Eav\Api\AttributeManagementInterface; use Magento\Eav\Api\AttributeSetRepositoryInterface; use Magento\Eav\Api\Data\AttributeSetInterface; +use Magento\Eav\Model\Config; +use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\TestFramework\Helper\Bootstrap; use Magento\Framework\App\Request\Http as HttpRequest; -use Magento\Eav\Api\AttributeManagementInterface; -use Magento\Catalog\Api\Data\ProductInterfaceFactory; -use Magento\Framework\Api\DataObjectHelper; -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Catalog\Api\Data\ProductInterface; -use Magento\Developer\Model\Logger\Handler\Syslog; +use Magento\Framework\Logger\Handler\System; use Magento\Framework\Logger\Monolog; -use Magento\Catalog\Model\Product\Attribute\Repository; +use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\AbstractBackendController; /** - * Test save attribute set + * Testing for saving an existing or creating a new attribute set. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @magentoAppArea adminhtml */ -class SaveTest extends \Magento\TestFramework\TestCase\AbstractBackendController +class SaveTest extends AbstractBackendController { /** * @var string @@ -63,6 +69,21 @@ class SaveTest extends \Magento\TestFramework\TestCase\AbstractBackendController */ private $attributeRepository; + /** + * @var AttributeSetRepositoryInterface + */ + private $attributeSetRepository; + + /** + * @var Config + */ + private $eavConfig; + + /** + * @var Json + */ + private $json; + /** * @inheritDoc */ @@ -80,11 +101,13 @@ public function setUp() $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); $this->attributeRepository = $this->_objectManager->get(Repository::class); $this->dataObjectHelper = $this->_objectManager->get(DataObjectHelper::class); + $this->attributeSetRepository = $this->_objectManager->get(AttributeSetRepositoryInterface::class); + $this->eavConfig = $this->_objectManager->get(Config::class); + $this->json = $this->_objectManager->get(Json::class); } /** * @inheritdoc - * @throws \Magento\Framework\Exception\FileSystemException */ public function tearDown() { @@ -93,9 +116,68 @@ public function tearDown() } /** + * Test that new attribute set based on default attribute set will be successfully created. + * + * @magentoDbIsolation enabled + * + * @return void + */ + public function testCreateNewAttributeSetBasedOnDefaultAttributeSet(): void + { + $this->createAttributeSetBySkeletonAndAssert( + 'Attribute set name for test', + $this->getCatalogProductDefaultAttributeSetId() + ); + } + + /** + * Test that new attribute set based on custom attribute set will be successfully created. + * * @magentoDataFixture Magento/Catalog/_files/attribute_set_with_renamed_group.php + * + * @magentoDbIsolation enabled + * + * @return void + */ + public function testCreateNewAttributeSetBasedOnCustomAttributeSet(): void + { + $existCustomAttributeSet = $this->getAttributeSetByName('attribute_set_test'); + $this->createAttributeSetBySkeletonAndAssert( + 'Attribute set name for test', + (int)$existCustomAttributeSet->getAttributeSetId() + ); + } + + /** + * Test that new attribute set based on custom attribute set will be successfully created. + * + * @magentoDbIsolation enabled + * + * @return void + */ + public function testGotErrorDuringCreateAttributeSetWithoutName(): void + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue( + [ + 'gotoEdit' => '1', + 'skeleton_set' => $this->getCatalogProductDefaultAttributeSetId(), + ] + ); + $this->dispatch('backend/catalog/product_set/save/'); + $this->assertSessionMessages( + $this->equalTo([(string)__('The attribute set name is empty. Enter the name and try again.')]), + MessageInterface::TYPE_ERROR + ); + } + + /** + * Test that exception throws during save attribute set name process if name of attribute set already exists. + * + * @magentoDataFixture Magento/Catalog/_files/attribute_set_with_renamed_group.php + * @return void */ - public function testAlreadyExistsExceptionProcessingWhenGroupCodeIsDuplicated() + public function testAlreadyExistsExceptionProcessingWhenGroupCodeIsDuplicated(): void { $attributeSet = $this->getAttributeSetByName('attribute_set_test'); $this->assertNotEmpty($attributeSet, 'Attribute set with name "attribute_set_test" is missed'); @@ -103,7 +185,7 @@ public function testAlreadyExistsExceptionProcessingWhenGroupCodeIsDuplicated() $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setPostValue( 'data', - json_encode( + $this->json->serialize( [ 'attribute_set_name' => 'attribute_set_test', 'groups' => [ @@ -119,44 +201,25 @@ public function testAlreadyExistsExceptionProcessingWhenGroupCodeIsDuplicated() ); $this->dispatch('backend/catalog/product_set/save/id/' . $attributeSet->getAttributeSetId()); - $jsonResponse = json_decode($this->getResponse()->getBody()); + $jsonResponse = $this->json->unserialize($this->getResponse()->getBody()); $this->assertNotNull($jsonResponse); - $this->assertEquals(1, $jsonResponse->error); + $this->assertEquals(1, $jsonResponse['error']); $this->assertContains( - 'Attribute group with same code already exist. Please rename "attribute-group-name" group', - $jsonResponse->message + (string)__('Attribute group with same code already exist. Please rename "attribute-group-name" group'), + $jsonResponse['message'] ); } - /** - * @param string $attributeSetName - * @return AttributeSetInterface|null - */ - protected function getAttributeSetByName($attributeSetName) - { - $objectManager = Bootstrap::getObjectManager(); - - /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); - $searchCriteriaBuilder->addFilter('attribute_set_name', $attributeSetName); - - /** @var AttributeSetRepositoryInterface $attributeSetRepository */ - $attributeSetRepository = $objectManager->get(AttributeSetRepositoryInterface::class); - $result = $attributeSetRepository->getList($searchCriteriaBuilder->create()); - - $items = $result->getItems(); - return $result->getTotalCount() ? array_pop($items) : null; - } - /** * Test behavior when attribute set was changed to a new set - * with deleted attribute from the previous set + * with deleted attribute from the previous set. * * @magentoDataFixture Magento/Catalog/_files/product_simple.php * @magentoDataFixture Magento/Catalog/_files/attribute_set_based_on_default.php * @magentoDbIsolation disabled + * @return void */ - public function testRemoveAttributeFromAttributeSet() + public function testRemoveAttributeFromAttributeSet(): void { $message = 'Attempt to load value of nonexistent EAV attribute'; $this->removeSyslog(); @@ -178,7 +241,7 @@ public function testRemoveAttributeFromAttributeSet() } /** - * Retrieve system.log file path + * Retrieve system.log file path. * * @return string */ @@ -186,7 +249,7 @@ private function getSyslogPath(): string { if (!$this->systemLogPath) { foreach ($this->logger->getHandlers() as $handler) { - if ($handler instanceof \Magento\Framework\Logger\Handler\System) { + if ($handler instanceof System) { $this->systemLogPath = $handler->getUrl(); } } @@ -200,11 +263,103 @@ private function getSyslogPath(): string * * @return void */ - private function removeSyslog() + private function removeSyslog(): void { $this->syslogHandler->close(); if (file_exists($this->getSyslogPath())) { unlink($this->getSyslogPath()); } } + + /** + * Search and return attribute set by name. + * + * @param string $attributeSetName + * @return AttributeSetInterface|null + */ + private function getAttributeSetByName(string $attributeSetName): ?AttributeSetInterface + { + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $this->_objectManager->get(SearchCriteriaBuilder::class); + $searchCriteriaBuilder->addFilter('attribute_set_name', $attributeSetName); + $result = $this->attributeSetRepository->getList($searchCriteriaBuilder->create()); + + $items = $result->getItems(); + + return array_pop($items); + } + + /** + * Create attribute set by skeleton attribute set id and assert that attribute set + * created successfully and attributes from skeleton attribute set and created attribute set are equals. + * + * @param string $attributeSetName + * @param int $skeletonAttributeSetId + * @return void + */ + private function createAttributeSetBySkeletonAndAssert( + string $attributeSetName, + int $skeletonAttributeSetId + ): void { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue( + [ + 'attribute_set_name' => $attributeSetName, + 'gotoEdit' => '1', + 'skeleton_set' => $skeletonAttributeSetId, + ] + ); + $this->dispatch('backend/catalog/product_set/save/'); + $this->assertSessionMessages( + $this->equalTo([(string)__('You saved the attribute set.')]), + MessageInterface::TYPE_SUCCESS + ); + $createdAttributeSet = $this->getAttributeSetByName($attributeSetName); + $existAttributeSet = $this->attributeSetRepository->get($skeletonAttributeSetId); + + $this->assertNotNull($createdAttributeSet); + $this->assertEquals($attributeSetName, $createdAttributeSet->getAttributeSetName()); + + $this->assertAttributeSetsAttributesAreEquals($createdAttributeSet, $existAttributeSet); + } + + /** + * Assert that both attribute sets contains identical attributes by attribute ids. + * + * @param AttributeSetInterface $createdAttributeSet + * @param AttributeSetInterface $existAttributeSet + * @return void + */ + private function assertAttributeSetsAttributesAreEquals( + AttributeSetInterface $createdAttributeSet, + AttributeSetInterface $existAttributeSet + ): void { + $expectedAttributeIds = array_keys( + $this->attributeManagement->getAttributes( + ProductAttributeInterface::ENTITY_TYPE_CODE, + $existAttributeSet->getAttributeSetId() + ) + ); + sort($expectedAttributeIds); + $actualAttributeIds = array_keys( + $this->attributeManagement->getAttributes( + ProductAttributeInterface::ENTITY_TYPE_CODE, + $createdAttributeSet->getAttributeSetId() + ) + ); + sort($actualAttributeIds); + $this->assertSame($expectedAttributeIds, $actualAttributeIds); + } + + /** + * Retrieve default catalog product attribute set ID. + * + * @return int + */ + private function getCatalogProductDefaultAttributeSetId(): int + { + return (int)$this->eavConfig + ->getEntityType(ProductAttributeInterface::ENTITY_TYPE_CODE) + ->getDefaultAttributeSetId(); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/UpdateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/UpdateTest.php new file mode 100644 index 0000000000000..765f59b15be83 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/UpdateTest.php @@ -0,0 +1,238 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Adminhtml\Product\Set; + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Eav\Api\AttributeManagementInterface; +use Magento\Eav\Api\AttributeSetRepositoryInterface; +use Magento\Eav\Api\Data\AttributeGroupInterface; +use Magento\Eav\Api\Data\AttributeSetInterface; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Group\Collection; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Group\CollectionFactory; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\TestFramework\Eav\Model\GetAttributeSetByName; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Test update attribute set. + */ +class UpdateTest extends AbstractBackendController +{ + /** + * @var Json + */ + private $json; + + /** + * @var AttributeSetRepositoryInterface + */ + private $attributeSetRepository; + + /** + * @var AttributeManagementInterface + */ + private $attributeManagement; + + /** + * @var CollectionFactory + */ + private $attributeGroupCollectionFactory; + + /** + * @var GetAttributeSetByName + */ + private $getAttributeSetByName; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->json = $this->_objectManager->get(Json::class); + $this->attributeSetRepository = $this->_objectManager->get(AttributeSetRepositoryInterface::class); + $this->attributeManagement = $this->_objectManager->get(AttributeManagementInterface::class); + $this->attributeGroupCollectionFactory = $this->_objectManager->get(CollectionFactory::class); + $this->getAttributeSetByName = $this->_objectManager->get(GetAttributeSetByName::class); + } + + /** + * Test that name of attribute set will update/change correctly. + * + * @magentoDataFixture Magento/Catalog/_files/attribute_set_based_on_default.php + * + * @magentoDbIsolation disabled + * + * @return void + */ + public function testUpdateAttributeSetName(): void + { + $attributeSet = $this->getAttributeSetByName->execute('new_attribute_set'); + $currentAttrSetName = $attributeSet->getAttributeSetName(); + $this->assertNotNull($attributeSet); + $postData = $this->prepareDataToRequest($attributeSet); + $updateName = 'New attribute set name'; + $postData['attribute_set_name'] = $updateName; + $this->performRequest((int)$attributeSet->getAttributeSetId(), $postData); + $this->assertSessionMessages( + $this->equalTo([(string)__('You saved the attribute set.')]), + MessageInterface::TYPE_SUCCESS + ); + $updatedAttributeSet = $this->attributeSetRepository->get((int)$attributeSet->getAttributeSetId()); + $this->assertEquals($updateName, $updatedAttributeSet->getAttributeSetName()); + $updatedAttributeSet->setAttributeSetName($currentAttrSetName); + $this->attributeSetRepository->save($updatedAttributeSet); + } + + /** + * Test add new group to custom attribute set. + * + * @magentoDataFixture Magento/Catalog/_files/attribute_set_based_on_default.php + * + * @magentoDbIsolation disabled + * + * @return void + */ + public function testUpdateAttributeSetWithNewGroup(): void + { + $currentAttrSet = $this->getAttributeSetByName->execute('new_attribute_set'); + $this->assertNotNull($currentAttrSet); + $attrSetId = (int)$currentAttrSet->getAttributeSetId(); + $currentAttrGroups = $this->getAttributeSetGroupCollection($attrSetId)->getItems(); + $newGroupName = 'Test attribute group name'; + $newGroupSortOrder = 11; + $postData = $this->prepareDataToRequest($currentAttrSet); + $postData['groups'][] = [ + null, + $newGroupName, + $newGroupSortOrder, + ]; + $this->performRequest($attrSetId, $postData); + $this->assertSessionMessages( + $this->equalTo([(string)__('You saved the attribute set.')]), + MessageInterface::TYPE_SUCCESS + ); + $updatedAttrGroups = $this->getAttributeSetGroupCollection($attrSetId)->getItems(); + $diffGroups = array_diff_key($updatedAttrGroups, $currentAttrGroups); + $this->assertCount(1, $diffGroups); + /** @var AttributeGroupInterface $newGroup */ + $newGroup = reset($diffGroups); + $this->assertEquals($newGroupName, $newGroup->getAttributeGroupName()); + $this->assertEquals($newGroupSortOrder, $newGroup->getSortOrder()); + } + + /** + * Test delete custom group from custom attribute set. + * + * @magentoDataFixture Magento/Catalog/_files/attribute_set_based_on_default_with_custom_group.php + * + * @magentoDbIsolation disabled + * + * @return void + */ + public function testDeleteCustomGroupFromCustomAttributeSet(): void + { + $testGroupName = 'Test attribute group name'; + $currentAttrSet = $this->getAttributeSetByName->execute('new_attribute_set'); + $this->assertNotNull($currentAttrSet); + $attrSetId = (int)$currentAttrSet->getAttributeSetId(); + $currentAttrGroupsCollection = $this->getAttributeSetGroupCollection($attrSetId); + $customGroup = $currentAttrGroupsCollection->getItemByColumnValue( + AttributeGroupInterface::GROUP_NAME, + $testGroupName + ); + $this->assertNotNull($customGroup); + $postData = $this->prepareDataToRequest($currentAttrSet); + $postData['removeGroups'] = [ + $customGroup->getAttributeGroupId() + ]; + $this->performRequest($attrSetId, $postData); + $this->assertSessionMessages( + $this->equalTo([(string)__('You saved the attribute set.')]), + MessageInterface::TYPE_SUCCESS + ); + $updatedAttrGroups = $this->getAttributeSetGroupCollection($attrSetId)->getItems(); + $diffGroups = array_diff_key($currentAttrGroupsCollection->getItems(), $updatedAttrGroups); + $this->assertCount(1, $diffGroups); + /** @var AttributeGroupInterface $deletedGroup */ + $deletedGroup = reset($diffGroups); + $this->assertEquals($testGroupName, $deletedGroup->getAttributeGroupName()); + } + + /** + * Process attribute set save request. + * + * @param int $attributeSetId + * @param array $postData + * @return void + */ + private function performRequest(int $attributeSetId, array $postData = []): void + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue( + 'data', + $this->json->serialize($postData) + ); + $this->dispatch('backend/catalog/product_set/save/id/' . $attributeSetId); + } + + /** + * Prepare default data to request from attribute set. + * + * @param AttributeSetInterface $attributeSet + * @return array + */ + private function prepareDataToRequest(AttributeSetInterface $attributeSet): array + { + $result = [ + 'attribute_set_name' => $attributeSet->getAttributeSetName(), + 'removeGroups' => [], + 'not_attributes' => [], + ]; + $groups = $attributes = []; + /** @var AttributeGroupInterface $group */ + foreach ($this->getAttributeSetGroupCollection((int)$attributeSet->getAttributeSetId()) as $group) { + $groups[] = [ + $group->getAttributeGroupId(), + $group->getAttributeGroupName(), + $group->getSortOrder(), + ]; + } + $attributeSetAttributes = $this->attributeManagement->getAttributes( + ProductAttributeInterface::ENTITY_TYPE_CODE, + $attributeSet->getAttributeSetId() + ); + foreach ($attributeSetAttributes as $attribute) { + $attributes[] = [ + $attribute->getAttributeId(), + $attribute->getAttributeGroupId(), + $attribute->getSortOrder(), + ]; + } + $result['groups'] = $groups; + $result['attributes'] = $attributes; + + return $result; + } + + /** + * Build attribute set groups collection by attribute set id. + * + * @param int $attributeSetId + * @return Collection + */ + private function getAttributeSetGroupCollection(int $attributeSetId): Collection + { + $groupCollection = $this->attributeGroupCollectionFactory->create(); + $groupCollection->setAttributeSetFilter($attributeSetId); + + return $groupCollection; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php index 3a28801b1ace1..7ca04863f58a1 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php @@ -7,11 +7,17 @@ namespace Magento\Catalog\Controller\Adminhtml; +use Magento\Catalog\Model\Product\Attribute\Backend\LayoutUpdate; +use Magento\Framework\Acl\Builder; use Magento\Framework\App\Request\DataPersistorInterface; use Magento\Framework\Message\Manager; use Magento\Framework\App\Request\Http as HttpRequest; -use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ProductRepository; +use Magento\Catalog\Model\ProductRepositoryFactory; use Magento\Framework\Message\MessageInterface; +use Magento\TestFramework\Catalog\Model\ProductLayoutUpdateManager; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; use Magento\Catalog\Model\Product; use Magento\TestFramework\Helper\CacheCleaner; @@ -19,9 +25,37 @@ * Test class for Product adminhtml actions * * @magentoAppArea adminhtml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ProductTest extends \Magento\TestFramework\TestCase\AbstractBackendController { + /** + * @var Builder + */ + private $aclBuilder; + + /** + * @var ProductRepositoryFactory + */ + private $repositoryFactory; + + /** + * @var ProductResource + */ + private $resourceModel; + + /** + * @inheritDoc + */ + protected function setUp() + { + parent::setUp(); + + $this->aclBuilder = Bootstrap::getObjectManager()->get(Builder::class); + $this->repositoryFactory = Bootstrap::getObjectManager()->get(ProductRepositoryFactory::class); + $this->resourceModel = Bootstrap::getObjectManager()->get(ProductResource::class); + } + /** * Test calling save with invalid product's ID. */ @@ -45,7 +79,8 @@ public function testSaveActionWithDangerRequest() public function testSaveActionAndNew() { $this->getRequest()->setPostValue(['back' => 'new']); - $repository = $this->_objectManager->create(\Magento\Catalog\Model\ProductRepository::class); + /** @var ProductRepository $repository */ + $repository = $this->repositoryFactory->create(); $product = $repository->get('simple'); $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/catalog/product/save/id/' . $product->getEntityId()); @@ -64,8 +99,9 @@ public function testSaveActionAndNew() */ public function testSaveActionAndDuplicate() { - $repository = $this->_objectManager->create(\Magento\Catalog\Model\ProductRepository::class); - /** @var Product $product */ + $this->getRequest()->setPostValue(['back' => 'duplicate']); + /** @var ProductRepository $repository */ + $repository = $this->repositoryFactory->create(); $product = $repository->get('simple'); $this->assertSaveAndDuplicateAction($product); $this->assertRedirect($this->stringStartsWith('http://localhost/index.php/backend/catalog/product/edit/')); @@ -85,7 +121,8 @@ public function testSaveActionAndDuplicate() */ public function testSaveActionAndDuplicateWithUrlPathAttribute() { - $repository = $this->_objectManager->create(\Magento\Catalog\Model\ProductRepository::class); + /** @var ProductRepository $repository */ + $repository = $this->repositoryFactory->create(); /** @var Product $product */ $product = $repository->get('simple'); @@ -151,7 +188,8 @@ public function testIndexAction() */ public function testEditAction() { - $repository = $this->_objectManager->create(\Magento\Catalog\Model\ProductRepository::class); + /** @var ProductRepository $repository */ + $repository = $this->repositoryFactory->create(); $product = $repository->get('simple'); $this->dispatch('backend/catalog/product/edit/id/' . $product->getEntityId()); $body = $this->getResponse()->getBody(); @@ -370,13 +408,181 @@ public function saveActionTierPriceDataProvider() */ private function getProductData(array $tierPrice) { - $productRepositoryInterface = $this->_objectManager->get(ProductRepositoryInterface::class); - $product = $productRepositoryInterface->get('tier_prices')->getData(); + /** @var ProductRepository $repo */ + $repo = $this->repositoryFactory->create(); + $product = $repo->get('tier_prices')->getData(); $product['tier_price'] = $tierPrice; unset($product['entity_id']); return $product; } + /** + * Check whether additional authorization is required for the design fields. + * + * @magentoDbIsolation enabled + * @throws \Throwable + * @return void + */ + public function testSaveDesign(): void + { + $requestData = [ + 'product' => [ + 'type' => 'simple', + 'sku' => 'simple', + 'store' => '0', + 'set' => '4', + 'back' => 'edit', + 'type_id' => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE, + 'product' => [], + 'is_downloadable' => '0', + 'affect_configurable_product_attributes' => '1', + 'new_variation_attribute_set_id' => '4', + 'use_default' => [ + 'gift_message_available' => '0', + 'gift_wrapping_available' => '0' + ], + 'configurable_matrix_serialized' => '[]', + 'associated_product_ids_serialized' => '[]' + ] + ]; + $uri = 'backend/catalog/product/save'; + + //Trying to update product's design settings without proper permissions. + //Expected list of sessions messages collected throughout the controller calls. + $sessionMessages = ['Not allowed to edit the product\'s design attributes']; + $this->aclBuilder->getAcl()->deny(null, 'Magento_Catalog::edit_product_design'); + $requestData['product']['custom_design'] = '1'; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->dispatch($uri); + $this->assertSessionMessages( + self::equalTo($sessionMessages), + MessageInterface::TYPE_ERROR + ); + + //Trying again with the permissions. + $this->aclBuilder->getAcl()->allow(null, ['Magento_Catalog::products', 'Magento_Catalog::edit_product_design']); + $this->getRequest()->setDispatched(false); + $this->dispatch($uri); + /** @var ProductRepository $repo */ + $repo = $this->repositoryFactory->create(); + $product = $repo->get('simple'); + $this->assertNotEmpty($product->getCustomDesign()); + $this->assertEquals(1, $product->getCustomDesign()); + //No new error messages + $this->assertSessionMessages( + self::equalTo($sessionMessages), + MessageInterface::TYPE_ERROR + ); + } + + /** + * Save design without the permissions but with default values. + * + * @magentoDbIsolation enabled + * @throws \Throwable + * @return void + */ + public function testSaveDesignWithDefaults(): void + { + $optionsContainerDefault = $this->resourceModel->getAttribute('options_container')->getDefaultValue(); + $requestData = [ + 'product' => [ + 'type' => 'simple', + 'sku' => 'simple', + 'store' => '0', + 'set' => '4', + 'back' => 'edit', + 'product' => [], + 'type_id' => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE, + 'is_downloadable' => '0', + 'affect_configurable_product_attributes' => '1', + 'new_variation_attribute_set_id' => '4', + 'use_default' => [ + 'gift_message_available' => '0', + 'gift_wrapping_available' => '0' + ], + 'configurable_matrix_serialized' => '[]', + 'associated_product_ids_serialized' => '[]', + 'options_container' => $optionsContainerDefault + ] + ]; + $uri = 'backend/catalog/product/save'; + + //Updating product's design settings without proper permissions. + $this->aclBuilder->getAcl()->deny(null, 'Magento_Catalog::edit_product_design'); + //Testing that special "No Update" value is treated as no change. + $requestData['product']['custom_layout_update_file'] = LayoutUpdate::VALUE_NO_UPDATE; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->dispatch($uri); + + //Validating saved entity. + /** @var ProductRepository $repo */ + $repo = $this->repositoryFactory->create(); + $product = $repo->get('simple'); + $this->assertNotNull($product->getData('options_container')); + $this->assertEquals($optionsContainerDefault, $product->getData('options_container')); + } + + /** + * Test custom update files functionality. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDbIsolation disabled + * @throws \Throwable + * @return void + */ + public function testSaveCustomLayout(): void + { + $file = 'test_file'; + /** @var ProductRepository $repo */ + $repo = $this->repositoryFactory->create(); + $product = $repo->get('simple'); + /** @var ProductLayoutUpdateManager $layoutManager */ + $layoutManager = Bootstrap::getObjectManager()->get(ProductLayoutUpdateManager::class); + $layoutManager->setFakeFiles((int)$product->getId(), [$file]); + $productData = $product->getData(); + unset($productData['options']); + unset($productData[$product->getIdFieldName()]); + $requestData = [ + 'product' => $productData + ]; + $uri = 'backend/catalog/product/save'; + + //Saving a wrong file + $requestData['product']['custom_layout_update_file'] = $file . 'INVALID'; + $this->getRequest()->setDispatched(false); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->getRequest()->setParam('id', $product->getId()); + $this->dispatch($uri); + $this->assertSessionMessages( + self::equalTo(['Selected layout update is not available']), + MessageInterface::TYPE_ERROR + ); + + //Checking that the value is not saved + /** @var ProductRepository $repo */ + $repo = $this->repositoryFactory->create(); + $product = $repo->get('simple'); + $this->assertEmpty($product->getData('custom_layout_update_file')); + + //Saving the correct file + $requestData['product']['custom_layout_update_file'] = $file; + $this->getRequest()->setDispatched(false); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->getRequest()->setParam('id', $product->getId()); + $this->dispatch($uri); + + //Checking that the value is saved + /** @var ProductRepository $repo */ + $repo = $this->repositoryFactory->create(); + $product = $repo->get('simple'); + $this->assertEquals($file, $product->getData('custom_layout_update_file')); + } + /** * Dispatch Save&Duplicate action and check it * diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Category/CategoryUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Category/CategoryUrlRewriteTest.php new file mode 100644 index 0000000000000..1b51c65e1e853 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Category/CategoryUrlRewriteTest.php @@ -0,0 +1,90 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Category; + +use Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\Response\Http; +use Magento\Framework\Registry; +use Magento\Store\Model\ScopeInterface; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Checks category availability on storefront by url rewrite + * + * @magentoConfigFixture default/catalog/seo/generate_category_product_rewrites 1 + * @magentoDbIsolation enabled + */ +class CategoryUrlRewriteTest extends AbstractController +{ + /** @var Registry */ + private $registry; + + /** @var ScopeConfigInterface */ + private $config; + + /** @var string */ + private $categoryUrlSuffix; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->config = $this->_objectManager->get(ScopeConfigInterface::class); + $this->registry = $this->_objectManager->get(Registry::class); + $this->categoryUrlSuffix = $this->config->getValue( + CategoryUrlPathGenerator::XML_PATH_CATEGORY_URL_SUFFIX, + ScopeInterface::SCOPE_STORE + ); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/category_tree.php + * @dataProvider categoryRewriteProvider + * @param int $categoryId + * @param string $urlPath + * @return void + */ + public function testCategoryUrlRewrite(int $categoryId, string $urlPath): void + { + $this->dispatch(sprintf($urlPath, $this->categoryUrlSuffix)); + $currentCategory = $this->registry->registry('current_category'); + $response = $this->getResponse(); + $this->assertEquals( + Http::STATUS_CODE_200, + $response->getHttpResponseCode(), + 'Response code does not match expected value' + ); + $this->assertNotNull($currentCategory); + $this->assertEquals($categoryId, $currentCategory->getId()); + } + + /** + * @return array + */ + public function categoryRewriteProvider(): array + { + return [ + [ + 'category_id' => 400, + 'url_path' => '/category-1%s', + ], + [ + 'category_id' => 401, + 'url_path' => '/category-1/category-1-1%s', + ], + [ + 'category_id' => 402, + 'url_path' => '/category-1/category-1-1/category-1-1-1%s', + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/CategoryTest.php index 87b8d4a117e2d..c18a867a9b76e 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/CategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/CategoryTest.php @@ -3,24 +3,75 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Controller; +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Model\Category; +use Magento\TestFramework\Catalog\Model\CategoryLayoutUpdateManager; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Catalog\Model\Session; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\TestCase\AbstractController; + /** - * Test class for \Magento\Catalog\Controller\Category. + * Responsible for testing category view action on strorefront. * + * @see \Magento\Catalog\Controller\Category\View * @magentoAppArea frontend */ -class CategoryTest extends \Magento\TestFramework\TestCase\AbstractController +class CategoryTest extends AbstractController { + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var Registry + */ + private $registry; + + /** + * @var Session + */ + private $session; + + /** + * @var LayoutInterface + */ + private $layout; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->registry = $this->objectManager->get(Registry::class); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->session = $this->objectManager->get(Session::class); + } + + /** + * @inheritdoc + */ public function assert404NotFound() { parent::assert404NotFound(); - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->assertNull($objectManager->get(\Magento\Framework\Registry::class)->registry('current_category')); + + $this->assertNull($this->registry->registry('current_category')); } - public function getViewActionDataProvider() + /** + * @return array + */ + public function getViewActionDataProvider(): array { return [ 'category without children' => [ @@ -56,51 +107,96 @@ public function getViewActionDataProvider() * @dataProvider getViewActionDataProvider * @magentoDataFixture Magento/CatalogUrlRewrite/_files/categories_with_product_ids.php * @magentoDbIsolation disabled + * @param int $categoryId + * @param array $expectedHandles + * @param array $expectedContent + * @return void */ - public function testViewAction($categoryId, array $expectedHandles, array $expectedContent) + public function testViewAction(int $categoryId, array $expectedHandles, array $expectedContent): void { $this->dispatch("catalog/category/view/id/{$categoryId}"); - - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - - /** @var $currentCategory \Magento\Catalog\Model\Category */ - $currentCategory = $objectManager->get(\Magento\Framework\Registry::class)->registry('current_category'); - $this->assertInstanceOf(\Magento\Catalog\Model\Category::class, $currentCategory); + /** @var $currentCategory Category */ + $currentCategory = $this->registry->registry('current_category'); + $this->assertInstanceOf(Category::class, $currentCategory); $this->assertEquals($categoryId, $currentCategory->getId(), 'Category in registry.'); - $lastCategoryId = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Catalog\Model\Session::class - )->getLastVisitedCategoryId(); + $lastCategoryId = $this->session->getLastVisitedCategoryId(); $this->assertEquals($categoryId, $lastCategoryId, 'Last visited category.'); /* Layout updates */ - $handles = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Framework\View\LayoutInterface::class - )->getUpdate()->getHandles(); + $handles = $this->layout->getUpdate()->getHandles(); foreach ($expectedHandles as $expectedHandleName) { $this->assertContains($expectedHandleName, $handles); } $responseBody = $this->getResponse()->getBody(); - /* Response content */ foreach ($expectedContent as $expectedText) { $this->assertStringMatchesFormat($expectedText, $responseBody); } } - public function testViewActionNoCategoryId() + /** + * @return void + */ + public function testViewActionNoCategoryId(): void { $this->dispatch('catalog/category/view/'); $this->assert404NotFound(); } - public function testViewActionInactiveCategory() + /** + * @return void + */ + public function testViewActionNotExistingCategory(): void { $this->dispatch('catalog/category/view/id/8'); $this->assert404NotFound(); } + + /** + * Checks that disabled category is not available in storefront + * + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/inactive_category.php + * @return void + */ + public function testViewActionDisabledCategory(): void + { + $this->dispatch('catalog/category/view/id/111'); + + $this->assert404NotFound(); + } + + /** + * Check that custom layout update files is employed. + * + * @magentoDataFixture Magento/CatalogUrlRewrite/_files/categories_with_product_ids.php + * @return void + */ + public function testViewWithCustomUpdate(): void + { + //Setting a fake file for the category. + $file = 'test-file'; + $categoryId = 5; + /** @var CategoryLayoutUpdateManager $layoutManager */ + $layoutManager = Bootstrap::getObjectManager()->get(CategoryLayoutUpdateManager::class); + $layoutManager->setCategoryFakeFiles($categoryId, [$file]); + /** @var CategoryRepositoryInterface $categoryRepo */ + $categoryRepo = Bootstrap::getObjectManager()->create(CategoryRepositoryInterface::class); + $category = $categoryRepo->get($categoryId); + //Updating the custom attribute. + $category->setCustomAttribute('custom_layout_update_file', $file); + $categoryRepo->save($category); + + //Viewing the category + $this->dispatch("catalog/category/view/id/$categoryId"); + //Layout handles must contain the file. + $handles = Bootstrap::getObjectManager()->get(\Magento\Framework\View\LayoutInterface::class) + ->getUpdate() + ->getHandles(); + $this->assertContains("catalog_category_view_selectable_{$categoryId}_{$file}", $handles); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ProductUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ProductUrlRewriteTest.php new file mode 100644 index 0000000000000..d55c85d9b9d00 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ProductUrlRewriteTest.php @@ -0,0 +1,206 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Controller\Product; + +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Registry; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Request; +use Magento\TestFramework\Response; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Checks product availability on storefront by url rewrite + * + * @magentoConfigFixture default/catalog/seo/generate_category_product_rewrites 1 + * @magentoDbIsolation enabled + */ +class ProductUrlRewriteTest extends AbstractController +{ + /** @var ScopeConfigInterface */ + private $config; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var Registry */ + private $registry; + + /** @var CategoryRepositoryInterface */ + private $categoryRepository; + + /** @var StoreManagerInterface */ + private $storeManager; + + /** @var string */ + private $urlSuffix; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->config = $this->_objectManager->get(ScopeConfigInterface::class); + $this->productRepository = $this->_objectManager->create(ProductRepositoryInterface::class); + $this->registry = $this->_objectManager->get(Registry::class); + $this->categoryRepository = $this->_objectManager->create(CategoryRepositoryInterface::class); + $this->storeManager = $this->_objectManager->get(StoreManagerInterface::class); + $this->urlSuffix = $this->config->getValue( + ProductUrlPathGenerator::XML_PATH_PRODUCT_URL_SUFFIX, + ScopeInterface::SCOPE_STORE + ); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @return void + */ + public function testProductUrlRewrite(): void + { + $product = $this->productRepository->get('simple2'); + $url = $this->prepareUrl($product->getUrlKey()); + $this->dispatch($url); + + $this->assertProductIsVisible($product); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/category_product.php + * @return void + */ + public function testCategoryProductUrlRewrite(): void + { + $category = $this->categoryRepository->get(333); + $product = $this->productRepository->get('simple333'); + $url = $this->prepareUrl($category->getUrlKey(), false) . $this->prepareUrl($product->getUrlKey()); + $this->dispatch($url); + + $this->assertProductIsVisible($product); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @return void + */ + public function testProductRedirect(): void + { + $product = $this->productRepository->get('simple2'); + $oldUrl = $this->prepareUrl($product->getUrlKey()); + $data = [ + 'url_key' => 'new-url-key', + 'url_key_create_redirect' => $product->getUrlKey(), + 'save_rewrites_history' => true, + ]; + $this->updateProduct($product, $data); + $this->dispatch($oldUrl); + + $this->assertRedirect($this->stringContains($this->prepareUrl('new-url-key'))); + } + + /** + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @return void + */ + public function testMultistoreProductUrlRewrite(): void + { + $currentStore = $this->storeManager->getStore(); + $product = $this->productRepository->get('simple2'); + $firstStoreUrl = $this->prepareUrl($product->getUrlKey()); + $secondStoreId = $this->storeManager->getStore('fixturestore')->getId(); + $this->storeManager->setCurrentStore($secondStoreId); + + try { + $product = $this->updateProduct($product, ['url_key' => 'second-store-url-key']); + $this->assertEquals('second-store-url-key', $product->getUrlKey()); + $secondStoreUrl = $this->prepareUrl($product->getUrlKey()); + + $this->dispatch($secondStoreUrl); + $this->assertProductIsVisible($product); + $this->cleanUpCachedObjects(); + } finally { + $this->storeManager->setCurrentStore($currentStore); + } + + $this->dispatch($firstStoreUrl); + $this->assertProductIsVisible($product); + } + + /** + * Update product + * + * @param ProductInterface $product + * @param array $data + * @return ProductInterface + */ + private function updateProduct(ProductInterface $product, array $data): ProductInterface + { + $product->addData($data); + + return $this->productRepository->save($product); + } + + /** + * Clean up cached objects + * + * @return void + */ + private function cleanUpCachedObjects(): void + { + $this->registry->unregister('current_product'); + $this->registry->unregister('product'); + $this->_objectManager->removeSharedInstance(Request::class); + $this->_objectManager->removeSharedInstance(Response::class); + $this->_response = null; + $this->_request = null; + } + + /** + * Prepare url to dispatch + * + * @param string $urlKey + * @param bool $addSuffix + * @return string + */ + private function prepareUrl(string $urlKey, bool $addSuffix = true): string + { + $url = $addSuffix ? '/' . $urlKey . $this->urlSuffix : '/' . $urlKey; + + return $url; + } + + /** + * Assert that product is available in storefront + * + * @param ProductInterface $product + * @return void + */ + private function assertProductIsVisible(ProductInterface $product): void + { + $this->assertEquals( + Response::STATUS_CODE_200, + $this->getResponse()->getHttpResponseCode(), + 'Wrong response code is returned' + ); + $currentProduct = $this->registry->registry('current_product'); + $this->assertNotNull($currentProduct); + $this->assertEquals( + $product->getSku(), + $currentProduct->getSku(), + 'Wrong product is registered' + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ViewTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ViewTest.php index 92a782deee65a..f45c9934acfc1 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ViewTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/ViewTest.php @@ -3,18 +3,91 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Controller\Product; +use Magento\Catalog\Api\AttributeSetRepositoryInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Eav\Model\Entity\Type; +use Magento\Framework\App\Http; +use Magento\Framework\Registry; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Eav\Model\GetAttributeSetByName; +use Magento\TestFramework\Request; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Framework\Logger\Monolog as MagentoMonologLogger; +use Magento\TestFramework\Response; +use Magento\TestFramework\TestCase\AbstractController; + /** - * @magentoDataFixture Magento/Catalog/controllers/_files/products.php - * @magentoDbIsolation disabled + * Integration test for product view front action. + * + * @magentoAppArea frontend + * @magentoDbIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class ViewTest extends \Magento\TestFramework\TestCase\AbstractController +class ViewTest extends AbstractController { /** + * @var ProductRepositoryInterface $productRepository + */ + private $productRepository; + + /** + * @var AttributeSetRepositoryInterface $attributeSetRepository + */ + private $attributeSetRepository; + + /** + * @var ProductAttributeRepositoryInterface $attributeSetRepository + */ + private $attributeRepository; + + /** + * @var Type $productEntityType + */ + private $productEntityType; + + /** @var Registry */ + private $registry; + + /** @var StoreManagerInterface */ + private $storeManager; + + /** @var GetAttributeSetByName */ + private $getAttributeSetByName; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->productRepository = $this->_objectManager->create(ProductRepositoryInterface::class); + $this->attributeSetRepository = $this->_objectManager->create(AttributeSetRepositoryInterface::class); + $this->attributeRepository = $this->_objectManager->create(ProductAttributeRepositoryInterface::class); + $this->productEntityType = $this->_objectManager->create(Type::class) + ->loadByCode(Product::ENTITY); + $this->registry = $this->_objectManager->get(Registry::class); + $this->storeManager = $this->_objectManager->get(StoreManagerInterface::class); + $this->getAttributeSetByName = $this->_objectManager->get(GetAttributeSetByName::class); + } + + /** + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/Catalog/controllers/_files/products.php * @magentoConfigFixture current_store catalog/seo/product_canonical_tag 1 + * @return void */ - public function testViewActionWithCanonicalTag() + public function testViewActionWithCanonicalTag(): void { $this->markTestSkipped( 'MAGETWO-40724: Canonical url from tests sometimes does not equal canonical url from action' @@ -26,4 +99,261 @@ public function testViewActionWithCanonicalTag() $this->getResponse()->getBody() ); } + + /** + * View product with custom attribute when attribute removed from it. + * + * It tests that after changing product attribute set from Default to Custom + * there are no warning messages in log in case Custom not contains attribute from Default. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_country_of_manufacture.php + * @magentoDataFixture Magento/Catalog/_files/attribute_set_based_on_default_without_country_of_manufacture.php + * @return void + */ + public function testViewActionCustomAttributeSetWithoutCountryOfManufacture(): void + { + /** @var MockObject|LoggerInterface $logger */ + $logger = $this->setupLoggerMock(); + $product = $this->productRepository->get('simple_with_com'); + $attributeSetCustom = $this->getAttributeSetByName->execute('custom_attribute_set_wout_com'); + $product->setAttributeSetId($attributeSetCustom->getAttributeSetId()); + $this->productRepository->save($product); + + /** @var ProductAttributeInterface $attributeCountryOfManufacture */ + $attributeCountryOfManufacture = $this->attributeRepository->get('country_of_manufacture'); + $logger->expects($this->never()) + ->method('warning') + ->with( + "Attempt to load value of nonexistent EAV attribute", + [ + 'attribute_id' => $attributeCountryOfManufacture->getAttributeId(), + 'entity_type' => ProductInterface::class, + ] + ); + + $this->dispatch(sprintf('catalog/product/view/id/%s/', $product->getId())); + } + + /** + * @magentoDataFixture Magento/Quote/_files/is_not_salable_product.php + * @return void + */ + public function testDisabledProductInvisibility(): void + { + $product = $this->productRepository->get('simple-99'); + $this->dispatch(sprintf('catalog/product/view/id/%s/', $product->getId())); + + $this->assert404NotFound(); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @dataProvider productVisibilityDataProvider + * @param int $visibility + * @return void + */ + public function testProductVisibility(int $visibility): void + { + $product = $this->updateProductVisibility('simple2', $visibility); + $this->dispatch(sprintf('catalog/product/view/id/%s/', $product->getId())); + + $this->assertProductIsVisible($product); + } + + /** + * @return array + */ + public function productVisibilityDataProvider(): array + { + return [ + 'catalog_search' => [Visibility::VISIBILITY_BOTH], + 'search' => [Visibility::VISIBILITY_IN_SEARCH], + 'catalog' => [Visibility::VISIBILITY_IN_CATALOG], + ]; + } + + /** + * @magentoDataFixture Magento/Catalog/_files/simple_products_not_visible_individually.php + */ + public function testProductNotVisibleIndividually(): void + { + $product = $this->updateProductVisibility('simple_not_visible_1', Visibility::VISIBILITY_NOT_VISIBLE); + $this->dispatch(sprintf('catalog/product/view/id/%s/', $product->getId())); + + $this->assert404NotFound(); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_two_websites.php + * @magentoDbIsolation disabled + * @return void + */ + public function testProductVisibleOnTwoWebsites(): void + { + $currentStore = $this->storeManager->getStore(); + $product = $this->productRepository->get('simple-on-two-websites'); + $secondStoreId = $this->storeManager->getStore('fixture_second_store')->getId(); + $this->dispatch(sprintf('catalog/product/view/id/%s', $product->getId())); + $this->assertProductIsVisible($product); + $this->cleanUpCachedObjects(); + + try { + $this->storeManager->setCurrentStore($secondStoreId); + $this->dispatch(sprintf('catalog/product/view/id/%s', $product->getId())); + $this->assertProductIsVisible($product); + } finally { + $this->storeManager->setCurrentStore($currentStore); + } + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_two_websites.php + * @magentoDbIsolation disabled + * @return void + */ + public function testRemoveProductFromOneWebsiteVisibility(): void + { + $websiteId = $this->storeManager->getWebsite('test')->getId(); + $currentStore = $this->storeManager->getStore(); + $secondStoreId = $this->storeManager->getStore('fixture_second_store')->getId(); + $product = $this->updateProduct('simple-on-two-websites', ['website_ids' => [$websiteId]]); + $this->dispatch(sprintf('catalog/product/view/id/%s', $product->getId())); + $this->assert404NotFound(); + $this->cleanUpCachedObjects(); + + try { + $this->storeManager->setCurrentStore($secondStoreId); + + $this->dispatch(sprintf('catalog/product/view/id/%s', $product->getId())); + $this->assertProductIsVisible($product); + } finally { + $this->storeManager->setCurrentStore($currentStore->getId()); + } + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_two_websites.php + * @magentoDbIsolation disabled + * @return void + */ + public function testProductAttributeByStores(): void + { + $secondStoreId = $this->storeManager->getStore('fixture_second_store')->getId(); + $product = $this->productRepository->get('simple-on-two-websites'); + $currentStoreId = $this->storeManager->getStore()->getId(); + + try { + $this->storeManager->setCurrentStore($secondStoreId); + $product = $this->updateProduct($product, ['status' => 2]); + $this->dispatch(sprintf('catalog/product/view/id/%s', $product->getId())); + $this->assert404NotFound(); + $this->cleanUpCachedObjects(); + $this->storeManager->setCurrentStore($currentStoreId); + $this->dispatch(sprintf('catalog/product/view/id/%s', $product->getId())); + $this->assertProductIsVisible($product); + } finally { + $this->storeManager->setCurrentStore($currentStoreId); + } + } + + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @return void + */ + public function testProductWithoutWebsite(): void + { + $product = $this->updateProduct('simple2', ['website_ids' => []]); + $this->dispatch(sprintf('catalog/product/view/id/%s', $product->getId())); + + $this->assert404NotFound(); + } + + /** + * @param string|ProductInterface $product + * @param array $data + * @return ProductInterface + */ + public function updateProduct($product, array $data): ProductInterface + { + $product = is_string($product) ? $this->productRepository->get($product) : $product; + $product->addData($data); + + return $this->productRepository->save($product); + } + + /** + * @inheritdoc + */ + public function assert404NotFound() + { + parent::assert404NotFound(); + + $this->assertNull($this->registry->registry('current_product')); + } + + /** + * Assert that product is available in storefront + * + * @param ProductInterface $product + * @return void + */ + private function assertProductIsVisible(ProductInterface $product): void + { + $this->assertEquals( + Response::STATUS_CODE_200, + $this->getResponse()->getHttpResponseCode(), + 'Wrong response code is returned' + ); + $currentProduct = $this->registry->registry('current_product'); + $this->assertNotNull($currentProduct); + $this->assertEquals( + $product->getSku(), + $currentProduct->getSku(), + 'Wrong product is registered' + ); + } + + /** + * Clean up cached objects. + * + * @return void + */ + private function cleanUpCachedObjects(): void + { + $this->_objectManager->removeSharedInstance(Http::class); + $this->_objectManager->removeSharedInstance(Request::class); + $this->_objectManager->removeSharedInstance(Response::class); + $this->_request = null; + $this->_response = null; + } + + /** + * Setup logger mock to check there are no warning messages logged. + * + * @return MockObject + */ + private function setupLoggerMock(): MockObject + { + $logger = $this->getMockBuilder(LoggerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->_objectManager->addSharedInstance($logger, MagentoMonologLogger::class); + + return $logger; + } + + /** + * Update product visibility + * + * @param string $sku + * @param int $visibility + * @return ProductInterface + */ + private function updateProductVisibility(string $sku, int $visibility): ProductInterface + { + $product = $this->productRepository->get($sku); + $product->setVisibility($visibility); + + return $this->productRepository->save($product); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/ProductTest.php index ca9db3f28a91b..20805271f6b5b 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/ProductTest.php @@ -3,92 +3,114 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + -/** - * Test class for \Magento\Catalog\Controller\Product. - */ namespace Magento\Catalog\Controller; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\TestFramework\Catalog\Model\ProductLayoutUpdateManager; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Session; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Xpath; +use Magento\TestFramework\TestCase\AbstractController; + /** - * @magentoAppIsolation enabled + * Checks product view on storefront + * + * @see \Magento\Catalog\Controller\Product + * + * @magentoDbIsolation enabled */ -class ProductTest extends \Magento\TestFramework\TestCase\AbstractController +class ProductTest extends AbstractController { + /** @var Registry */ + private $registry; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var Session */ + private $session; + + /** + * @inheritdoc + */ protected function setUp() { if (defined('HHVM_VERSION')) { $this->markTestSkipped('Randomly fails due to known HHVM bug (DOMText mixed with DOMElement)'); } parent::setUp(); + + $this->registry = $this->_objectManager->get(Registry::class); + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + $this->session = $this->_objectManager->get(Session::class); } + /** + * @inheritdoc + */ public function assert404NotFound() { parent::assert404NotFound(); - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->assertNull($objectManager->get(\Magento\Framework\Registry::class)->registry('current_product')); + + $this->assertNull($this->registry->registry('current_product')); } - protected function _getProductImageFile() + /** + * Get product image file + * + * @return string + */ + protected function getProductImageFile(): string { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** - * @var $repository \Magento\Catalog\Model\ProductRepository - */ - $repository = $objectManager->create(\Magento\Catalog\Model\ProductRepository::class); - $product = $repository->get('simple_product_1'); + $product = $this->productRepository->get('simple_product_1'); $images = $product->getMediaGalleryImages()->getItems(); $image = reset($images); + return $image['file']; } /** * @magentoDataFixture Magento/Catalog/controllers/_files/products.php * @magentoAppArea frontend + * @return void */ - public function testViewAction() + public function testViewAction(): void { - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** - * @var $repository \Magento\Catalog\Model\ProductRepository - */ - $repository = $objectManager->create(\Magento\Catalog\Model\ProductRepository::class); - $product = $repository->get('simple_product_1'); + $product = $this->productRepository->get('simple_product_1'); $this->dispatch(sprintf('catalog/product/view/id/%s', $product->getEntityId())); + $currentProduct = $this->registry->registry('current_product'); - /** @var $currentProduct \Magento\Catalog\Model\Product */ - $currentProduct = $objectManager->get(\Magento\Framework\Registry::class)->registry('current_product'); - $this->assertInstanceOf(\Magento\Catalog\Model\Product::class, $currentProduct); + $this->assertInstanceOf(ProductInterface::class, $currentProduct); $this->assertEquals($product->getEntityId(), $currentProduct->getEntityId()); - - $lastViewedProductId = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Catalog\Model\Session::class - )->getLastViewedProductId(); - $this->assertEquals($product->getEntityId(), $lastViewedProductId); + $this->assertEquals($product->getEntityId(), $this->session->getLastViewedProductId()); $responseBody = $this->getResponse()->getBody(); /* Product info */ - $this->assertContains('Simple Product 1 Name', $responseBody); - $this->assertContains('Simple Product 1 Full Description', $responseBody); - $this->assertContains('Simple Product 1 Short Description', $responseBody); + $this->assertContains($product->getName(), $responseBody); + $this->assertContains($product->getDescription(), $responseBody); + $this->assertContains($product->getShortDescription(), $responseBody); + $this->assertContains($product->getSku(), $responseBody); /* Stock info */ $this->assertContains('$1,234.56', $responseBody); $this->assertContains('In stock', $responseBody); - $this->assertContains('Add to Cart', $responseBody); + $this->assertContains((string)__('Add to Cart'), $responseBody); /* Meta info */ $this->assertContains('<title>Simple Product 1 Meta Title', $responseBody); $this->assertEquals( 1, - \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath( + Xpath::getElementsCountForXpath( '//meta[@name="keywords" and @content="Simple Product 1 Meta Keyword"]', $responseBody ) ); $this->assertEquals( 1, - \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath( + Xpath::getElementsCountForXpath( '//meta[@name="description" and @content="Simple Product 1 Meta Description"]', $responseBody ) @@ -97,34 +119,36 @@ public function testViewAction() /** * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @return void */ - public function testViewActionConfigurable() + public function testViewActionConfigurable(): void { - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** - * @var $repository \Magento\Catalog\Model\ProductRepository - */ - $repository = $objectManager->create(\Magento\Catalog\Model\ProductRepository::class); - $product = $repository->get('simple'); + $product = $this->productRepository->get('simple'); $this->dispatch(sprintf('catalog/product/view/id/%s', $product->getEntityId())); $html = $this->getResponse()->getBody(); $this->assertEquals( 1, - \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath( + Xpath::getElementsCountForXpath( '//*[@id="product-options-wrapper"]', $html ) ); } - public function testViewActionNoProductId() + /** + * @return void + */ + public function testViewActionNoProductId(): void { $this->dispatch('catalog/product/view/id/'); + $this->assert404NotFound(); } - public function testViewActionRedirect() + /** + * @return void + */ + public function testViewActionRedirect(): void { $this->dispatch('catalog/product/view/?store=default'); @@ -133,30 +157,31 @@ public function testViewActionRedirect() /** * @magentoDataFixture Magento/Catalog/controllers/_files/products.php + * @return void */ - public function testGalleryAction() + public function testGalleryAction(): void { - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** - * @var $repository \Magento\Catalog\Model\ProductRepository - */ - $repository = $objectManager->create(\Magento\Catalog\Model\ProductRepository::class); - $product = $repository->get('simple_product_1'); + $product = $this->productRepository->get('simple_product_1'); $this->dispatch(sprintf('catalog/product/gallery/id/%s', $product->getEntityId())); $this->assertContains('http://localhost/pub/media/catalog/product/', $this->getResponse()->getBody()); - $this->assertContains($this->_getProductImageFile(), $this->getResponse()->getBody()); + $this->assertContains($this->getProductImageFile(), $this->getResponse()->getBody()); } - public function testGalleryActionRedirect() + /** + * @return void + */ + public function testGalleryActionRedirect(): void { $this->dispatch('catalog/product/gallery/?store=default'); $this->assertRedirect(); } - public function testGalleryActionNoProduct() + /** + * @return void + */ + public function testGalleryActionNoProduct(): void { $this->dispatch('catalog/product/gallery/id/'); @@ -165,13 +190,14 @@ public function testGalleryActionNoProduct() /** * @magentoDataFixture Magento/Catalog/controllers/_files/products.php + * @return void */ - public function testImageAction() + public function testImageAction(): void { $this->markTestSkipped("All logic has been cut to avoid possible malicious usage of the method"); ob_start(); /* Preceding slash in URL is required in this case */ - $this->dispatch('/catalog/product/image' . $this->_getProductImageFile()); + $this->dispatch('/catalog/product/image' . $this->getProductImageFile()); $imageContent = ob_get_clean(); /** * Check against PNG file signature. @@ -180,10 +206,44 @@ public function testImageAction() $this->assertStringStartsWith(sprintf("%cPNG\r\n%c\n", 137, 26), $imageContent); } - public function testImageActionNoImage() + /** + * @return void + */ + public function testImageActionNoImage(): void { $this->dispatch('catalog/product/image/'); $this->assert404NotFound(); } + + /** + * Check that custom layout update files is employed. + * + * @magentoDataFixture Magento/Catalog/controllers/_files/products.php + * @return void + */ + public function testViewWithCustomUpdate(): void + { + //Setting a fake file for the product. + $file = 'test-file'; + /** @var ProductRepositoryInterface $repository */ + $repository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); + $sku = 'simple_product_1'; + $product = $repository->get($sku); + $productId = $product->getId(); + /** @var ProductLayoutUpdateManager $layoutManager */ + $layoutManager = Bootstrap::getObjectManager()->get(ProductLayoutUpdateManager::class); + $layoutManager->setFakeFiles((int)$productId, [$file]); + //Updating the custom attribute. + $product->setCustomAttribute('custom_layout_update_file', $file); + $repository->save($product); + + //Viewing the product + $this->dispatch("catalog/product/view/id/$productId"); + //Layout handles must contain the file. + $handles = Bootstrap::getObjectManager()->get(\Magento\Framework\View\LayoutInterface::class) + ->getUpdate() + ->getHandles(); + $this->assertContains("catalog_product_view_selectable_{$sku}_{$file}", $handles); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdateTest.php new file mode 100644 index 0000000000000..40725d3ee58be --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/AbstractLayoutUpdateTest.php @@ -0,0 +1,121 @@ +category = $this->categoryFactory->create(); + $this->category->load(2); + } + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->categoryFactory = Bootstrap::getObjectManager()->get(CategoryFactory::class); + $this->recreateCategory(); + $this->attribute = $this->category->getAttributes()['custom_layout_update_file']->getBackend(); + $this->layoutManager = Bootstrap::getObjectManager()->get(CategoryLayoutUpdateManager::class); + } + + /** + * Check that custom layout update file's values erase the old attribute's value. + * + * @return void + * @throws \Throwable + */ + public function testDependsOnNewUpdate(): void + { + //New selected file value is set + $this->layoutManager->setCategoryFakeFiles(2, ['new']); + $this->category->setCustomAttribute('custom_layout_update', 'test'); + $this->category->setOrigData('custom_layout_update', 'test'); + $this->category->setCustomAttribute('custom_layout_update_file', 'new'); + $this->attribute->beforeSave($this->category); + $this->assertEmpty($this->category->getCustomAttribute('custom_layout_update')->getValue()); + $this->assertEquals('new', $this->category->getCustomAttribute('custom_layout_update_file')->getValue()); + $this->assertEmpty($this->category->getData('custom_layout_update')); + $this->assertEquals('new', $this->category->getData('custom_layout_update_file')); + + //Existing update chosen + $this->recreateCategory(); + $this->category->setData('custom_layout_update', 'test'); + $this->category->setOrigData('custom_layout_update', 'test'); + $this->category->setData( + 'custom_layout_update_file', + \Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate::VALUE_USE_UPDATE_XML + ); + $this->attribute->beforeSave($this->category); + $this->assertEquals('test', $this->category->getData('custom_layout_update')); + /** @var AbstractBackend $fileAttribute */ + $fileAttribute = $this->category->getAttributes()['custom_layout_update_file']->getBackend(); + $fileAttribute->beforeSave($this->category); + $this->assertEquals(null, $this->category->getData('custom_layout_update_file')); + + //Removing custom layout update by explicitly selecting the new file (or an empty file). + $this->recreateCategory(); + $this->category->setData('custom_layout_update', 'test'); + $this->category->setOrigData('custom_layout_update', 'test'); + $this->category->setData( + 'custom_layout_update_file', + \Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate::VALUE_NO_UPDATE + ); + $this->attribute->beforeSave($this->category); + $this->assertEmpty($this->category->getData('custom_layout_update')); + + //Empty value doesn't change the old attribute. Any non-string value can be used to represent an empty value. + $this->recreateCategory(); + $this->category->setData('custom_layout_update', 'test'); + $this->category->setOrigData('custom_layout_update', 'test'); + $this->category->setData( + 'custom_layout_update_file', + false + ); + $this->attribute->beforeSave($this->category); + $this->assertEquals('test', $this->category->getData('custom_layout_update')); + $this->assertNull($this->category->getData('custom_layout_update_file')); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php new file mode 100644 index 0000000000000..7f594d265418f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Attribute/Backend/CustomlayoutupdateTest.php @@ -0,0 +1,124 @@ +category = $this->categoryFactory->create(); + $this->category->load(2); + } + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->categoryFactory = Bootstrap::getObjectManager()->get(CategoryFactory::class); + $this->recreateCategory(); + $this->attribute = $this->category->getAttributes()['custom_layout_update']->getBackend(); + } + + /** + * Test that attribute cannot be modified but only removed completely. + * + * @return void + * @throws \Throwable + * @magentoDbIsolation enabled + */ + public function testImmutable(): void + { + //Value is empty + $this->category->setCustomAttribute('custom_layout_update', false); + $this->category->setOrigData('custom_layout_update', null); + $this->attribute->beforeSave($this->category); + + //New value + $this->category->setCustomAttribute('custom_layout_update', 'test'); + $this->category->setOrigData('custom_layout_update', null); + $caughtException = false; + try { + $this->attribute->beforeSave($this->category); + } catch (LocalizedException $exception) { + $caughtException = true; + } + $this->assertTrue($caughtException); + $this->category->setCustomAttribute('custom_layout_update', 'testNew'); + $this->category->setOrigData('custom_layout_update', 'test'); + $caughtException = false; + try { + $this->attribute->beforeSave($this->category); + } catch (LocalizedException $exception) { + $caughtException = true; + } + $this->assertTrue($caughtException); + + //Removing a value + $this->category->setCustomAttribute('custom_layout_update', ''); + $this->category->setOrigData('custom_layout_update', 'test'); + $this->attribute->beforeSave($this->category); + $this->assertNull($this->category->getCustomAttribute('custom_layout_update')->getValue()); + + //Using old stored value + //Saving old value 1st + $this->recreateCategory(); + $this->category->setOrigData('custom_layout_update', 'test'); + $this->category->setData('custom_layout_update', 'test'); + $this->category->save(); + $this->recreateCategory(); + $this->category = $this->categoryFactory->create(['data' => $this->category->getData()]); + + //Trying the same value. + $this->category->setData('custom_layout_update', 'test'); + $this->attribute->beforeSave($this->category); + //Trying new value + $this->category->setData('custom_layout_update', 'test2'); + $caughtException = false; + try { + $this->attribute->beforeSave($this->category); + } catch (LocalizedException $exception) { + $caughtException = true; + } + $this->assertTrue($caughtException); + //Empty value + $this->category->setData('custom_layout_update', null); + $this->attribute->beforeSave($this->category); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php index b8e1f07364c28..6d66055cd1548 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Category/DataProviderTest.php @@ -5,11 +5,20 @@ */ namespace Magento\Catalog\Model\Category; -use Magento\Catalog\Model\Category\DataProvider; -use Magento\Eav\Model\Config as EavConfig; +use Magento\TestFramework\Catalog\Model\CategoryLayoutUpdateManager; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\Registry; +use PHPUnit\Framework\TestCase; +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\CategoryFactory; +use Magento\Catalog\Model\Category\Attribute\Backend\LayoutUpdate; -class DataProviderTest extends \PHPUnit\Framework\TestCase +/** + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoAppArea adminhtml + */ +class DataProviderTest extends TestCase { /** * @var DataProvider @@ -17,18 +26,28 @@ class DataProviderTest extends \PHPUnit\Framework\TestCase private $dataProvider; /** - * @var \Magento\Eav\Model\Entity\Type + * @var Registry */ - private $entityType; + private $registry; /** - * {@inheritDoc} + * @var CategoryFactory */ - protected function setUp() + private $categoryFactory; + + /** + * @var CategoryLayoutUpdateManager + */ + private $fakeFiles; + + /** + * Create subject instance. + * + * @return DataProvider + */ + private function createDataProvider(): DataProvider { - parent::setUp(); - $objectManager = Bootstrap::getObjectManager(); - $this->dataProvider = $objectManager->create( + return Bootstrap::getObjectManager()->create( DataProvider::class, [ 'name' => 'category_form_data_source', @@ -36,8 +55,19 @@ protected function setUp() 'requestFieldName' => 'id' ] ); + } - $this->entityType = $objectManager->create(EavConfig::class)->getEntityType('catalog_category'); + /** + * {@inheritDoc} + */ + protected function setUp() + { + parent::setUp(); + $objectManager = Bootstrap::getObjectManager(); + $this->dataProvider = $this->createDataProvider(); + $this->registry = $objectManager->get(Registry::class); + $this->categoryFactory = $objectManager->get(CategoryFactory::class); + $this->fakeFiles = $objectManager->get(CategoryLayoutUpdateManager::class); } /** @@ -59,4 +89,136 @@ public function testGetMetaRequiredAttributes() } } } + + /** + * Check that deprecated custom layout attribute is hidden. + * + * @return void + */ + public function testOldCustomLayoutInvisible(): void + { + //Testing a category without layout xml + /** @var Category $category */ + $category = $this->categoryFactory->create(); + $category->load(2); + $this->registry->register('category', $category); + + $meta = $this->dataProvider->getMeta(); + $this->assertArrayHasKey('design', $meta); + $this->assertArrayHasKey('children', $meta['design']); + $this->assertArrayHasKey('custom_layout_update', $meta['design']['children']); + $this->assertArrayHasKey('arguments', $meta['design']['children']['custom_layout_update']); + $this->assertArrayHasKey('data', $meta['design']['children']['custom_layout_update']['arguments']); + $this->assertArrayHasKey( + 'config', + $meta['design']['children']['custom_layout_update']['arguments']['data'] + ); + $config = $meta['design']['children']['custom_layout_update']['arguments']['data']['config']; + $this->assertTrue($config['visible'] === false); + } + + /** + * Check that custom layout update file attribute is processed correctly. + * + * @return void + */ + public function testCustomLayoutFileAttribute(): void + { + //File has value + /** @var Category $category */ + $category = $this->categoryFactory->create(); + $id = 2; + $category->load($id); + $category->setData('custom_layout_update', null); + $category->setData('custom_layout_update_file', $file = 'test-file'); + $this->registry->register('category', $category); + $data = $this->dataProvider->getData(); + $this->assertEquals($file, $data[$id]['custom_layout_update_file']); + + //File has no value, the deprecated attribute does. + $this->dataProvider = $this->createDataProvider(); + $category->setData('custom_layout_update', $deprecated = 'test-deprecated'); + $category->setData('custom_layout_update_file', null); + $data = $this->dataProvider->getData(); + $this->assertEquals($deprecated, $data[$id]['custom_layout_update']); + $this->assertEquals(LayoutUpdate::VALUE_USE_UPDATE_XML, $data[$id]['custom_layout_update_file']); + } + + /** + * Extract custom layout update file attribute's options from metadata. + * + * @param array $meta + * @return array + */ + private function extractCustomLayoutOptions(array $meta): array + { + $this->assertArrayHasKey('design', $meta); + $this->assertArrayHasKey('children', $meta['design']); + $this->assertArrayHasKey('custom_layout_update_file', $meta['design']['children']); + $this->assertArrayHasKey('arguments', $meta['design']['children']['custom_layout_update_file']); + $this->assertArrayHasKey('data', $meta['design']['children']['custom_layout_update_file']['arguments']); + $this->assertArrayHasKey( + 'config', + $meta['design']['children']['custom_layout_update_file']['arguments']['data'] + ); + $this->assertArrayHasKey( + 'options', + $meta['design']['children']['custom_layout_update_file']['arguments']['data']['config'] + ); + + return $meta['design']['children']['custom_layout_update_file']['arguments']['data']['config']['options']; + } + + /** + * Check that proper options are returned for a category. + * + * @return void + */ + public function testCustomLayoutMeta(): void + { + //Testing a category without layout xml + /** @var Category $category */ + $category = $this->categoryFactory->create(); + $category->load(2); + $this->fakeFiles->setCategoryFakeFiles((int)$category->getId(), ['test1', 'test2']); + $this->registry->register('category', $category); + + $meta = $this->dataProvider->getMeta(); + $list = $this->extractCustomLayoutOptions($meta); + $expectedList = [ + [ + 'label' => 'No update', + 'value' => \Magento\Catalog\Model\Attribute\Backend\AbstractLayoutUpdate::VALUE_NO_UPDATE, + '__disableTmpl' => true + ], + ['label' => 'test1', 'value' => 'test1', '__disableTmpl' => true], + ['label' => 'test2', 'value' => 'test2', '__disableTmpl' => true] + ]; + sort($expectedList); + sort($list); + $this->assertEquals($expectedList, $list); + + //Product with old layout xml + $category->setCustomAttribute('custom_layout_update', 'test'); + $this->fakeFiles->setCategoryFakeFiles((int)$category->getId(), ['test3']); + + $meta = $this->dataProvider->getMeta(); + $expectedList = [ + [ + 'label' => 'No update', + 'value' => \Magento\Catalog\Model\Attribute\Backend\AbstractLayoutUpdate::VALUE_NO_UPDATE, + '__disableTmpl' => true + ], + [ + 'label' => 'Use existing', + 'value' => LayoutUpdate::VALUE_USE_UPDATE_XML, + '__disableTmpl' => true + ], + ['label' => 'test3', 'value' => 'test3', '__disableTmpl' => true], + ]; + $list = $this->extractCustomLayoutOptions($meta); + sort($expectedList); + sort($list); + $this->assertEquals($expectedList, $list); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryRepositoryTest.php index f1e235f8c9bf2..e1e4a87c033d0 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryRepositoryTest.php @@ -7,14 +7,14 @@ namespace Magento\Catalog\Model; -use Magento\Backend\Model\Auth; use Magento\Catalog\Api\CategoryRepositoryInterface; -use Magento\Catalog\Api\Data\CategoryInterface; -use Magento\Catalog\Api\Data\CategoryInterfaceFactory; -use Magento\Framework\Acl\Builder; +use Magento\Catalog\Api\CategoryRepositoryInterfaceFactory; +use Magento\Framework\Exception\LocalizedException; +use Magento\TestFramework\Catalog\Model\CategoryLayoutUpdateManager; +use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; -use Magento\TestFramework\Bootstrap as TestBootstrap; /** * Provide tests for CategoryRepository model. @@ -22,26 +22,24 @@ class CategoryRepositoryTest extends TestCase { /** - * Test subject. - * - * @var CategoryRepositoryInterface + * @var CategoryLayoutUpdateManager */ - private $repo; + private $layoutManager; /** - * @var Auth + * @var CategoryRepositoryInterfaceFactory */ - private $auth; + private $repositoryFactory; /** - * @var Builder + * @var CollectionFactory */ - private $aclBuilder; + private $productCollectionFactory; /** - * @var CategoryInterfaceFactory + * @var CategoryCollectionFactory */ - private $categoryFactory; + private $categoryCollectionFactory; /** * Sets up common objects. @@ -50,63 +48,82 @@ class CategoryRepositoryTest extends TestCase */ protected function setUp() { - $this->repo = Bootstrap::getObjectManager()->create(CategoryRepositoryInterface::class); - $this->auth = Bootstrap::getObjectManager()->get(Auth::class); - $this->aclBuilder = Bootstrap::getObjectManager()->get(Builder::class); - $this->categoryFactory = Bootstrap::getObjectManager()->get(CategoryInterfaceFactory::class); + $this->repositoryFactory = Bootstrap::getObjectManager()->get(CategoryRepositoryInterfaceFactory::class); + $this->layoutManager = Bootstrap::getObjectManager()->get(CategoryLayoutUpdateManager::class); + $this->productCollectionFactory = Bootstrap::getObjectManager()->get(CollectionFactory::class); + $this->categoryCollectionFactory = Bootstrap::getObjectManager()->create(CategoryCollectionFactory::class); } /** - * @inheritDoc + * Create subject object. + * + * @return CategoryRepositoryInterface */ - protected function tearDown() + private function createRepo(): CategoryRepositoryInterface { - parent::tearDown(); - - $this->auth->logout(); - $this->aclBuilder->resetRuntimeAcl(); + return $this->repositoryFactory->create(); } /** - * Test authorization when saving category's design settings. + * Test that custom layout file attribute is saved. * + * @return void + * @throws \Throwable * @magentoDataFixture Magento/Catalog/_files/category.php - * @magentoAppArea adminhtml * @magentoDbIsolation enabled * @magentoAppIsolation enabled */ - public function testSaveDesign() + public function testCustomLayout(): void { - $category = $this->repo->get(333); - $this->auth->login(TestBootstrap::ADMIN_NAME, TestBootstrap::ADMIN_PASSWORD); - - //Admin doesn't have access to category's design. - $this->aclBuilder->getAcl()->deny(null, 'Magento_Catalog::edit_category_design'); - - $category->setCustomAttribute('custom_design', 2); - $category = $this->repo->save($category); - $customDesignAttribute = $category->getCustomAttribute('custom_design'); - $this->assertTrue(!$customDesignAttribute || !$customDesignAttribute->getValue()); - - //Admin has access to category' design. - $this->aclBuilder->getAcl() - ->allow(null, ['Magento_Catalog::categories', 'Magento_Catalog::edit_category_design']); + //New valid value + $repo = $this->createRepo(); + $category = $repo->get(333); + $newFile = 'test'; + $this->layoutManager->setCategoryFakeFiles(333, [$newFile]); + $category->setCustomAttribute('custom_layout_update_file', $newFile); + $repo->save($category); + $repo = $this->createRepo(); + $category = $repo->get(333); + $this->assertEquals($newFile, $category->getCustomAttribute('custom_layout_update_file')->getValue()); - $category->setCustomAttribute('custom_design', 2); - $category = $this->repo->save($category); - $this->assertNotEmpty($category->getCustomAttribute('custom_design')); - $this->assertEquals(2, $category->getCustomAttribute('custom_design')->getValue()); + //Setting non-existent value + $newFile = 'does not exist'; + $category->setCustomAttribute('custom_layout_update_file', $newFile); + $caughtException = false; + try { + $repo->save($category); + } catch (LocalizedException $exception) { + $caughtException = true; + } + $this->assertTrue($caughtException); + } - //Creating a new one - /** @var CategoryInterface $newCategory */ - $newCategory = $this->categoryFactory->create(); - $newCategory->setName('new category without design'); - $newCategory->setParentId($category->getParentId()); - $newCategory->setIsActive(true); - $this->aclBuilder->getAcl()->deny(null, 'Magento_Catalog::edit_category_design'); - $newCategory->setCustomAttribute('custom_design', 2); - $newCategory = $this->repo->save($newCategory); - $customDesignAttribute = $newCategory->getCustomAttribute('custom_design'); - $this->assertTrue(!$customDesignAttribute || !$customDesignAttribute->getValue()); + /** + * Test removal of categories. + * + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/categories.php + * @magentoAppArea adminhtml + * @return void + */ + public function testCategoryBehaviourAfterDelete(): void + { + $productCollection = $this->productCollectionFactory->create(); + $deletedCategories = ['3', '4', '5', '13']; + $categoryCollectionIds = $this->categoryCollectionFactory->create()->getAllIds(); + $this->createRepo()->deleteByIdentifier(3); + $this->assertEquals( + 0, + $productCollection->addCategoriesFilter(['in' => $deletedCategories])->getSize(), + 'The category-products relations was not deleted after category delete' + ); + $newCategoryCollectionIds = $this->categoryCollectionFactory->create()->getAllIds(); + $difference = array_diff($categoryCollectionIds, $newCategoryCollectionIds); + sort($difference); + $this->assertEquals( + $deletedCategories, + $difference, + 'Wrong categories was deleted' + ); } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AbstractAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AbstractAttributeTest.php new file mode 100644 index 0000000000000..d2ab4d69dc45c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AbstractAttributeTest.php @@ -0,0 +1,196 @@ +objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->attributeRepository = $this->objectManager->create(AttributeRepositoryInterface::class); + } + + /** + * @dataProvider productProvider + * @param $productSku + * @return void + */ + public function testSaveAttribute(string $productSku): void + { + $product = $this->setAttributeValueAndValidate($productSku, $this->getDefaultAttributeValue()); + $product = $this->productRepository->save($product); + $this->assertEquals($this->getDefaultAttributeValue(), $product->getData($this->getAttributeCode())); + } + + /** + * @dataProvider productProvider + * @param string $productSku + * @return void + */ + public function testRequiredAttribute(string $productSku): void + { + $this->expectException(Exception::class); + $messageFormat = 'The "%s" attribute value is empty. Set the attribute and try again.'; + $this->expectExceptionMessage( + (string)__(sprintf($messageFormat, $this->getAttribute()->getDefaultFrontendLabel())) + ); + $this->prepareAttribute(['is_required' => true]); + $this->unsetAttributeValueAndValidate($productSku); + } + + /** + * @dataProvider productProvider + * @param string $productSku + * @return void + */ + public function testDefaultValue(string $productSku): void + { + $this->prepareAttribute(['default_value' => $this->getDefaultAttributeValue()]); + $product = $this->unsetAttributeValueAndValidate($productSku); + $product = $this->productRepository->save($product); + $this->assertEquals($this->getDefaultAttributeValue(), $product->getData($this->getAttributeCode())); + } + + /** + * @dataProvider uniqueAttributeValueProvider + * @param string $firstSku + * @param string $secondSku + * @return void + */ + public function testUniqueAttribute(string $firstSku, string $secondSku): void + { + $this->expectException(Exception::class); + $messageFormat = 'The value of the "%s" attribute isn\'t unique. Set a unique value and try again.'; + $this->expectExceptionMessage( + (string)__(sprintf($messageFormat, $this->getAttribute()->getDefaultFrontendLabel())) + ); + $this->prepareAttribute(['is_unique' => 1]); + $product = $this->setAttributeValueAndValidate($firstSku, $this->getDefaultAttributeValue()); + $this->productRepository->save($product); + $this->setAttributeValueAndValidate($secondSku, $this->getDefaultAttributeValue()); + } + + /** + * Get attribute + * + * @return ProductAttributeInterface + */ + protected function getAttribute(): ProductAttributeInterface + { + if ($this->attribute === null) { + $this->attribute = $this->attributeRepository->get( + ProductAttributeInterface::ENTITY_TYPE_CODE, + $this->getAttributeCode() + ); + } + + return $this->attribute; + } + + /** + * Set attribute value to product and validate the product + * + * @param string $attributeValue + * @param string $productSku + * @return ProductInterface + */ + protected function setAttributeValueAndValidate(string $productSku, string $attributeValue): ProductInterface + { + $product = $this->productRepository->get($productSku); + $product->addData([$this->getAttributeCode() => $attributeValue]); + $product->validate(); + + return $product; + } + + /** + * Unset attribute value of the product and validate the product + * + * @param string $productSku + * @return ProductInterface + */ + private function unsetAttributeValueAndValidate(string $productSku): ProductInterface + { + $product = $this->productRepository->get($productSku); + $product->unsetData($this->getAttributeCode()); + $product->validate(); + + return $product; + } + + /** + * Prepare attribute to test + * + * @param array $data + * @return void + */ + private function prepareAttribute(array $data): void + { + $attribute = $this->getAttribute(); + $attribute->addData($data); + $this->attributeRepository->save($attribute); + } + + /** + * Returns attribute code for current test + * + * @return string + */ + abstract protected function getAttributeCode(): string; + + /** + * Get default value for current attribute + * + * @return string + */ + abstract protected function getDefaultAttributeValue(): string; + + /** + * Products provider for tests + * + * @return array + */ + abstract public function productProvider(): array; + + /** + * Provider for unique attribute tests + * + * @return array + */ + abstract public function uniqueAttributeValueProvider(): array; +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDateTest.php new file mode 100644 index 0000000000000..d30f32087c815 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDateTest.php @@ -0,0 +1,79 @@ +markTestSkipped('Test is blocked by issue MC-28950'); + } + + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'date_attribute'; + } + + /** + * @inheritdoc + */ + protected function getDefaultAttributeValue(): string + { + return $this->getAttribute()->getBackend()->formatDate('11/20/19'); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_date_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php + * @dataProvider uniqueAttributeValueProvider + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod + * @inheritdoc + */ + public function testUniqueAttribute(string $firstSku, string $secondSku): void + { + parent::testUniqueAttribute($firstSku, $secondSku); + } + + /** + * @inheritdoc + */ + public function productProvider(): array + { + return [ + [ + 'product_sku' => 'simple2', + ], + ]; + } + + /** + * @inheritdoc + */ + public function uniqueAttributeValueProvider(): array + { + return [ + [ + 'first_product_sku' => 'simple2', + 'second_product_sku' => 'simple-out-of-stock', + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDropdownTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDropdownTest.php new file mode 100644 index 0000000000000..c1cdd0bab28aa --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeDropdownTest.php @@ -0,0 +1,70 @@ +getAttribute()->getSource()->getOptionId('Option 1'); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php + * @dataProvider uniqueAttributeValueProvider + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod + * @inheritdoc + */ + public function testUniqueAttribute(string $firstSku, string $secondSku): void + { + parent::testUniqueAttribute($firstSku, $secondSku); + } + + /** + * @inheritdoc + */ + public function productProvider(): array + { + return [ + [ + 'product_sku' => 'simple2', + ], + ]; + } + + /** + * @inheritdoc + */ + public function uniqueAttributeValueProvider(): array + { + return [ + [ + 'first_product_sku' => 'simple2', + 'second_product_sku' => 'simple-out-of-stock', + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeMultiSelectTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeMultiSelectTest.php new file mode 100644 index 0000000000000..4ee2b83010bdc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeMultiSelectTest.php @@ -0,0 +1,79 @@ +getAttribute()->getSource()->getOptionId('Option 1'); + } + + /** + * @inheritdoc + * @dataProvider productProvider + */ + public function testDefaultValue(string $productSku): void + { + $this->markTestSkipped('Test is blocked by issue MC-29019'); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/multiselect_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php + * @dataProvider uniqueAttributeValueProvider + * phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod + * @inheritdoc + */ + public function testUniqueAttribute(string $firstSku, string $secondSku): void + { + parent::testUniqueAttribute($firstSku, $secondSku); + } + + /** + * @inheritdoc + */ + public function productProvider(): array + { + return [ + [ + 'product_sku' => 'simple2', + ], + ]; + } + + /** + * @inheritdoc + */ + public function uniqueAttributeValueProvider(): array + { + return [ + [ + 'first_product_sku' => 'simple2', + 'second_product_sku' => 'simple-out-of-stock', + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributePriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributePriceTest.php new file mode 100644 index 0000000000000..5de9d30f71638 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributePriceTest.php @@ -0,0 +1,93 @@ +markTestSkipped('Test is blocked by issue MC-29018'); + parent::testUniqueAttribute($firstSku, $secondSku); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_decimal_attribute.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @return void + */ + public function testNegativeValue(): void + { + $this->expectException(Exception::class); + $this->expectExceptionMessage((string)__('Please enter a number 0 or greater in this field.')); + $this->setAttributeValueAndValidate('simple2', '-1'); + } + + /** + * @dataProvider productProvider + * @param string $productSku + */ + public function testDefaultValue(string $productSku): void + { + // product price attribute does not support default value + } + + /** + * @inheritdoc + */ + public function productProvider(): array + { + return [ + [ + 'product_sku' => 'simple2', + ], + ]; + } + + /** + * @inheritdoc + */ + public function uniqueAttributeValueProvider(): array + { + return [ + [ + 'first_product_sku' => 'simple2', + 'second_product_sku' => 'simple-out-of-stock', + ], + ]; + } + + /** + * @inheritdoc + */ + protected function getAttributeCode(): string + { + return 'decimal_attribute'; + } + + /** + * @inheritdoc + */ + protected function getDefaultAttributeValue(): string + { + return '100'; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextAreaTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextAreaTest.php new file mode 100644 index 0000000000000..61dbf78962c9e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextAreaTest.php @@ -0,0 +1,70 @@ + 'simple2', + ] + ]; + } + + /** + * @inheritdoc + */ + public function uniqueAttributeValueProvider(): array + { + return [ + [ + 'first_product_sku' => 'simple2', + 'second_product_sku' => 'simple-out-of-stock', + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextTest.php new file mode 100644 index 0000000000000..4da09bc1eca5a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeTextTest.php @@ -0,0 +1,70 @@ + 'simple2', + ], + ]; + } + + /** + * @inheritdoc + */ + public function uniqueAttributeValueProvider(): array + { + return [ + [ + 'first_product_sku' => 'simple2', + 'second_product_sku' => 'simple-out-of-stock', + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeYesNoTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeYesNoTest.php new file mode 100644 index 0000000000000..7b966791c7b6e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Save/AttributeYesNoTest.php @@ -0,0 +1,70 @@ + 'simple2', + ], + ]; + } + + /** + * @inheritdoc + */ + public function uniqueAttributeValueProvider(): array + { + return [ + [ + 'first_product_sku' => 'simple2', + 'second_product_sku' => 'simple-out-of-stock', + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/SetTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/SetTest.php new file mode 100644 index 0000000000000..d5a8b694b7718 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/SetTest.php @@ -0,0 +1,276 @@ +objectManager = Bootstrap::getObjectManager(); + $this->setRepository = $this->objectManager->get(AttributeSetRepositoryInterface::class); + $this->attributeRepository = $this->objectManager->create(ProductAttributeRepositoryInterface::class); + $this->config = $this->objectManager->get(Config::class); + $this->defaultSetId = (int)$this->config->getEntityType(Product::ENTITY)->getDefaultAttributeSetId(); + $this->attributeSetResource = $this->objectManager->get(AttributeSetResource::class); + $this->attributeCollectionFactory = $this->objectManager->get(CollectionFactory ::class); + $this->attributeGroupByName = $this->objectManager->get(GetAttributeGroupByName::class); + $this->getEntityIdByAttributeId = $this->objectManager->get(GetEntityIdByAttributeId::class); + } + + /** + * @magentoDataFixture Magento/Eav/_files/attribute_with_options.php + * @dataProvider addAttributeToSetDataProvider + * @param string $groupName + * @param string $attributeCode + * @return void + */ + public function testSaveWithGroupsAndAttributes(string $groupName, string $attributeCode): void + { + $set = $this->setRepository->get($this->defaultSetId); + $attributeGroup = $this->getAttributeGroup($groupName); + $groupId = $attributeGroup ? $attributeGroup->getAttributeGroupId() : 'ynode-1'; + $attributeId = (int)$this->attributeRepository->get($attributeCode)->getAttributeId(); + $additional = [ + 'attributes' => [ + [$attributeId, $groupId, 1], + ], + 'groups' => [ + [$groupId, $groupName, 1], + ], + ]; + $set->organizeData($this->getAttributeSetData($additional)); + $this->attributeSetResource->save($set); + $groupId = $attributeGroup + ? $attributeGroup->getAttributeGroupId() + : $this->getAttributeGroup($groupName)->getAttributeGroupId(); + $this->config->clear(); + $setInfo = $this->attributeSetResource->getSetInfo([$attributeId], $this->defaultSetId); + $expectedInfo = [ + $attributeId => [$this->defaultSetId => ['group_id' => $groupId, 'group_sort' => '1', 'sort' => '1']], + ]; + $this->assertEquals($expectedInfo, $setInfo); + } + + /** + * @return array + */ + public function addAttributeToSetDataProvider(): array + { + return [ + 'add_to_existing_group' => [ + 'group_name' => 'Content', + 'attribute_code' => 'zzz', + ], + 'add_to_new_group' => [ + 'group_name' => 'Test', + 'attribute_code' => 'zzz', + ], + 'move_to_existing_group' => [ + 'group_name' => 'Images', + 'attribute_code' => 'description', + ], + 'move_to_new_group' => [ + 'group_name' => 'Test', + 'attribute_code' => 'description', + ], + ]; + } + + /** + * @return void + */ + public function testSaveWithChangedGroupSorting(): void + { + $set = $this->setRepository->get($this->defaultSetId); + $contentGroupId = $this->getAttributeGroup('Content')->getAttributeGroupId(); + $imagesGroupId = $this->getAttributeGroup('Images')->getAttributeGroupId(); + $additional = [ + 'groups' => [ + [$contentGroupId, 'Content', 2], + [$imagesGroupId, 'Images', 1] + ] + ]; + $set->organizeData($this->getAttributeSetData($additional)); + $this->attributeSetResource->save($set); + $contentGroupSort = $this->getAttributeGroup('Content')->getSortOrder(); + $imagesGroupSort = $this->getAttributeGroup('Images')->getSortOrder(); + $this->assertEquals(2, $contentGroupSort); + $this->assertEquals(1, $imagesGroupSort); + } + + /** + * @return void + */ + public function testSaveWithRemovedGroup(): void + { + $set = $this->setRepository->get($this->defaultSetId); + $designGroupId = $this->getAttributeGroup('Design')->getAttributeGroupId(); + $additional = [ + 'removeGroups' => [$designGroupId], + ]; + $set->organizeData($this->getAttributeSetData($additional)); + $this->attributeSetResource->save($set); + $this->assertNull( + $this->getAttributeGroup('Design'), + 'Group "Design" wan\'t deleted.' + ); + $unusedSetAttributes = $this->getSetExcludedAttributes((int)$set->getAttributeSetId()); + $designAttributeCodes = ['page_layout', 'options_container', 'custom_layout_update']; + $this->assertNotEmpty( + array_intersect($designAttributeCodes, $unusedSetAttributes), + 'Attributes from "Design" group still assigned to attribute set.' + ); + } + + /** + * @return void + */ + public function testSaveWithRemovedAttribute(): void + { + $set = $this->setRepository->get($this->defaultSetId); + $attributeId = (int)$this->attributeRepository->get('meta_description')->getAttributeId(); + $additional = [ + 'not_attributes' => [$this->getEntityAttributeId($this->defaultSetId, $attributeId)], + ]; + $set->organizeData($this->getAttributeSetData($additional)); + $this->attributeSetResource->save($set); + $this->config->clear(); + $setInfo = $this->attributeSetResource->getSetInfo([$attributeId], $this->defaultSetId); + $this->assertEmpty($setInfo[$attributeId]); + $unusedSetAttributes = $this->getSetExcludedAttributes((int)$set->getAttributeSetId()); + $this->assertNotEmpty( + array_intersect(['meta_description'], $unusedSetAttributes), + 'Attribute still assigned to attribute set.' + ); + } + + /** + * Returns attribute set data for saving. + * + * @param array $additional + * @return array + */ + private function getAttributeSetData(array $additional): array + { + $data = [ + 'attributes' => [], + 'groups' => [], + 'not_attributes' => [], + 'removeGroups' => [], + 'attribute_set_name' => 'Default', + ]; + + return array_merge($data, $additional); + } + + /** + * Returns attribute group by name. + * + * @param string $groupName + * @return AttributeGroupInterface|null + */ + private function getAttributeGroup(string $groupName): ?AttributeGroupInterface + { + return $this->attributeGroupByName->execute($this->defaultSetId, $groupName); + } + + /** + * Returns list of unused attributes in attribute set. + * + * @param int $setId + * @return array + */ + private function getSetExcludedAttributes(int $setId): array + { + $collection = $this->attributeCollectionFactory->create() + ->setExcludeSetFilter($setId); + $result = $collection->getColumnValues(AttributeInterface::ATTRIBUTE_CODE); + + return $result; + } + + /** + * Returns entity attribute id. + * + * @param int $setId + * @param int $attributeId + * @return int + */ + private function getEntityAttributeId(int $setId, int $attributeId): int + { + return $this->getEntityIdByAttributeId->execute($setId, $attributeId); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/CopierTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/CopierTest.php new file mode 100644 index 0000000000000..6510e048f0e2d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/CopierTest.php @@ -0,0 +1,47 @@ +get(ProductRepository::class); + $copier = Bootstrap::getObjectManager()->get(Copier::class); + + $product = $productRepository->get($productSKU); + $duplicate = $copier->copy($product); + + $duplicateStoreView = $productRepository->getById($duplicate->getId(), false, Store::DISTRO_STORE_ID); + $productStoreView = $productRepository->get($productSKU, false, Store::DISTRO_STORE_ID); + + $this->assertNotEquals( + $duplicateStoreView->getUrlKey(), + $productStoreView->getUrlKey(), + 'url_key of product duplicate should be different then url_key of the product for the same store view' + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/CreateCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/CreateCustomOptionsTest.php index 94bbcd8bae66b..cab75c7848f06 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/CreateCustomOptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/CreateCustomOptionsTest.php @@ -24,7 +24,6 @@ * Testing option types: "Area", "File", "Drop-down", "Radio-Buttons", * "Checkbox", "Multiple Select", "Date", "Date & Time" and "Time". * - * @magentoAppArea adminhtml * @magentoAppIsolation enabled * @magentoDbIsolation enabled */ @@ -70,11 +69,11 @@ class CreateCustomOptionsTest extends TestCase protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); - $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); - $this->optionRepository = $this->objectManager->create(ProductCustomOptionRepositoryInterface::class); - $this->customOptionFactory = $this->objectManager->create(ProductCustomOptionInterfaceFactory::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->optionRepository = $this->objectManager->get(ProductCustomOptionRepositoryInterface::class); + $this->customOptionFactory = $this->objectManager->get(ProductCustomOptionInterfaceFactory::class); $this->customOptionValueFactory = $this->objectManager - ->create(ProductCustomOptionValuesInterfaceFactory::class); + ->get(ProductCustomOptionValuesInterfaceFactory::class); $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); } @@ -83,7 +82,8 @@ protected function setUp() * * @magentoDataFixture Magento/Catalog/_files/product_with_options.php * @magentoDataFixture Magento/Store/_files/core_second_third_fixturestore.php - * + * @magentoAppArea adminhtml + * @magentoAppIsolation disabled * @magentoConfigFixture default_store catalog/price/scope 1 * @magentoConfigFixture secondstore_store catalog/price/scope 1 */ @@ -120,6 +120,8 @@ public function testSaveOptionPriceByStore(): void * @dataProvider productCustomOptionsTypeTextDataProvider * * @param array $optionData + * + * @magentoDbIsolation enabled */ public function testCreateOptionsWithTypeText(array $optionData): void { @@ -140,6 +142,8 @@ public function testCreateOptionsWithTypeText(array $optionData): void * * @param string $rawExtensions * @param string $expectedExtensions + * + * @magentoDbIsolation enabled */ public function testFileExtensions(string $rawExtensions, string $expectedExtensions): void { @@ -174,6 +178,8 @@ public function testFileExtensions(string $rawExtensions, string $expectedExtens * * @param array $optionData * @param array $optionValueData + * + * @magentoDbIsolation enabled */ public function testCreateOptionsWithTypeSelect(array $optionData, array $optionValueData): void { @@ -199,6 +205,8 @@ public function testCreateOptionsWithTypeSelect(array $optionData, array $option * @dataProvider productCustomOptionsTypeDateDataProvider * * @param array $optionData + * + * @magentoDbIsolation enabled */ public function testCreateOptionsWithTypeDate(array $optionData): void { @@ -217,6 +225,8 @@ public function testCreateOptionsWithTypeDate(array $optionData): void * * @param array $optionData * @param \Exception $expectedErrorObject + * + * @magentoDbIsolation enabled */ public function testCreateOptionWithError(array $optionData, \Exception $expectedErrorObject): void { diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/DeleteCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/DeleteCustomOptionsTest.php new file mode 100644 index 0000000000000..8039d3515e304 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/DeleteCustomOptionsTest.php @@ -0,0 +1,238 @@ +objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->optionRepository = $this->objectManager->get(ProductCustomOptionRepositoryInterface::class); + $this->customOptionFactory = $this->objectManager->get(ProductCustomOptionInterfaceFactory::class); + $this->customOptionValueFactory = $this->objectManager + ->get(ProductCustomOptionValuesInterfaceFactory::class); + + parent::setUp(); + } + + /** + * Test delete product custom options with type "area". + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\Area::getDataForCreateOptions() + * + * @param array $optionData + * @return void + */ + public function testDeleteAreaCustomOption(array $optionData): void + { + $this->deleteAndAssertNotSelectCustomOptions($optionData); + } + + /** + * Test delete product custom options with type "file". + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\File::getDataForCreateOptions() + * + * @param array $optionData + * @return void + */ + public function testDeleteFileCustomOption(array $optionData): void + { + $this->deleteAndAssertNotSelectCustomOptions($optionData); + } + + /** + * Test delete product custom options with type "Date". + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\Date::getDataForCreateOptions() + * + * @param array $optionData + * @return void + */ + public function testDeleteDateCustomOption(array $optionData): void + { + $this->deleteAndAssertNotSelectCustomOptions($optionData); + } + + /** + * Test delete product custom options with type "Date & Time". + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\DateTime::getDataForCreateOptions() + * + * @param array $optionData + * @return void + */ + public function testDeleteDateTimeCustomOption(array $optionData): void + { + $this->deleteAndAssertNotSelectCustomOptions($optionData); + } + + /** + * Test delete product custom options with type "Time". + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\Time::getDataForCreateOptions() + * + * @param array $optionData + * @return void + */ + public function testDeleteTimeCustomOption(array $optionData): void + { + $this->deleteAndAssertNotSelectCustomOptions($optionData); + } + + /** + * Test delete product custom options with type "Drop-down". + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\DropDown::getDataForCreateOptions() + * + * @param array $optionData + * @param array $optionValueData + * @return void + */ + public function testDeleteDropDownCustomOption(array $optionData, array $optionValueData): void + { + $this->deleteAndAssertSelectCustomOptions($optionData, $optionValueData); + } + + /** + * Test delete product custom options with type "Radio Buttons". + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\RadioButtons::getDataForCreateOptions() + * + * @param array $optionData + * @param array $optionValueData + * @return void + */ + public function testDeleteRadioButtonsCustomOption(array $optionData, array $optionValueData): void + { + $this->deleteAndAssertSelectCustomOptions($optionData, $optionValueData); + } + + /** + * Test delete product custom options with type "Checkbox". + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\Checkbox::getDataForCreateOptions() + * + * @param array $optionData + * @param array $optionValueData + * @return void + */ + public function testDeleteCheckboxCustomOption(array $optionData, array $optionValueData): void + { + $this->deleteAndAssertSelectCustomOptions($optionData, $optionValueData); + } + + /** + * Test delete product custom options with type "Multiple Select". + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\MultipleSelect::getDataForCreateOptions() + * + * @param array $optionData + * @param array $optionValueData + * @return void + */ + public function testDeleteMultipleSelectCustomOption(array $optionData, array $optionValueData): void + { + $this->deleteAndAssertSelectCustomOptions($optionData, $optionValueData); + } + + /** + * Delete product custom options which are not from "select" group and assert that option was deleted. + * + * @param array $optionData + * @return void + */ + private function deleteAndAssertNotSelectCustomOptions(array $optionData): void + { + $product = $this->productRepository->get('simple'); + $createdOption = $this->customOptionFactory->create(['data' => $optionData]); + $createdOption->setProductSku($product->getSku()); + $product->setOptions([$createdOption]); + $this->productRepository->save($product); + $this->assertCount(1, $this->optionRepository->getProductOptions($product)); + $product->setOptions([]); + $this->productRepository->save($product); + $this->assertCount(0, $this->optionRepository->getProductOptions($product)); + } + + /** + * Delete product custom options which from "select" group and assert that option was deleted. + * + * @param array $optionData + * @param array $optionValueData + * @return void + */ + private function deleteAndAssertSelectCustomOptions(array $optionData, array $optionValueData): void + { + $optionValue = $this->customOptionValueFactory->create(['data' => $optionValueData]); + $optionData['values'] = [$optionValue]; + $this->deleteAndAssertNotSelectCustomOptions($optionData); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/CreateHandlerTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/CreateHandlerTest.php index 03455bb341cae..2277470e33b12 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/CreateHandlerTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/CreateHandlerTest.php @@ -251,6 +251,32 @@ public function additionalGalleryFieldsProvider(): array ]; } + /** + * @magentoDataFixture Magento/Catalog/_files/product_image_attribute.php + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Catalog/_files/product_image.php + * @return void + */ + public function testExecuteWithCustomMediaAttribute(): void + { + $data = [ + 'media_gallery' => ['images' => ['image' => ['file' => $this->fileName, 'label' => '']]], + 'image' => 'no_selection', + 'small_image' => 'no_selection', + 'swatch_image' => 'no_selection', + 'thumbnail' => 'no_selection', + 'image_attribute' => $this->fileName + ]; + $product = $this->initProduct($data); + $this->createHandler->execute($product); + $mediaAttributeValue = $this->productResource->getAttributeRawValue( + $product->getId(), + ['image_attribute'], + $product->getStoreId() + ); + $this->assertEquals($this->fileName, $mediaAttributeValue); + } + /** * Returns product for testing. * diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/ReadHandlerTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/ReadHandlerTest.php index 35c995ec552a5..89b91ab57e51a 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/ReadHandlerTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/ReadHandlerTest.php @@ -3,75 +3,350 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model\Product\Gallery; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\Data\ProductInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\Catalog\Model\ResourceModel\Product\Gallery; use Magento\Framework\EntityManager\MetadataPool; +use Magento\Store\Api\StoreRepositoryInterface; +use Magento\Store\Model\Store; use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; /** - * Test class for \Magento\Catalog\Model\Product\Gallery\ReadHandler. + * Provide tests for loading gallery images on product load. * - * @magentoDataFixture Magento/Catalog/_files/product_with_image.php + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ReadHandlerTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\TestFramework\ObjectManager + * @var ObjectManager + */ + private $objectManager; + + /** + * @var ReadHandler + */ + private $readHandler; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var ProductInterfaceFactory */ - protected $objectManager; + private $productFactory; /** - * @var \Magento\Catalog\Model\Product\Gallery\ReadHandler + * @var ProductResource */ - protected $readHandler; + private $productResource; + /** + * @var Gallery + */ + private $galleryResource; + + /** + * @var StoreRepositoryInterface + */ + private $storeRepository; + + /** + * @var string + */ + private $productLinkField; + + /** + * @inheritdoc + */ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); - - $this->readHandler = $this->objectManager->create( - \Magento\Catalog\Model\Product\Gallery\ReadHandler::class - ); + $this->readHandler = $this->objectManager->create(ReadHandler::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productFactory = $this->objectManager->get(ProductInterfaceFactory::class); + $this->productResource = $this->objectManager->get(ProductResource::class); + $this->galleryResource = $this->objectManager->create(Gallery::class); + $this->storeRepository = $this->objectManager->create(StoreRepositoryInterface::class); + $this->productLinkField = $this->objectManager->get(MetadataPool::class) + ->getMetadata(ProductInterface::class) + ->getLinkField(); } /** - * @covers \Magento\Catalog\Model\Product\Gallery\ReadHandler::execute + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDbIsolation enabled + * @return void */ - public function testExecute() + public function testExecuteWithoutImages(): void { - /** @var \Magento\Catalog\Model\Product $product */ - $product = $this->objectManager->create( - \Magento\Catalog\Model\Product::class - ); - - /** - * @var $entityMetadata \Magento\Framework\EntityManager\EntityMetadata - */ - $entityMetadata = $this->objectManager - ->get(MetadataPool::class) - ->getMetadata(ProductInterface::class); - $productRepository = $this->objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); - $linkFieldId = $productRepository->get('simple')->getData($entityMetadata->getLinkField()); - - $product->setData($entityMetadata->getLinkField(), $linkFieldId); + $product = $this->getProductInstance(); $this->readHandler->execute($product); - $data = $product->getData(); - $this->assertArrayHasKey('media_gallery', $data); $this->assertArrayHasKey('images', $data['media_gallery']); + $this->assertCount(0, $data['media_gallery']['images']); + } + /** + * @magentoDataFixture Magento/Catalog/_files/product_with_image.php + * @magentoDbIsolation enabled + * @return void + */ + public function testExecuteWithOneImage(): void + { + $product = $this->getProductInstance(); + $this->readHandler->execute($product); + $data = $product->getData(); + $this->assertArrayHasKey('media_gallery', $data); + $this->assertArrayHasKey('images', $data['media_gallery']); $this->assertCount(1, $data['media_gallery']['images']); + $galleryImage = reset($data['media_gallery']['images']); + $this->assertEquals('/m/a/magento_image.jpg', $galleryImage['file']); + $this->assertEquals(1, $galleryImage['position']); + $this->assertEquals('Image Alt Text', $galleryImage['label']); + $this->assertEquals(0, $galleryImage['disabled']); + } + + /** + * @dataProvider executeWithTwoImagesDataProvider + * @magentoDataFixture Magento/Catalog/_files/product_with_multiple_images.php + * @magentoDbIsolation enabled + * @param array $images + * @param array $expectation + * @return void + */ + public function testExecuteWithTwoImages(array $images, array $expectation): void + { + $this->setGalleryImages($this->getProduct(), $images); + $productInstance = $this->getProductInstance(); + $this->readHandler->execute($productInstance); + $data = $productInstance->getData(); + $this->assertArrayHasKey('media_gallery', $data); + $this->assertArrayHasKey('images', $data['media_gallery']); + $this->assertCount(count($expectation), $data['media_gallery']['images']); + $imagesToAssert = []; foreach ($data['media_gallery']['images'] as $valueId => $imageData) { - $this->assertEquals( - 'Image Alt Text', - $imageData['label'] - ); + $imagesToAssert[] = [ + 'file' => $imageData['file'], + 'label' => $imageData['label'], + 'position' => $imageData['position'], + 'disabled' => $imageData['disabled'], + ]; $this->assertEquals( $imageData['value_id'], $valueId ); } + $this->assertEquals($expectation, $imagesToAssert); + } + + /** + * @return array + */ + public function executeWithTwoImagesDataProvider(): array + { + return [ + 'with_two_images' => [ + 'images' => [ + '/m/a/magento_image.jpg' => [], + '/m/a/magento_thumbnail.jpg' => [], + ], + 'expectation' => [ + [ + 'file' => '/m/a/magento_image.jpg', + 'label' => 'Image Alt Text', + 'position' => '1', + 'disabled' => '0', + ], + [ + 'file' => '/m/a/magento_thumbnail.jpg', + 'label' => 'Thumbnail Image', + 'position' => '2', + 'disabled' => '0', + ], + ], + ], + 'with_two_images_and_changed_position_and_one_disabled' => [ + 'images' => [ + '/m/a/magento_image.jpg' => [ + 'position' => '2', + 'disabled' => '0', + ], + '/m/a/magento_thumbnail.jpg' => [ + 'position' => '1', + 'disabled' => '1', + ], + ], + 'expectation' => [ + [ + 'file' => '/m/a/magento_thumbnail.jpg', + 'label' => 'Thumbnail Image', + 'position' => '1', + 'disabled' => '1', + ], + [ + 'file' => '/m/a/magento_image.jpg', + 'label' => 'Image Alt Text', + 'position' => '2', + 'disabled' => '0', + ], + ], + ], + ]; + } + + /** + * @dataProvider executeOnStoreViewDataProvider + * @magentoDataFixture Magento/Catalog/_files/product_with_image.php + * @magentoDataFixture Magento/Store/_files/second_store.php + * @magentoDbIsolation disabled + * @param string $file + * @param string $field + * @param string $value + * @param array $expectation + * @return void + */ + public function testExecuteOnStoreView(string $file, string $field, string $value, array $expectation): void + { + $product = $this->getProduct(); + $secondStoreId = (int)$this->storeRepository->get('fixture_second_store')->getId(); + $this->setGalleryImages($product, [$file => [$field => $value]], (int)$secondStoreId); + $productInstance = $this->getProductInstance($secondStoreId); + $this->readHandler->execute($productInstance); + $data = $productInstance->getData(); + $this->assertArrayHasKey('media_gallery', $data); + $this->assertArrayHasKey('images', $data['media_gallery']); + $image = reset($data['media_gallery']['images']); + $dataToAssert = [ + $field => $image[$field], + $field . '_default' => $image[$field . '_default'], + ]; + $this->assertEquals($expectation, $dataToAssert); + } + + /** + * @return array + */ + public function executeOnStoreViewDataProvider(): array + { + return [ + 'with_store_label' => [ + 'file' => '/m/a/magento_image.jpg', + 'field' => 'label', + 'value' => 'Some store label', + 'expectation' => [ + 'label' => 'Some store label', + 'label_default' => 'Image Alt Text', + ], + ], + 'with_store_position' => [ + 'file' => '/m/a/magento_image.jpg', + 'field' => 'position', + 'value' => '2', + 'expectation' => [ + 'position' => '2', + 'position_default' => '1', + ], + ], + 'with_store_disabled' => [ + 'file' => '/m/a/magento_image.jpg', + 'field' => 'disabled', + 'value' => '1', + 'expectation' => [ + 'disabled' => '1', + 'disabled_default' => '0', + ], + ], + ]; + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + parent::tearDown(); + $this->galleryResource->getConnection() + ->delete($this->galleryResource->getTable(Gallery::GALLERY_TABLE)); + $this->galleryResource->getConnection() + ->delete($this->galleryResource->getTable(Gallery::GALLERY_VALUE_TABLE)); + $this->galleryResource->getConnection() + ->delete($this->galleryResource->getTable(Gallery::GALLERY_VALUE_TO_ENTITY_TABLE)); + } + + /** + * Returns product for testing. + * + * @return ProductInterface + */ + private function getProduct(): ProductInterface + { + return $this->productRepository->get('simple', false, Store::DEFAULT_STORE_ID); + } + + /** + * Updates product gallery images and saves product. + * + * @param ProductInterface $product + * @param array $images + * @param int|null $storeId + * @return void + */ + private function setGalleryImages(ProductInterface $product, array $images, ?int $storeId = null): void + { + $product->setImage(null); + foreach ($images as $file => $data) { + $mediaGalleryData = $product->getData('media_gallery'); + foreach ($mediaGalleryData['images'] as &$image) { + if ($image['file'] == $file) { + foreach ($data as $key => $value) { + $image[$key] = $value; + } + } + } + + $product->setData('media_gallery', $mediaGalleryData); + if (!empty($data['main'])) { + $product->setImage($file); + } + } + + if ($storeId) { + $product->setStoreId($storeId); + } + + $this->productResource->save($product); + } + + /** + * Returns empty product instance. + * + * @param int|null $storeId + * @return ProductInterface + */ + private function getProductInstance(?int $storeId = null): ProductInterface + { + /** @var ProductInterface $product */ + $product = $this->productFactory->create(); + $product->setData( + $this->productLinkField, + $this->getProduct()->getData($this->productLinkField) + ); + + if ($storeId) { + $product->setStoreId($storeId); + } + + return $product; } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/UpdateHandlerTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/UpdateHandlerTest.php index 559dd6d1b747d..fcee06187f374 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/UpdateHandlerTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/UpdateHandlerTest.php @@ -8,18 +8,24 @@ namespace Magento\Catalog\Model\Product\Gallery; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Product; -use Magento\Framework\Filesystem; +use Magento\Catalog\Model\Product\Media\Config; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\Catalog\Model\ResourceModel\Product\Gallery; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\WriteInterface; use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Api\StoreRepositoryInterface; +use Magento\Store\Model\Store; use Magento\TestFramework\Helper\Bootstrap; -use Magento\Framework\Filesystem\Directory\WriteInterface; /** - * Test for \Magento\Catalog\Model\Product\Gallery\UpdateHandler. + * Provides tests for media gallery images update during product save. * - * @magentoDataFixture Magento/Catalog/_files/product_simple.php - * @magentoDataFixture Magento/Catalog/_files/product_image.php + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class UpdateHandlerTest extends \PHPUnit\Framework\TestCase { @@ -33,68 +39,342 @@ class UpdateHandlerTest extends \PHPUnit\Framework\TestCase */ private $updateHandler; + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var StoreRepositoryInterface + */ + private $storeRepository; + + /** + * @var Gallery + */ + private $galleryResource; + + /** + * @var ProductResource + */ + private $productResource; + /** * @var WriteInterface */ private $mediaDirectory; /** - * @var Filesystem + * @var Config */ - private $filesystem; + private $config; /** * @var string */ private $fileName; + /** + * @var int + */ + private $mediaAttributeId; + /** * @inheritdoc */ protected function setUp() { $this->fileName = 'image.txt'; - $this->objectManager = Bootstrap::getObjectManager(); $this->updateHandler = $this->objectManager->create(UpdateHandler::class); - $this->filesystem = $this->objectManager->get(Filesystem::class); - $this->mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->storeRepository = $this->objectManager->create(StoreRepositoryInterface::class); + $this->galleryResource = $this->objectManager->create(Gallery::class); + $this->productResource = $this->objectManager->create(ProductResource::class); + $this->mediaAttributeId = (int)$this->productResource->getAttribute('media_gallery')->getAttributeId(); + $this->config = $this->objectManager->get(Config::class); + $this->mediaDirectory = $this->objectManager->get(Filesystem::class) + ->getDirectoryWrite(DirectoryList::MEDIA); $this->mediaDirectory->writeFile($this->fileName, 'Test'); } /** + * Tests updating image with illegal filename during product save. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Catalog/_files/product_image.php + * * @return void */ public function testExecuteWithIllegalFilename(): void { - $filePath = str_repeat('/..', 2) . DIRECTORY_SEPARATOR . $this->fileName; - - /** @var $product Product */ - $product = Bootstrap::getObjectManager()->create(Product::class); - $product->load(1); + $product = $this->getProduct(); $product->setData( 'media_gallery', [ 'images' => [ 'image' => [ 'value_id' => '100', - 'file' => $filePath, + 'file' => '/../..' . DIRECTORY_SEPARATOR . $this->fileName, 'label' => 'New image', 'removed' => 1, ], ], ] ); - $this->updateHandler->execute($product); $this->assertFileExists($this->mediaDirectory->getAbsolutePath($this->fileName)); } /** + * Tests updating image label, position and disabling during product save. + * + * @magentoDataFixture Magento/Catalog/_files/product_with_image.php + * @magentoDbIsolation enabled * @return void */ - protected function tearDown(): void + public function testExecuteWithOneImage(): void { + $product = $this->getProduct(); + $this->updateProductGalleryImages($product, ['label' => 'New image', 'disabled' => '1']); + $this->updateHandler->execute($product); + $productImages = $this->galleryResource->loadProductGalleryByAttributeId($product, $this->mediaAttributeId); + $updatedImage = reset($productImages); + $this->assertTrue(is_array($updatedImage)); + $this->assertEquals('New image', $updatedImage['label']); + $this->assertEquals('New image', $updatedImage['label_default']); + $this->assertEquals('1', $updatedImage['disabled']); + $this->assertEquals('1', $updatedImage['disabled_default']); + } + + /** + * Tests updating image roles during product save. + * + * @magentoDataFixture Magento/Catalog/_files/product_with_multiple_images.php + * @dataProvider executeWithTwoImagesAndRolesDataProvider + * @magentoDbIsolation enabled + * @param array $roles + * @return void + */ + public function testExecuteWithTwoImagesAndDifferentRoles(array $roles): void + { + $imageRoles = ['image', 'small_image', 'thumbnail', 'swatch_image']; + $product = $this->getProduct(); + $product->addData($roles); + $this->updateHandler->execute($product); + $productsImageData = $this->productResource->getAttributeRawValue( + $product->getId(), + $imageRoles, + $product->getStoreId() + ); + $this->assertEquals($roles, $productsImageData); + } + + /** + * Tests updating image roles during product save on non default store view. + * + * @magentoDataFixture Magento/Catalog/_files/product_with_multiple_images.php + * @magentoDataFixture Magento/Store/_files/second_store.php + * @dataProvider executeWithTwoImagesAndRolesDataProvider + * @magentoDbIsolation enabled + * @param array $roles + * @return void + */ + public function testExecuteWithTwoImagesAndDifferentRolesOnStoreView(array $roles): void + { + $secondStoreId = (int)$this->storeRepository->get('fixture_second_store')->getId(); + $imageRoles = ['image', 'small_image', 'thumbnail', 'swatch_image']; + $product = $this->getProduct($secondStoreId); + $product->addData($roles); + $this->updateHandler->execute($product); + + $storeImages = $this->productResource->getAttributeRawValue( + $product->getId(), + $imageRoles, + $secondStoreId + ); + $this->assertEquals($roles, $storeImages); + + $defaultImages = $this->productResource->getAttributeRawValue( + $product->getId(), + $imageRoles, + Store::DEFAULT_STORE_ID + ); + $this->assertEquals('/m/a/magento_image.jpg', $defaultImages['image']); + $this->assertEquals('/m/a/magento_image.jpg', $defaultImages['small_image']); + $this->assertEquals('/m/a/magento_thumbnail.jpg', $defaultImages['thumbnail']); + $this->assertEquals('/m/a/magento_thumbnail.jpg', $defaultImages['swatch_image']); + } + + /** + * @return array + */ + public function executeWithTwoImagesAndRolesDataProvider(): array + { + return [ + 'unassign_all_roles' => [ + 'roles' => [ + 'image' => 'no_selection', + 'small_image' =>'no_selection', + 'thumbnail' => 'no_selection', + 'swatch_image' => 'no_selection', + ], + ], + 'assign_already_used_role' => [ + 'roles' => [ + 'image' => '/m/a/magento_image.jpg', + 'small_image' => '/m/a/magento_thumbnail.jpg', + 'thumbnail' => '/m/a/magento_thumbnail.jpg', + 'swatch_image' => '/m/a/magento_image.jpg', + ], + ], + ]; + } + + /** + * Tests updating image position during product save. + * + * @magentoDataFixture Magento/Catalog/_files/product_with_multiple_images.php + * @magentoDbIsolation enabled + * @return void + */ + public function testExecuteWithTwoImagesAndChangedPosition(): void + { + $positionMap = [ + '/m/a/magento_image.jpg' => '2', + '/m/a/magento_thumbnail.jpg' => '1', + ]; + $product = $this->getProduct(); + $images = $product->getData('media_gallery')['images']; + foreach ($images as &$image) { + $image['position'] = $positionMap[$image['file']]; + } + $product->setData('store_id', Store::DEFAULT_STORE_ID); + $product->setData('media_gallery', ['images' => $images]); + $this->updateHandler->execute($product); + $productImages = $this->galleryResource->loadProductGalleryByAttributeId($product, $this->mediaAttributeId); + foreach ($productImages as $updatedImage) { + $this->assertEquals($positionMap[$updatedImage['file']], $updatedImage['position']); + $this->assertEquals($positionMap[$updatedImage['file']], $updatedImage['position_default']); + } + } + + /** + * Tests image remove during product save. + * + * @magentoDataFixture Magento/Catalog/_files/product_with_image.php + * @magentoDbIsolation enabled + * @return void + */ + public function testExecuteWithImageToDelete(): void + { + $product = $this->getProduct(); + $image = $product->getImage(); + $this->updateProductGalleryImages($product, ['removed' => '1']); + $this->updateHandler->execute($product); + $productImages = $this->galleryResource->loadProductGalleryByAttributeId($product, $this->mediaAttributeId); + $this->assertCount(0, $productImages); + $this->assertFileNotExists( + $this->mediaDirectory->getAbsolutePath($this->config->getBaseMediaPath() . $image) + ); + $defaultImages = $this->productResource->getAttributeRawValue( + $product->getId(), + ['image', 'small_image', 'thumbnail', 'swatch_image'], + Store::DEFAULT_STORE_ID + ); + $this->assertEquals('no_selection', $defaultImages['image']); + $this->assertEquals('no_selection', $defaultImages['small_image']); + $this->assertEquals('no_selection', $defaultImages['thumbnail']); + } + + /** + * Tests updating images data during product save on non default store view. + * + * @magentoDataFixture Magento/Catalog/_files/product_with_multiple_images.php + * @magentoDataFixture Magento/Store/_files/second_store.php + * @magentoDbIsolation enabled + * @return void + */ + public function testExecuteWithTwoImagesOnStoreView(): void + { + $secondStoreId = (int)$this->storeRepository->get('fixture_second_store')->getId(); + $storeImages = [ + '/m/a/magento_image.jpg' => [ + 'label' => 'Store image', + 'label_default' => 'Image Alt Text', + 'disabled' => '1', + 'disabled_default' => '0', + 'position' => '2', + 'position_default' => '1', + ], + '/m/a/magento_thumbnail.jpg' => [ + 'label' => 'Store thumbnail', + 'label_default' => 'Thumbnail Image', + 'disabled' => '0', + 'disabled_default' => '0', + 'position' => '1', + 'position_default' => '2', + ], + ]; + $product = $this->getProduct($secondStoreId); + $images = $product->getData('media_gallery')['images']; + foreach ($images as &$image) { + $image['label'] = $storeImages[$image['file']]['label']; + $image['disabled'] = $storeImages[$image['file']]['disabled']; + $image['position'] = $storeImages[$image['file']]['position']; + } + $product->setData('media_gallery', ['images' => $images]); + $this->updateHandler->execute($product); + $productImages = $this->galleryResource->loadProductGalleryByAttributeId($product, $this->mediaAttributeId); + foreach ($productImages as $image) { + $imageToAssert = [ + 'label' => $image['label'], + 'label_default' =>$image['label_default'], + 'disabled' =>$image['disabled'], + 'disabled_default' => $image['disabled_default'], + 'position' => $image['position'], + 'position_default' => $image['position_default'], + ]; + $this->assertEquals($storeImages[$image['file']], $imageToAssert); + } + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + parent::tearDown(); $this->mediaDirectory->getDriver()->deleteFile($this->mediaDirectory->getAbsolutePath($this->fileName)); + $this->galleryResource->getConnection() + ->delete($this->galleryResource->getTable(Gallery::GALLERY_TABLE)); + $this->galleryResource->getConnection() + ->delete($this->galleryResource->getTable(Gallery::GALLERY_VALUE_TABLE)); + $this->galleryResource->getConnection() + ->delete($this->galleryResource->getTable(Gallery::GALLERY_VALUE_TO_ENTITY_TABLE)); + } + + /** + * Returns current product. + * + * @param int|null $storeId + * @return ProductInterface|Product + */ + private function getProduct(?int $storeId = null): ProductInterface + { + return $this->productRepository->get('simple', false, $storeId, true); + } + + /** + * @param ProductInterface|Product $product + * @param array $imageData + * @return void + */ + private function updateProductGalleryImages(ProductInterface $product, array $imageData): void + { + $images = $product->getData('media_gallery')['images']; + $image = reset($images) ?: []; + $product->setData('store_id', Store::DEFAULT_STORE_ID); + $product->setData('media_gallery', ['images' => ['image' => array_merge($image, $imageData)]]); } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/LinksTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/LinksTest.php new file mode 100644 index 0000000000000..b8be34f460dcb --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/LinksTest.php @@ -0,0 +1,267 @@ + '2', + 'sku' => 'custom-design-simple-product', + 'position' => 1, + ], + [ + 'id' => '10', + 'sku' => 'simple1', + 'position' => 2, + ], + ]; + + /** @var array */ + private $existingProducts = [ + [ + 'id' => '10', + 'sku' => 'simple1', + 'position' => 1, + ], + [ + 'id' => '11', + 'sku' => 'simple2', + 'position' => 2, + ], + [ + 'id' => '12', + 'sku' => 'simple3', + 'position' => 3, + ], + ]; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var ObjectManager */ + private $objectManager; + + /** @var ProductResource */ + private $productResource; + + /** @var ProductLinkInterfaceFactory */ + private $productLinkInterfaceFactory; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->productResource = $this->objectManager->create(ProductResource::class); + $this->productLinkInterfaceFactory = $this->objectManager->create(ProductLinkInterfaceFactory::class); + } + + /** + * Test edit and remove simple related, up-sells, cross-sells products in an existing product + * + * @dataProvider editDeleteRelatedUpSellCrossSellProductsProvider + * @magentoDataFixture Magento/Catalog/_files/products.php + * @magentoDataFixture Magento/Catalog/_files/multiple_products.php + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + * @param array $data + * @return void + */ + public function testEditRemoveRelatedUpSellCrossSellProducts(array $data): void + { + /** @var ProductInterface|Product $product */ + $product = $this->productRepository->get('simple'); + $this->setCustomProductLinks($product, $this->getProductData($data['defaultLinks'])); + $this->productRepository->save($product); + + $productData = $this->getProductData($data['productLinks']); + $this->setCustomProductLinks($product, $productData); + $this->productResource->save($product); + + $product = $this->productRepository->get('simple'); + $expectedLinks = isset($data['expectedProductLinks']) + ? $this->getProductData($data['expectedProductLinks']) + : $productData; + + $this->assertEquals( + $expectedLinks, + $this->getActualLinks($product), + "Expected linked products do not match actual linked products!" + ); + } + + /** + * Provide test data for testEditDeleteRelatedUpSellCrossSellProducts(). + * + * @return array + */ + public function editDeleteRelatedUpSellCrossSellProductsProvider(): array + { + return [ + 'update' => [ + 'data' => [ + 'defaultLinks' => $this->defaultDataFixture, + 'productLinks' => $this->existingProducts, + ], + ], + 'delete' => [ + 'data' => [ + 'defaultLinks' => $this->defaultDataFixture, + 'productLinks' => [] + ], + ], + 'same' => [ + 'data' => [ + 'defaultLinks' => $this->existingProducts, + 'productLinks' => $this->existingProducts, + ], + ], + 'change_position' => [ + 'data' => [ + 'defaultLinks' => $this->existingProducts, + 'productLinks' => array_replace_recursive( + $this->existingProducts, + [ + ['position' => 4], + ['position' => 5], + ['position' => 6], + ] + ), + ], + ], + 'without_position' => [ + 'data' => [ + 'defaultLinks' => $this->defaultDataFixture, + 'productLinks' => array_replace_recursive( + $this->existingProducts, + [ + ['position' => null], + ['position' => null], + ['position' => null], + ] + ), + 'expectedProductLinks' => array_replace_recursive( + $this->existingProducts, + [ + ['position' => 1], + ['position' => 2], + ['position' => 3], + ] + ), + ], + ], + ]; + } + + /** + * Create an array of products by link type that will be linked + * + * @param array $productFixture + * @return array + */ + private function getProductData(array $productFixture): array + { + $productData = []; + foreach ($this->linkTypes as $linkType) { + $productData[$linkType] = []; + foreach ($productFixture as $data) { + $productData[$linkType][] = $data; + } + } + + return $productData; + } + + /** + * Link related, up-sells, cross-sells products received from the array + * + * @param ProductInterface|Product $product + * @param array $productData + * @return void + */ + private function setCustomProductLinks(ProductInterface $product, array $productData): void + { + $productLinks = []; + foreach ($productData as $linkType => $links) { + foreach ($links as $data) { + /** @var ProductLinkInterface|Link $productLink */ + $productLink = $this->productLinkInterfaceFactory->create(); + $productLink->setSku('simple'); + $productLink->setLinkedProductSku($data['sku']); + if (isset($data['position'])) { + $productLink->setPosition($data['position']); + } + $productLink->setLinkType($linkType); + $productLinks[] = $productLink; + } + } + $product->setProductLinks($productLinks); + } + + /** + * Get an array of received related, up-sells, cross-sells products + * + * @param ProductInterface|Product $product + * @return array + */ + private function getActualLinks(ProductInterface $product): array + { + $actualLinks = []; + foreach ($this->linkTypes as $linkType) { + $products = []; + $actualLinks[$linkType] = []; + switch ($linkType) { + case 'upsell': + $products = $product->getUpSellProducts(); + break; + case 'crosssell': + $products = $product->getCrossSellProducts(); + break; + case 'related': + $products = $product->getRelatedProducts(); + break; + } + /** @var ProductInterface|Product $productItem */ + foreach ($products as $productItem) { + $actualLinks[$linkType][] = [ + 'id' => $productItem->getId(), + 'sku' => $productItem->getSku(), + 'position' => $productItem->getPosition(), + ]; + } + } + + return $actualLinks; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/TextTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/TextTest.php new file mode 100644 index 0000000000000..74082c339bd79 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Option/Type/TextTest.php @@ -0,0 +1,79 @@ + 11, 'type' => 'area']; + + /** + * @var Text + */ + protected $optionText; + + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * {@inheritDoc} + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->optionText = $this->objectManager->create(Text::class); + } + + /** + * Check if newline symbols are normalized in option value + * + * @dataProvider optionValueDataProvider + * @param array $productOptionData + * @param string $optionValue + * @param string $expectedOptionValue + */ + public function testNormalizeNewlineSymbols( + array $productOptionData, + string $optionValue, + string $expectedOptionValue + ) { + $productOption = $this->objectManager->create( + Option::class, + ['data' => $productOptionData] + ); + + $this->optionText->setOption($productOption); + $this->optionText->setUserValue($optionValue); + $this->optionText->validateUserValue([]); + + $this->assertSame($expectedOptionValue, $this->optionText->getUserValue()); + } + + /** + * Data provider for testNormalizeNewlineSymbols + * + * @return array + */ + public function optionValueDataProvider() + { + return [ + [self::STUB_OPTION_DATA, 'string string', 'string string'], + [self::STUB_OPTION_DATA, "string \r\n string", "string \n string"], + [self::STUB_OPTION_DATA, "string \n\r string", "string \n string"], + [self::STUB_OPTION_DATA, "string \r string", "string \n string"] + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceTest.php index 8d3a119873afb..fe7207d310345 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Type/PriceTest.php @@ -3,105 +3,214 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Option; +use Magento\Customer\Model\Session; +use Magento\Framework\DataObject; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; /** + * Simple product price test. + * * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDbIsolation enabled */ -class PriceTest extends \PHPUnit\Framework\TestCase +class PriceTest extends TestCase { /** - * @var \Magento\Catalog\Model\Product\Type\Price + * @var ObjectManager + */ + private $objectManager; + + /** + * @var Price */ - protected $_model; + private $productPrice; - protected function setUp() + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var Session + */ + private $customerSession; + + /** + * @inheritdoc + */ + protected function setUp(): void { - $this->_model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\Product\Type\Price::class - ); + $this->objectManager = Bootstrap::getObjectManager(); + $this->productPrice = $this->objectManager->create(Price::class); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->customerSession = $this->objectManager->get(Session::class); } - public function testGetPrice() + /** + * Assert that for logged user product price equal to price from catalog rule. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/CatalogRule/_files/catalog_rule_6_off_logged_user.php + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @magentoDbIsolation disabled + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * + * @return void + */ + public function testPriceByRuleForLoggedUser(): void { - $this->assertEquals('test', $this->_model->getPrice(new \Magento\Framework\DataObject(['price' => 'test']))); + $product = $this->productRepository->get('simple'); + $this->assertEquals(10, $this->productPrice->getFinalPrice(1, $product)); + $this->customerSession->setCustomerId(1); + try { + $this->assertEquals(4, $this->productPrice->getFinalPrice(1, $product)); + } finally { + $this->customerSession->setCustomerId(null); + } } - public function testGetFinalPrice() + /** + * Assert price for different customer groups. + * + * @magentoDataFixture Magento/Catalog/_files/simple_product_with_tier_price_for_logged_user.php + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @magentoAppIsolation enabled + * + * @return void + */ + public function testTierPriceWithDifferentCustomerGroups(): void { - $repository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\ProductRepository::class - ); - $product = $repository->get('simple'); - // fixture + $product = $this->productRepository->get('simple'); + $this->assertEquals(8, $this->productPrice->getFinalPrice(2, $product)); + $this->assertEquals(5, $this->productPrice->getFinalPrice(3, $product)); + $this->customerSession->setCustomerId(1); + try { + $this->assertEquals(1, $this->productPrice->getFinalPrice(3, $product)); + } finally { + $this->customerSession->setCustomerId(null); + } + } + + /** + * Get price from custom object. + * + * @return void + */ + public function testGetPrice(): void + { + $objectWithPrice = $this->objectManager->create(DataObject::class, ['data' => ['price' => 'test']]); + $this->assertEquals('test', $this->productPrice->getPrice($objectWithPrice)); + } + + /** + * Get product final price for different product count. + * + * @return void + */ + public function testGetFinalPrice(): void + { + $product = $this->productRepository->get('simple'); // regular & tier prices - $this->assertEquals(10.0, $this->_model->getFinalPrice(1, $product)); - $this->assertEquals(8.0, $this->_model->getFinalPrice(2, $product)); - $this->assertEquals(5.0, $this->_model->getFinalPrice(5, $product)); + $this->assertEquals(10.0, $this->productPrice->getFinalPrice(1, $product)); + $this->assertEquals(8.0, $this->productPrice->getFinalPrice(2, $product)); + $this->assertEquals(5.0, $this->productPrice->getFinalPrice(5, $product)); // with options $buyRequest = $this->prepareBuyRequest($product); $product->getTypeInstance()->prepareForCart($buyRequest, $product); //product price + options price(10+1+2+3+3) - $this->assertEquals(19.0, $this->_model->getFinalPrice(1, $product)); + $this->assertEquals(19.0, $this->productPrice->getFinalPrice(1, $product)); //product tier price + options price(5+1+2+3+3) - $this->assertEquals(14.0, $this->_model->getFinalPrice(5, $product)); + $this->assertEquals(14.0, $this->productPrice->getFinalPrice(5, $product)); } - public function testGetFormatedPrice() + /** + * Assert that formated price is correct. + * + * @return void + */ + public function testGetFormatedPrice(): void { - $repository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\ProductRepository::class - ); - $product = $repository->get('simple'); - // fixture - $this->assertEquals('$10.00', $this->_model->getFormatedPrice($product)); + $product = $this->productRepository->get('simple'); + $this->assertEquals('$10.00', $this->productPrice->getFormatedPrice($product)); } - public function testCalculatePrice() + /** + * Test calculate price by date. + * + * @return void + */ + public function testCalculatePrice(): void { - $this->assertEquals(10, $this->_model->calculatePrice(10, 8, '1970-12-12 23:59:59', '1971-01-01 01:01:01')); - $this->assertEquals(8, $this->_model->calculatePrice(10, 8, '1970-12-12 23:59:59', '2034-01-01 01:01:01')); + $this->assertEquals( + 10, + $this->productPrice->calculatePrice(10, 8, '1970-12-12 23:59:59', '1971-01-01 01:01:01') + ); + $this->assertEquals( + 8, + $this->productPrice->calculatePrice(10, 8, '1970-12-12 23:59:59', '2034-01-01 01:01:01') + ); } - public function testCalculateSpecialPrice() + /** + * Test calculate price by date. + * + * @return void + */ + public function testCalculateSpecialPrice(): void { $this->assertEquals( 10, - $this->_model->calculateSpecialPrice(10, 8, '1970-12-12 23:59:59', '1971-01-01 01:01:01') + $this->productPrice->calculateSpecialPrice(10, 8, '1970-12-12 23:59:59', '1971-01-01 01:01:01') ); $this->assertEquals( 8, - $this->_model->calculateSpecialPrice(10, 8, '1970-12-12 23:59:59', '2034-01-01 01:01:01') + $this->productPrice->calculateSpecialPrice(10, 8, '1970-12-12 23:59:59', '2034-01-01 01:01:01') ); } - public function testIsTierPriceFixed() + /** + * Assert that product tier price is fixed. + * + * @return void + */ + public function testIsTierPriceFixed(): void { - $this->assertTrue($this->_model->isTierPriceFixed()); + $this->assertTrue($this->productPrice->isTierPriceFixed()); } /** - * Build buy request based on product custom options + * Build buy request based on product custom options. * * @param Product $product - * @return \Magento\Framework\DataObject + * @return DataObject */ - private function prepareBuyRequest(Product $product) + private function prepareBuyRequest(Product $product): DataObject { $options = []; - /** @var $option \Magento\Catalog\Model\Product\Option */ + /** @var Option $option */ foreach ($product->getOptions() as $option) { switch ($option->getGroupByType()) { - case \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_GROUP_DATE: + case ProductCustomOptionInterface::OPTION_GROUP_DATE: $value = ['year' => 2013, 'month' => 8, 'day' => 9, 'hour' => 13, 'minute' => 35]; break; - case \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_GROUP_SELECT: + case ProductCustomOptionInterface::OPTION_GROUP_SELECT: $value = key($option->getValues()); break; default: @@ -111,6 +220,6 @@ private function prepareBuyRequest(Product $product) $options[$option->getId()] = $value; } - return new \Magento\Framework\DataObject(['qty' => 1, 'options' => $options]); + return $this->objectManager->create(DataObject::class, ['data' => ['qty' => 1, 'options' => $options]]); } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UpdateCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UpdateCustomOptionsTest.php new file mode 100644 index 0000000000000..c07303f03e4f1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UpdateCustomOptionsTest.php @@ -0,0 +1,465 @@ +objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->optionRepository = $this->objectManager->get(ProductCustomOptionRepositoryInterface::class); + $this->customOptionFactory = $this->objectManager->get(ProductCustomOptionInterfaceFactory::class); + $this->customOptionValueFactory = $this->objectManager + ->get(ProductCustomOptionValuesInterfaceFactory::class); + $this->storeManager = $this->objectManager->get(StoreManagerInterface::class); + $this->currentStoreId = $this->storeManager->getStore()->getId(); + $adminStoreId = $this->storeManager->getStore('admin')->getId(); + $this->storeManager->setCurrentStore($adminStoreId); + + parent::setUp(); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->storeManager->setCurrentStore($this->currentStoreId); + + parent::tearDown(); + } + + /** + * Test update product custom options with type "area". + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\Area::getDataForUpdateOptions + * + * @param array $optionData + * @param array $updateData + * @return void + */ + public function testUpdateAreaCustomOption(array $optionData, array $updateData): void + { + $this->updateAndAssertNotSelectCustomOptions($optionData, $updateData); + } + + /** + * Test update product custom options with type "file". + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\File::getDataForUpdateOptions + * + * @param array $optionData + * @param array $updateData + * @return void + */ + public function testUpdateFileCustomOption(array $optionData, array $updateData): void + { + $this->updateAndAssertNotSelectCustomOptions($optionData, $updateData); + } + + /** + * Test update product custom options with type "Date". + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\Date::getDataForUpdateOptions + * + * @param array $optionData + * @param array $updateData + * @return void + */ + public function testUpdateDateCustomOption(array $optionData, array $updateData): void + { + $this->updateAndAssertNotSelectCustomOptions($optionData, $updateData); + } + + /** + * Test update product custom options with type "Date & Time". + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\DateTime::getDataForUpdateOptions + * + * @param array $optionData + * @param array $updateData + * @return void + */ + public function testUpdateDateTimeCustomOption(array $optionData, array $updateData): void + { + $this->updateAndAssertNotSelectCustomOptions($optionData, $updateData); + } + + /** + * Test update product custom options with type "Time". + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\Time::getDataForUpdateOptions + * + * @param array $optionData + * @param array $updateData + * @return void + */ + public function testUpdateTimeCustomOption(array $optionData, array $updateData): void + { + $this->updateAndAssertNotSelectCustomOptions($optionData, $updateData); + } + + /** + * Test update product custom options with type "Drop-down". + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\DropDown::getDataForUpdateOptions + * + * @param array $optionData + * @param array $optionValueData + * @param array $updateOptionData + * @param array $updateOptionValueData + * @return void + */ + public function testUpdateDropDownCustomOption( + array $optionData, + array $optionValueData, + array $updateOptionData, + array $updateOptionValueData + ): void { + $this->updateAndAssertSelectCustomOptions( + $optionData, + $optionValueData, + $updateOptionData, + $updateOptionValueData + ); + } + + /** + * Test update product custom options with type "Radio Buttons". + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\RadioButtons::getDataForUpdateOptions + * + * @param array $optionData + * @param array $optionValueData + * @param array $updateOptionData + * @param array $updateOptionValueData + * @return void + */ + public function testUpdateRadioButtonsCustomOption( + array $optionData, + array $optionValueData, + array $updateOptionData, + array $updateOptionValueData + ): void { + $this->updateAndAssertSelectCustomOptions( + $optionData, + $optionValueData, + $updateOptionData, + $updateOptionValueData + ); + } + + /** + * Test update product custom options with type "Checkbox". + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\Checkbox::getDataForUpdateOptions + * + * @param array $optionData + * @param array $optionValueData + * @param array $updateOptionData + * @param array $updateOptionValueData + * @return void + */ + public function testUpdateCheckboxCustomOption( + array $optionData, + array $optionValueData, + array $updateOptionData, + array $updateOptionValueData + ): void { + $this->updateAndAssertSelectCustomOptions( + $optionData, + $optionValueData, + $updateOptionData, + $updateOptionValueData + ); + } + + /** + * Test update product custom options with type "Multiple Select". + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * + * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\MultipleSelect::getDataForUpdateOptions + * + * @param array $optionData + * @param array $optionValueData + * @param array $updateOptionData + * @param array $updateOptionValueData + * @return void + */ + public function testUpdateMultipleSelectCustomOption( + array $optionData, + array $optionValueData, + array $updateOptionData, + array $updateOptionValueData + ): void { + $this->updateAndAssertSelectCustomOptions( + $optionData, + $optionValueData, + $updateOptionData, + $updateOptionValueData + ); + } + + /** + * Update product custom options which are not from "select" group and assert updated data. + * + * @param array $optionData + * @param array $updateData + * @return void + */ + private function updateAndAssertNotSelectCustomOptions(array $optionData, array $updateData): void + { + $productSku = 'simple'; + $createdOption = $this->createCustomOption($optionData, $productSku); + $updatedOption = $this->updateOptionWithValues($updateData, $productSku); + + foreach ($updateData as $methodKey => $newValue) { + $this->assertEquals($newValue, $updatedOption->getDataUsingMethod($methodKey)); + $this->assertNotEquals( + $createdOption->getDataUsingMethod($methodKey), + $updatedOption->getDataUsingMethod($methodKey) + ); + } + + $this->assertEquals($createdOption->getOptionId(), $updatedOption->getOptionId()); + } + + /** + * Update product custom options which from "select" group and assert updated data. + * + * @param array $optionData + * @param array $optionValueData + * @param array $updateOptionData + * @param array $updateOptionValueData + * @return void + */ + private function updateAndAssertSelectCustomOptions( + array $optionData, + array $optionValueData, + array $updateOptionData, + array $updateOptionValueData + ): void { + $productSku = 'simple'; + $createdOption = $this->createCustomOptionWithValue($optionData, $optionValueData, $productSku); + $createdOptionValue = $this->getOptionValue($createdOption); + $updatedOption = $this->updateOptionAndValueWithValues($updateOptionData, $updateOptionValueData, $productSku); + $updatedOptionValue = $this->getOptionValue($updatedOption); + + foreach ($updateOptionData as $methodKey => $newValue) { + $this->assertEquals($newValue, $updatedOption->getDataUsingMethod($methodKey)); + $this->assertNotEquals( + $createdOption->getDataUsingMethod($methodKey), + $updatedOption->getDataUsingMethod($methodKey) + ); + } + + foreach ($updateOptionValueData as $methodKey => $newValue) { + $methodName = str_replace('_', '', ucwords($methodKey, '_')); + $this->assertEquals($newValue, $updatedOptionValue->{'get' . $methodName}()); + $this->assertNotEquals( + $createdOptionValue->getDataUsingMethod($methodKey), + $updatedOptionValue->getDataUsingMethod($methodKey) + ); + } + + $this->assertEquals($createdOption->getOptionId(), $updatedOption->getOptionId()); + } + + /** + * Create custom option and save product with created option. + * + * @param array $optionData + * @param string $productSku + * @return ProductCustomOptionInterface|Option + */ + private function createCustomOption(array $optionData, string $productSku): ProductCustomOptionInterface + { + $product = $this->productRepository->get($productSku); + $createdOption = $this->customOptionFactory->create(['data' => $optionData]); + $createdOption->setProductSku($product->getSku()); + $product->setOptions([$createdOption]); + $this->productRepository->save($product); + $productCustomOptions = $this->optionRepository->getProductOptions($product); + $option = reset($productCustomOptions); + + return $option; + } + + /** + * Create custom option from select group and save product with created option. + * + * @param array $optionData + * @param array $optionValueData + * @param string $productSku + * @return ProductCustomOptionInterface|Option + */ + private function createCustomOptionWithValue( + array $optionData, + array $optionValueData, + string $productSku + ): ProductCustomOptionInterface { + $optionValue = $this->customOptionValueFactory->create(['data' => $optionValueData]); + $optionData['values'] = [$optionValue]; + + return $this->createCustomOption($optionData, $productSku); + } + + /** + * Update product option with values. + * + * @param array $updateData + * @param string $productSku + * @return ProductCustomOptionInterface|Option + */ + private function updateOptionWithValues(array $updateData, string $productSku): ProductCustomOptionInterface + { + $product = $this->productRepository->get($productSku); + $currentOption = $this->getProductOptionByProductSku($product->getSku()); + $currentOption->setProductSku($product->getSku()); + foreach ($updateData as $methodKey => $newValue) { + $currentOption->setDataUsingMethod($methodKey, $newValue); + } + $product->setOptions([$currentOption]); + $this->productRepository->save($product); + + return $this->getProductOptionByProductSku($product->getSku()); + } + + /** + * Update product option with values. + * + * @param array $optionUpdateData + * @param array $optionValueUpdateData + * @param string $productSku + * @return ProductCustomOptionInterface|Option + */ + private function updateOptionAndValueWithValues( + array $optionUpdateData, + array $optionValueUpdateData, + string $productSku + ): ProductCustomOptionInterface { + $product = $this->productRepository->get($productSku); + $currentOption = $this->getProductOptionByProductSku($product->getSku()); + $currentOption->setProductSku($product->getSku()); + $optionValue = $this->getOptionValue($currentOption); + foreach ($optionUpdateData as $methodKey => $newValue) { + $currentOption->setDataUsingMethod($methodKey, $newValue); + } + foreach ($optionValueUpdateData as $methodKey => $newValue) { + $optionValue->setDataUsingMethod($methodKey, $newValue); + } + $currentOption->setValues([$optionValue]); + $product->setOptions([$currentOption]); + $this->productRepository->save($product); + + return $this->getProductOptionByProductSku($product->getSku()); + } + + /** + * Get product option by product sku. + * + * @param string $productSku + * @return ProductCustomOptionInterface|Option + */ + private function getProductOptionByProductSku(string $productSku): ProductCustomOptionInterface + { + $product = $this->productRepository->get($productSku); + $currentOptions = $this->optionRepository->getProductOptions($product); + + return reset($currentOptions); + } + + /** + * Return custom option value. + * + * @param ProductCustomOptionInterface $customOption + * @return ProductCustomOptionValuesInterface|Value + */ + private function getOptionValue(ProductCustomOptionInterface $customOption): ProductCustomOptionValuesInterface + { + $optionValues = $customOption->getValues(); + + return reset($optionValues); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UpdateProductWebsiteTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UpdateProductWebsiteTest.php new file mode 100644 index 0000000000000..646e661419292 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UpdateProductWebsiteTest.php @@ -0,0 +1,106 @@ +objectManager = Bootstrap::getObjectManager(); + $this->productWebsiteLink = $this->objectManager->get(Link::class); + $this->websiteRepository = $this->objectManager->get(WebsiteRepositoryInterface::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + } + + /** + * @magentoDataFixture Magento/Store/_files/website.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @return void + */ + public function testAssignProductToWebsite(): void + { + $defaultWebsiteId = $this->websiteRepository->get('base')->getId(); + $secondWebsiteId = $this->websiteRepository->get('test')->getId(); + $product = $this->updateProductWebsites('simple2', [$defaultWebsiteId, $secondWebsiteId]); + $this->assertEquals( + [$defaultWebsiteId, $secondWebsiteId], + $this->productWebsiteLink->getWebsiteIdsByProductId($product->getId()) + ); + } + + /** + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/Catalog/_files/product_two_websites.php + * @return void + */ + public function testUnassignProductFromWebsite(): void + { + $secondWebsiteId = $this->websiteRepository->get('test')->getId(); + $product = $this->updateProductWebsites('simple-on-two-websites', [$secondWebsiteId]); + $this->assertEquals([$secondWebsiteId], $this->productWebsiteLink->getWebsiteIdsByProductId($product->getId())); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @return void + */ + public function testAssignNonExistingWebsite(): void + { + $messageFormat = 'The website with id %s that was requested wasn\'t found. Verify the website and try again.'; + $nonExistingWebsiteId = 921564; + $this->expectException(NoSuchEntityException::class); + $this->expectExceptionMessage((string)__(sprintf($messageFormat, $nonExistingWebsiteId))); + $this->updateProductWebsites('simple2', [$nonExistingWebsiteId]); + } + + /** + * Update product websites attribute + * + * @param string $productSku + * @param array $websiteIds + * @return ProductInterface + */ + private function updateProductWebsites(string $productSku, array $websiteIds): ProductInterface + { + $product = $this->productRepository->get($productSku); + $product->setWebsiteIds($websiteIds); + + return $this->productRepository->save($product); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UrlTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UrlTest.php index e0860897fcc24..ff2979150954e 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UrlTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UrlTest.php @@ -87,6 +87,9 @@ public function getUrlsWithSecondStoreProvider() ]; } + /** + * @magentoDbIsolation disabled + */ public function testGetProductUrl() { $repository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( @@ -121,6 +124,7 @@ public function testGetUrlPath() } /** + * @magentoDbIsolation disabled * @magentoAppArea frontend */ public function testGetUrl() @@ -142,6 +146,7 @@ public function testGetUrl() * Check that rearranging product url rewrites do not influence on whether to use category in product links * * @magentoConfigFixture current_store catalog/seo/product_use_categories 0 + * @magentoDbIsolation disabled */ public function testGetProductUrlWithRearrangedUrlRewrites() { diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductLink/ProductLinkQueryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductLink/ProductLinkQueryTest.php new file mode 100644 index 0000000000000..8509174e127e7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductLink/ProductLinkQueryTest.php @@ -0,0 +1,145 @@ +query = $objectManager->get(ProductLinkQuery::class); + $this->productRepo = $objectManager->get(ProductRepository::class); + $this->criteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); + } + + /** + * Generate search criteria. + * + * @param \Magento\Catalog\Model\Product[] $products + * @return ListCriteriaInterface[] + */ + private function generateCriteriaList(array $products): array + { + $typesList = ['related', 'crosssell', 'upsell']; + /** @var ListCriteriaInterface[] $criteriaList */ + $criteriaList = []; + foreach ($products as $product) { + $sku = $product->getSku(); + $typesFilter = [$typesList[rand(0, 2)], $typesList[rand(0, 2)]]; + //Not always providing product entity or the default criteria implementation for testing purposes. + //Getting 1 list with types filter and one without. + $criteriaList[] = new ListCriteria($sku, $typesFilter, $product); + $criteria = new class implements ListCriteriaInterface + { + /** + * @var string + */ + public $sku; + + /** + * @inheritDoc + */ + public function getBelongsToProductSku(): string + { + return $this->sku; + } + + /** + * @inheritDoc + */ + public function getLinkTypes(): ?array + { + return null; + } + }; + $criteria->sku = $sku; + $criteriaList[] = $criteria; + } + + return $criteriaList; + } + + /** + * Test getting links for a list of products. + * + * @magentoDataFixture Magento/Catalog/_files/multiple_related_products.php + * @return void + * @throws \Throwable + */ + public function testSearch(): void + { + //Finding root products + $list = $this->productRepo->getList( + $this->criteriaBuilder->addFilter('sku', 'simple-related-%', 'like')->create() + ); + //Creating criteria + $criteriaList = $this->generateCriteriaList($list->getItems()); + $this->assertNotEmpty($criteriaList); + //Searching + $result = $this->query->search($criteriaList); + //Checking results + $this->assertCount(count($criteriaList), $result); + foreach ($criteriaList as $index => $criteria) { + //No errors, links must be found + $this->assertNull($result[$index]->getError()); + if (!$criteria->getLinkTypes()) { + //If there were no types filter the list cannot be empty + $this->assertNotEmpty($result[$index]->getResult()); + } + foreach ($result[$index]->getResult() as $link) { + //Links must belong to requested products. + $this->assertEquals($criteria->getBelongsToProductSku(), $link->getSku()); + if ($criteria->getLinkTypes()) { + //If link filter was set no other link types must be returned + $this->assertContains($link->getLinkType(), $criteria->getLinkTypes()); + } + //Type must be accurate + $this->assertEquals(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE, $link->getLinkedProductType()); + //Determining whether the product is supposed to be linked by SKU + preg_match('/^simple\-related\-(\d+)$/i', $criteria->getBelongsToProductSku(), $productIndex); + $this->assertNotEmpty($productIndex); + $this->assertFalse(empty($productIndex[1])); + $productIndex = (int)$productIndex[1]; + $this->assertRegExp('/^related\-product\-' .$productIndex .'\-\d+$/i', $link->getLinkedProductSku()); + //Position must be set + $this->assertGreaterThan(0, $link->getPosition()); + } + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php index fa2a0e5cb34b7..fb07d08faca58 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php @@ -11,6 +11,8 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\ResourceModel\Product as ProductResource; use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Exception\LocalizedException; +use Magento\TestFramework\Catalog\Model\ProductLayoutUpdateManager; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Bootstrap as TestBootstrap; use Magento\Framework\Acl\Builder; @@ -46,15 +48,10 @@ class ProductRepositoryTest extends \PHPUnit\Framework\TestCase */ private $productResource; - /* - * @var Auth - */ - private $auth; - /** - * @var Builder + * @var ProductLayoutUpdateManager */ - private $aclBuilder; + private $layoutManager; /** * Sets up common objects @@ -63,21 +60,19 @@ protected function setUp() { $this->productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); $this->searchCriteriaBuilder = Bootstrap::getObjectManager()->get(SearchCriteriaBuilder::class); - $this->auth = Bootstrap::getObjectManager()->get(Auth::class); - $this->aclBuilder = Bootstrap::getObjectManager()->get(Builder::class); $this->productFactory = Bootstrap::getObjectManager()->get(ProductFactory::class); $this->productResource = Bootstrap::getObjectManager()->get(ProductResource::class); + $this->layoutManager = Bootstrap::getObjectManager()->get(ProductLayoutUpdateManager::class); } /** - * @inheritDoc + * Create new subject instance. + * + * @return ProductRepositoryInterface */ - protected function tearDown() + private function createRepo(): ProductRepositoryInterface { - parent::tearDown(); - - $this->auth->logout(); - $this->aclBuilder->resetRuntimeAcl(); + return Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); } /** @@ -206,30 +201,36 @@ public function testUpdateProductSku() } /** - * Test authorization when saving product's design settings. + * Test that custom layout file attribute is saved. * + * @return void + * @throws \Throwable * @magentoDataFixture Magento/Catalog/_files/product_simple.php - * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled */ - public function testSaveDesign() + public function testCustomLayout(): void { - $product = $this->productRepository->get('simple'); - $this->auth->login(TestBootstrap::ADMIN_NAME, TestBootstrap::ADMIN_PASSWORD); - - //Admin doesn't have access to product's design. - $this->aclBuilder->getAcl()->deny(null, 'Magento_Catalog::edit_product_design'); - - $product->setCustomAttribute('custom_design', 2); - $product = $this->productRepository->save($product); - $this->assertEmpty($product->getCustomAttribute('custom_design')); - - //Admin has access to products' design. - $this->aclBuilder->getAcl() - ->allow(null, ['Magento_Catalog::products','Magento_Catalog::edit_product_design']); - - $product->setCustomAttribute('custom_design', 2); - $product = $this->productRepository->save($product); - $this->assertNotEmpty($product->getCustomAttribute('custom_design')); - $this->assertEquals(2, $product->getCustomAttribute('custom_design')->getValue()); + //New valid value + $repo = $this->createRepo(); + $product = $repo->get('simple'); + $newFile = 'test'; + $this->layoutManager->setFakeFiles((int)$product->getId(), [$newFile]); + $product->setCustomAttribute('custom_layout_update_file', $newFile); + $repo->save($product); + $repo = $this->createRepo(); + $product = $repo->get('simple'); + $this->assertEquals($newFile, $product->getCustomAttribute('custom_layout_update_file')->getValue()); + + //Setting non-existent value + $newFile = 'does not exist'; + $product->setCustomAttribute('custom_layout_update_file', $newFile); + $caughtException = false; + try { + $repo->save($product); + } catch (LocalizedException $exception) { + $caughtException = true; + } + $this->assertTrue($caughtException); } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Attribute/Entity/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Attribute/Entity/AttributeTest.php index 8ecf3da8e1aae..2df9c468ba10a 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Attribute/Entity/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Attribute/Entity/AttributeTest.php @@ -23,6 +23,7 @@ * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php * @magentoDataFixture Magento/Catalog/_files/multiselect_attribute.php * @magentoDataFixture Magento/Catalog/_files/product_without_options.php + * @magentoDbIsolation enabled */ class AttributeTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php index de0e881474cf0..2100920ab8ac9 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php @@ -14,7 +14,7 @@ use Magento\TestFramework\Helper\Bootstrap; /** - * Collection test + * Test for Magento\Catalog\Model\ResourceModel\Product\Collection * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -277,15 +277,57 @@ public function testAddAttributeToFilterAffectsGetSize(): void } /** - * Add tier price attribute filter to collection + * Add tier price attribute filter to collection with different condition types. * + * @param mixed $condition * @magentoDataFixture Magento/Catalog/Model/ResourceModel/_files/few_simple_products.php * @magentoDataFixture Magento/Catalog/Model/ResourceModel/_files/product_simple.php + * + * @dataProvider addAttributeTierPriceToFilterDataProvider */ - public function testAddAttributeTierPriceToFilter(): void + public function testAddAttributeTierPriceToFilter($condition): void { - $this->assertEquals(11, $this->collection->getSize()); - $this->collection->addAttributeToFilter('tier_price', ['gt' => 0]); - $this->assertEquals(1, $this->collection->getSize()); + $size = $this->collection->addAttributeToFilter('tier_price', $condition)->getSize(); + $this->assertEquals(1, $size); + } + + /** + * @return array + */ + public function addAttributeTierPriceToFilterDataProvider(): array + { + return [ + 'condition is array' => [['eq' => 8]], + 'condition is string' => ['8'], + 'condition is int' => [8], + 'condition is null' => [null] + ]; + } + + /** + * Add is_saleable attribute filter to collection with different condition types. + * + * @param mixed $condition + * @magentoDataFixture Magento/Catalog/Model/ResourceModel/_files/product_simple.php + * + * @dataProvider addAttributeIsSaleableToFilterDataProvider + */ + public function testAddAttributeIsSaleableToFilter($condition): void + { + $size = $this->collection->addAttributeToFilter('is_saleable', $condition)->getSize(); + $this->assertEquals(1, $size); + } + + /** + * @return array + */ + public function addAttributeIsSaleableToFilterDataProvider(): array + { + return [ + 'condition is array' => [['eq' => 1]], + 'condition is string' => ['1'], + 'condition is int' => [1], + 'condition is null' => [null] + ]; } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php index e218c508b7d3e..f560854fa75f6 100755 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/ProductTest.php @@ -6,7 +6,12 @@ namespace Magento\Catalog\Model\ResourceModel; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Product\Action; +use Magento\Eav\Api\Data\AttributeSetInterface; +use Magento\Eav\Model\AttributeSetRepository; +use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Eav\Model\GetAttributeSetByName; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; use Magento\Framework\Exception\NoSuchEntityException; @@ -164,4 +169,33 @@ public function testUpdateStoreSpecificSpecialPrice() $product = $this->productRepository->get('simple', false, 0, true); $this->assertEquals(5.99, $product->getSpecialPrice()); } + + /** + * Checks that product has no attribute values for attributes not assigned to the product's attribute set. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Catalog/_files/attribute_set_with_image_attribute.php + */ + public function testChangeAttributeSet() + { + $attributeCode = 'funny_image'; + /** @var GetAttributeSetByName $attributeSetModel */ + $attributeSetModel = $this->objectManager->get(GetAttributeSetByName::class); + $attributeSet = $attributeSetModel->execute('attribute_set_with_media_attribute'); + + $product = $this->productRepository->get('simple', true, 1, true); + $product->setAttributeSetId($attributeSet->getAttributeSetId()); + $this->productRepository->save($product); + $product->setData($attributeCode, 'test'); + $this->model->saveAttribute($product, $attributeCode); + + $product = $this->productRepository->get('simple', true, 1, true); + $this->assertEquals('test', $product->getData($attributeCode)); + + $product->setAttributeSetId($product->getDefaultAttributeSetId()); + $this->productRepository->save($product); + + $attribute = $this->model->getAttributeRawValue($product->getId(), $attributeCode, 1); + $this->assertEmpty($attribute); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AbstractEavTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AbstractEavTest.php new file mode 100644 index 0000000000000..3375a4e8e4094 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AbstractEavTest.php @@ -0,0 +1,306 @@ + 'input', + 'hidden' => 'input', + 'boolean' => 'checkbox', + 'media_image' => 'image', + 'price' => 'input', + 'weight' => 'input', + 'gallery' => 'image' + ]; + $this->objectManager = Bootstrap::getObjectManager(); + $this->locatorMock = $this->createMock(LocatorInterface::class); + $this->locatorMock->expects($this->any())->method('getStore')->willReturn( + $this->objectManager->get(StoreInterface::class) + ); + $this->metaPropertiesMapper = $this->objectManager->create(MetaProperties::class, ['mappings' => []]); + $this->compositeConfigProcessor = $this->objectManager->create( + CompositeConfigProcessor::class, + ['eavWysiwygDataProcessors' => []] + ); + $this->eavModifier = $this->objectManager->create( + Eav::class, + [ + 'locator' => $this->locatorMock, + 'formElementMapper' => $this->objectManager->create(FormElement::class, ['mappings' => $mappings]), + 'metaPropertiesMapper' => $this->metaPropertiesMapper, + 'wysiwygConfigProcessor' => $this->compositeConfigProcessor, + ] + ); + $this->attributeRepository = $this->objectManager->create(ProductAttributeRepositoryInterface::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productFactory = $this->objectManager->get(ProductInterfaceFactory::class); + $this->defaultSetId = (int)$this->objectManager->create(Type::class) + ->loadByCode(ProductAttributeInterface::ENTITY_TYPE_CODE) + ->getDefaultAttributeSetId(); + } + + /** + * @param ProductInterface $product + * @param array $expectedMeta + * @return void + */ + protected function callModifyMetaAndAssert(ProductInterface $product, array $expectedMeta): void + { + $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($product); + $actualMeta = $this->eavModifier->modifyMeta([]); + $this->prepareDataForComparison($actualMeta, $expectedMeta); + $this->assertEquals($expectedMeta, $actualMeta); + } + + /** + * @param ProductInterface $product + * @param array $expectedData + * @return void + */ + protected function callModifyDataAndAssert(ProductInterface $product, array $expectedData): void + { + $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($product); + $actualData = $this->eavModifier->modifyData([]); + $this->prepareDataForComparison($actualData, $expectedData); + $this->assertEquals($expectedData, $actualData); + } + + /** + * Prepare data for comparison to avoid false positive failures. + * + * Make sure that $data contains all the data contained in $expectedData, + * ignore all fields not declared in $expectedData + * + * @param array &$data + * @param array $expectedData + * @return void + */ + protected function prepareDataForComparison(array &$data, array $expectedData): void + { + foreach ($data as $key => &$item) { + if (!isset($expectedData[$key])) { + unset($data[$key]); + continue; + } + if ($item instanceof Phrase) { + $item = (string)$item; + } elseif (is_array($item)) { + $this->prepareDataForComparison($item, $expectedData[$key]); + } elseif ($key === 'price_id' || $key === 'sortOrder') { + $data[$key] = '__placeholder__'; + } + } + } + + /** + * Updates attribute default value. + * + * @param string $attributeCode + * @param string $defaultValue + * @return void + */ + protected function setAttributeDefaultValue(string $attributeCode, string $defaultValue): void + { + $attribute = $this->attributeRepository->get($attributeCode); + $attribute->setDefaultValue($defaultValue); + $this->attributeRepository->save($attribute); + } + + /** + * Returns attribute options list. + * + * @param string $attributeCode + * @return array + */ + protected function getAttributeOptions(string $attributeCode): array + { + $attribute = $this->attributeRepository->get($attributeCode); + + return $attribute->usesSource() ? $attribute->getSource()->getAllOptions() : []; + } + + /** + * Returns attribute option value by id. + * + * @param string $attributeCode + * @param string $label + * @return int|null + */ + protected function getOptionValueByLabel(string $attributeCode, string $label): ?int + { + $result = null; + foreach ($this->getAttributeOptions($attributeCode) as $option) { + if ($option['label'] == $label) { + $result = (int)$option['value']; + } + } + + return $result; + } + + /** + * Returns product for testing. + * + * @return ProductInterface + */ + protected function getProduct(): ProductInterface + { + return $this->productRepository->get('simple', false, Store::DEFAULT_STORE_ID); + } + + /** + * Returns new product object. + * + * @return ProductInterface + */ + protected function getNewProduct(): ProductInterface + { + $product = $this->productFactory->create(); + $product->setAttributeSetId($this->defaultSetId); + + return $product; + } + + /** + * Updates product. + * + * @param ProductInterface $product + * @param array $attributeData + * @return void + */ + protected function saveProduct(ProductInterface $product, array $attributeData): void + { + $product->addData($attributeData); + $this->productRepository->save($product); + } + + /** + * Adds additional array nesting to expected meta. + * + * @param array $attributeMeta + * @param string $groupCode + * @param string $attributeCode + * @return array + */ + protected function addMetaNesting(array $attributeMeta, string $groupCode, string $attributeCode): array + { + return [ + $groupCode => [ + 'arguments' => ['data' => ['config' => ['dataScope' => 'data.product']]], + 'children' => [ + 'container_' . $attributeCode => [ + 'children' => [$attributeCode => ['arguments' => ['data' => ['config' => $attributeMeta]]]], + ], + ], + ], + ]; + } + + /** + * Adds additional array nesting to expected data. + * + * @param array $data + * @return array + */ + protected function addDataNesting(array $data): array + { + return [1 => ['product' => $data]]; + } + + /** + * Returns attribute codes from product meta data array. + * + * @param array $actualMeta + * @return array + */ + protected function getUsedAttributes(array $actualMeta): array + { + $usedAttributes = []; + foreach ($actualMeta as $group) { + foreach (array_keys($group['children']) as $field) { + $usedAttributes[] = str_replace('container_', '', $field); + } + } + + return $usedAttributes; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/AttributeSetGroupsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/AttributeSetGroupsTest.php new file mode 100644 index 0000000000000..4890d179c7d95 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/AttributeSetGroupsTest.php @@ -0,0 +1,36 @@ +locatorMock->expects($this->any())->method('getProduct')->willReturn($this->getProduct()); + $meta = $this->eavModifier->modifyMeta([]); + $this->assertArrayNotHasKey( + 'test-attribute-group-name', + $meta, + 'Attribute set group without attributes appear on product page in admin panel' + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/BooleanAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/BooleanAttributeTest.php new file mode 100644 index 0000000000000..16ad3d3ad4564 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/BooleanAttributeTest.php @@ -0,0 +1,86 @@ +callModifyMetaAndAssert( + $this->getProduct(), + $this->addMetaNesting($this->getAttributeMeta(), 'product-details', 'boolean_attribute') + ); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_boolean_attribute.php + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @return void + */ + public function testModifyData(): void + { + $product = $this->getProduct(); + $attributeData = ['boolean_attribute' => 1]; + $this->saveProduct($product, $attributeData); + $expectedData = $this->addDataNesting($attributeData); + $this->callModifyDataAndAssert($product, $expectedData); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_boolean_attribute.php + * @return void + */ + public function testModifyMetaNewProduct(): void + { + $this->setAttributeDefaultValue('boolean_attribute', '0'); + $attributesMeta = array_merge($this->getAttributeMeta(), ['default' => '0']); + $expectedMeta = $this->addMetaNesting( + $attributesMeta, + 'product-details', + 'boolean_attribute' + ); + $this->callModifyMetaAndAssert($this->getNewProduct(), $expectedMeta); + } + + /** + * @return array + */ + private function getAttributeMeta(): array + { + return [ + 'dataType' => 'boolean', + 'formElement' => 'checkbox', + 'visible' => '1', + 'required' => '0', + 'label' => 'Boolean Attribute', + 'code' => 'boolean_attribute', + 'source' => 'product-details', + 'scopeLabel' => '[STORE VIEW]', + 'globalScope' => false, + 'sortOrder' => '__placeholder__', + 'componentType' => 'field', + 'prefer' => 'toggle', + 'valueMap' => [ + 'true' => '1', + 'false' => '0', + ] + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/DateAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/DateAttributeTest.php new file mode 100644 index 0000000000000..feac956ddf549 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/DateAttributeTest.php @@ -0,0 +1,81 @@ +callModifyMetaAndAssert( + $this->getProduct(), + $this->addMetaNesting($this->getAttributeMeta(), 'product-details', 'date_attribute') + ); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_date_attribute.php + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @return void + */ + public function testModifyData(): void + { + $product = $this->getProduct(); + $attributeData = ['date_attribute' => '01/01/2010']; + $this->saveProduct($product, $attributeData); + $expectedData = $this->addDataNesting($attributeData); + $this->callModifyDataAndAssert($product, $expectedData); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_date_attribute.php + * @return void + */ + public function testModifyMetaNewProduct(): void + { + $this->setAttributeDefaultValue('date_attribute', '01/01/2000'); + $attributesMeta = array_merge($this->getAttributeMeta(), ['default' => '2000-01-01 00:00:00']); + $expectedMeta = $this->addMetaNesting( + $attributesMeta, + 'product-details', + 'date_attribute' + ); + $this->callModifyMetaAndAssert($this->getNewProduct(), $expectedMeta); + } + + /** + * @return array + */ + private function getAttributeMeta(): array + { + return [ + 'dataType' => 'date', + 'formElement' => 'date', + 'visible' => '1', + 'required' => '0', + 'label' => 'Date Attribute', + 'code' => 'date_attribute', + 'source' => 'product-details', + 'scopeLabel' => '[GLOBAL]', + 'globalScope' => true, + 'sortOrder' => '__placeholder__', + 'componentType' => 'field' + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/DecimalAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/DecimalAttributeTest.php new file mode 100644 index 0000000000000..901613498e53a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/DecimalAttributeTest.php @@ -0,0 +1,67 @@ +callModifyMetaAndAssert( + $this->getProduct(), + $this->addMetaNesting($this->getAttributeMeta(), 'product-details', 'decimal_attribute') + ); + } + + /** + * @return void + */ + public function testModifyData(): void + { + $product = $this->getProduct(); + $attributeData = ['decimal_attribute' => '10.00']; + $this->saveProduct($product, $attributeData); + $expectedData = $this->addDataNesting($attributeData); + $this->callModifyDataAndAssert($product, $expectedData); + } + + /** + * @return array + */ + private function getAttributeMeta(): array + { + return [ + 'dataType' => 'price', + 'formElement' => 'input', + 'visible' => '1', + 'required' => '0', + 'label' => 'Decimal Attribute', + 'code' => 'decimal_attribute', + 'source' => 'product-details', + 'scopeLabel' => '[GLOBAL]', + 'globalScope' => true, + 'sortOrder' => '__placeholder__', + 'componentType' => 'field', + 'validation' => [ + 'validate-zero-or-greater' => true, + ], + 'addbefore' => '$' + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/DefaultAttributesTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/DefaultAttributesTest.php new file mode 100644 index 0000000000000..fbf752cc9e239 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/DefaultAttributesTest.php @@ -0,0 +1,47 @@ +callModifyMetaAndAssert($this->getProduct(), $expectedMeta); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_admin_store.php + * @return void + */ + public function testModifyData(): void + { + $expectedData = include __DIR__ . '/../_files/eav_expected_data_output.php'; + $this->callModifyDataAndAssert($this->getProduct(), $expectedData); + } + + /** + * @return void + */ + public function testModifyMetaNewProduct(): void + { + $expectedMeta = include __DIR__ . '/../_files/eav_expected_meta_output_w_default.php'; + $this->callModifyMetaAndAssert($this->getNewProduct(), $expectedMeta); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/ImageAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/ImageAttributeTest.php new file mode 100644 index 0000000000000..75a20145fdcda --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/ImageAttributeTest.php @@ -0,0 +1,41 @@ +locatorMock->expects($this->any())->method('getProduct')->willReturn($this->getProduct()); + $actualMeta = $this->eavModifier->modifyMeta([]); + $this->assertArrayNotHasKey('image_attribute', $this->getUsedAttributes($actualMeta)); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_image_attribute.php + * @return void + */ + public function testModifyMetaNewProduct(): void + { + $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($this->getNewProduct()); + $actualMeta = $this->eavModifier->modifyMeta([]); + $this->assertArrayNotHasKey('image_attribute', $this->getUsedAttributes($actualMeta)); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/MultiselectAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/MultiselectAttributeTest.php new file mode 100644 index 0000000000000..eefb49dcf6239 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/MultiselectAttributeTest.php @@ -0,0 +1,65 @@ +callModifyMetaAndAssert( + $this->getProduct(), + $this->addMetaNesting($this->getAttributeMeta(), 'product-details', 'multiselect_attribute') + ); + } + + /** + * @return void + */ + public function testModifyData(): void + { + $product = $this->getProduct(); + $optionValue = $this->getOptionValueByLabel('multiselect_attribute', 'Option 3'); + $attributeData = ['multiselect_attribute' => $optionValue]; + $this->saveProduct($product, $attributeData); + $expectedData = $this->addDataNesting($attributeData); + $this->callModifyDataAndAssert($product, $expectedData); + } + + /** + * @return array + */ + private function getAttributeMeta(): array + { + return [ + 'dataType' => 'multiselect', + 'formElement' => 'multiselect', + 'visible' => '1', + 'required' => '0', + 'label' => 'Multiselect Attribute', + 'code' => 'multiselect_attribute', + 'source' => 'product-details', + 'scopeLabel' => '[GLOBAL]', + 'globalScope' => true, + 'sortOrder' => '__placeholder__', + 'options' => $this->getAttributeOptions('multiselect_attribute'), + 'componentType' => 'field', + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/SelectAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/SelectAttributeTest.php new file mode 100644 index 0000000000000..493608a43b77b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/SelectAttributeTest.php @@ -0,0 +1,66 @@ +callModifyMetaAndAssert( + $this->getProduct(), + $this->addMetaNesting($this->getAttributeMeta(), 'product-details', 'dropdown_attribute') + ); + } + + /** + * @return void + */ + public function testModifyData(): void + { + $product = $this->getProduct(); + $attributeData = [ + 'dropdown_attribute' => $this->getOptionValueByLabel('dropdown_attribute', 'Option 3') + ]; + $this->saveProduct($product, $attributeData); + $expectedData = $this->addDataNesting($attributeData); + $this->callModifyDataAndAssert($product, $expectedData); + } + + /** + * @return array + */ + private function getAttributeMeta(): array + { + return [ + 'dataType' => 'select', + 'formElement' => 'select', + 'visible' => '1', + 'required' => '0', + 'label' => 'Drop-Down Attribute', + 'code' => 'dropdown_attribute', + 'source' => 'product-details', + 'scopeLabel' => '[STORE VIEW]', + 'globalScope' => false, + 'sortOrder' => '__placeholder__', + 'options' => $this->getAttributeOptions('dropdown_attribute'), + 'componentType' => 'field', + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/VarcharAttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/VarcharAttributeTest.php new file mode 100644 index 0000000000000..dfa40d138d640 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Eav/VarcharAttributeTest.php @@ -0,0 +1,81 @@ +callModifyMetaAndAssert( + $this->getProduct(), + $this->addMetaNesting($this->getAttributeMeta(), 'product-details', 'varchar_attribute') + ); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_varchar_attribute.php + * @return void + */ + public function testModifyMetaNewProduct(): void + { + $this->setAttributeDefaultValue('varchar_attribute', 'test'); + $attributesMeta = array_merge($this->getAttributeMeta(), ['default' => 'test']); + $expectedMeta = $this->addMetaNesting( + $attributesMeta, + 'product-details', + 'varchar_attribute' + ); + $this->callModifyMetaAndAssert($this->getNewProduct(), $expectedMeta); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_varchar_attribute.php + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @return void + */ + public function testModifyData(): void + { + $product = $this->getProduct(); + $attributeData = ['varchar_attribute' => 'Test message']; + $this->saveProduct($product, $attributeData); + $expectedData = $this->addDataNesting($attributeData); + $this->callModifyDataAndAssert($product, $expectedData); + } + + /** + * @return array + */ + private function getAttributeMeta(): array + { + return [ + 'dataType' => 'text', + 'formElement' => 'input', + 'visible' => '1', + 'required' => '0', + 'label' => 'Varchar Attribute', + 'code' => 'varchar_attribute', + 'source' => 'product-details', + 'scopeLabel' => '[GLOBAL]', + 'globalScope' => true, + 'sortOrder' => '__placeholder__', + 'componentType' => 'field' + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/EavTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/EavTest.php index 6dd3436041b33..83c6e99df629e 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/EavTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/EavTest.php @@ -3,124 +3,218 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Ui\DataProvider\Product\Form\Modifier; +use Magento\Eav\Api\AttributeSetRepositoryInterface; +use Magento\Eav\Model\AttributeSetRepository; +use Magento\TestFramework\Eav\Model\GetAttributeGroupByName; +use Magento\TestFramework\Eav\Model\ResourceModel\GetEntityIdByAttributeId; + /** + * Provides tests for eav modifier used in products admin form data provider. + * * @magentoDbIsolation enabled - * @magentoAppIsolation enabled - * @magentoAppArea adminhtml */ -class EavTest extends \PHPUnit\Framework\TestCase +class EavTest extends AbstractEavTest { /** - * @var \Magento\Framework\ObjectManagerInterface + * @var GetAttributeGroupByName */ - protected $objectManager; + private $attributeGroupByName; /** - * @var \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav + * @var GetEntityIdByAttributeId */ - protected $eavModifier; + private $getEntityIdByAttributeId; /** - * @var \Magento\Catalog\Model\Locator\LocatorInterface|\PHPUnit_Framework_MockObject_MockObject + * @var AttributeSetRepository */ - protected $locatorMock; + private $setRepository; + /** + * @inheritdoc + */ protected function setUp() { - $mappings = [ - "text" => "input", - "hidden" => "input", - "boolean" => "checkbox", - "media_image" => "image", - "price" => "input", - "weight" => "input", - "gallery" => "image" - ]; - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->locatorMock = $this->createMock(\Magento\Catalog\Model\Locator\LocatorInterface::class); - $store = $this->objectManager->get(\Magento\Store\Api\Data\StoreInterface::class); - $this->locatorMock->expects($this->any())->method('getStore')->willReturn($store); - $this->eavModifier = $this->objectManager->create( - \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Eav::class, - [ - 'locator' => $this->locatorMock, - 'formElementMapper' => $this->objectManager->create( - \Magento\Ui\DataProvider\Mapper\FormElement::class, - ['mappings' => $mappings] - ) - ] - ); parent::setUp(); + $this->attributeGroupByName = $this->objectManager->get(GetAttributeGroupByName::class); + $this->getEntityIdByAttributeId = $this->objectManager->get(GetEntityIdByAttributeId::class); + $this->setRepository = $this->objectManager->get(AttributeSetRepositoryInterface::class); } /** + * @magentoDataFixture Magento/Catalog/_files/product_text_attribute.php * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @dataProvider modifyMetaWithAttributeProvider + * @param string $groupName + * @param string $groupCode + * @param string $attributeCode + * @param array $attributeMeta + * @return void */ - public function testModifyMeta() - { - /** @var \Magento\Catalog\Model\Product $product */ - $product = $this->objectManager->create(\Magento\Catalog\Model\Product::class); - $product->load(1); - $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($product); - $expectedMeta = include __DIR__ . '/_files/eav_expected_meta_output.php'; + public function testModifyMetaWithAttributeInGroups( + string $groupName, + string $groupCode, + string $attributeCode, + array $attributeMeta + ): void { + $attributeGroup = $this->attributeGroupByName->execute($this->defaultSetId, $groupName); + $groupId = $attributeGroup ? $attributeGroup->getAttributeGroupId() : 'ynode-1'; + $data = [ + 'attributes' => [ + [$this->attributeRepository->get($attributeCode)->getAttributeId(), $groupId, 1], + ], + 'groups' => [ + [$groupId, $groupName, 1], + ], + ]; + $this->prepareAttributeSet($data); + $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($this->getProduct()); $actualMeta = $this->eavModifier->modifyMeta([]); + $expectedMeta = $this->addMetaNesting($attributeMeta, $groupCode, $attributeCode); $this->prepareDataForComparison($actualMeta, $expectedMeta); $this->assertEquals($expectedMeta, $actualMeta); } - public function testModifyMetaNewProduct() + /** + * @return array + */ + public function modifyMetaWithAttributeProvider(): array + { + $textAttributeMeta = [ + 'dataType' => 'textarea', + 'formElement' => 'textarea', + 'visible' => '1', + 'required' => '0', + 'label' => 'Text Attribute', + 'code' => 'text_attribute', + 'source' => 'content', + 'scopeLabel' => '[GLOBAL]', + 'globalScope' => true, + 'sortOrder' => '__placeholder__', + 'componentType' => 'field', + ]; + $urlKeyAttributeMeta = [ + 'dataType' => 'text', + 'formElement' => 'input', + 'visible' => '1', + 'required' => '0', + 'label' => 'URL Key', + 'code' => 'url_key', + 'source' => 'image-management', + 'scopeLabel' => '[STORE VIEW]', + 'globalScope' => false, + 'sortOrder' => '__placeholder__', + 'componentType' => 'field', + ]; + + return [ + 'new_attribute_in_existing_group' => [ + 'group_name' => 'Content', + 'group_code' => 'content', + 'attribute_code' => 'text_attribute', + 'attribute_meta' => $textAttributeMeta, + ], + 'new_attribute_in_new_group' => [ + 'group_name' => 'Test', + 'group_code' => 'test', + 'attribute_code' => 'text_attribute', + 'attribute_meta' => array_merge($textAttributeMeta, ['source' => 'test']), + ], + 'old_attribute_moved_to_existing_group' => [ + 'group_name' => 'Images', + 'group_code' => 'image-management', + 'attribute_code' => 'url_key', + 'attribute_meta' => $urlKeyAttributeMeta, + ], + 'old_attribute_moved_to_new_group' => [ + 'group_name' => 'Test', + 'group_code' => 'test', + 'attribute_code' => 'url_key', + 'attribute_meta' => array_merge($urlKeyAttributeMeta, ['source' => 'test']), + ], + ]; + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @return void + */ + public function testModifyMetaWithChangedGroupSorting(): void + { + $contentGroupId = $this->attributeGroupByName->execute($this->defaultSetId, 'Content') + ->getAttributeGroupId(); + $imagesGroupId = $this->attributeGroupByName->execute($this->defaultSetId, 'Images') + ->getAttributeGroupId(); + $additional = ['groups' => [[$contentGroupId, 'Content', 2], [$imagesGroupId, 'Images', 1]]]; + $this->prepareAttributeSet($additional); + $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($this->getProduct()); + $actualMeta = $this->eavModifier->modifyMeta([]); + $groupCodes = ['image-management', 'content']; + $groups = array_filter( + array_keys($actualMeta), + function ($group) use ($groupCodes) { + return in_array($group, $groupCodes); + } + ); + $this->assertEquals($groupCodes, $groups); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @return void + */ + public function testModifyMetaWithRemovedGroup(): void { - /** @var \Magento\Catalog\Model\Product $product */ - $product = $this->objectManager->create(\Magento\Catalog\Model\Product::class); - $product->setAttributeSetId(4); - $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($product); - $expectedMeta = include __DIR__ . '/_files/eav_expected_meta_output_w_default.php'; + $designAttributes = ['page_layout', 'options_container', 'custom_layout_update']; + $designGroupId =$this->attributeGroupByName->execute($this->defaultSetId, 'Design') + ->getAttributeGroupId(); + $additional = ['removeGroups' => [$designGroupId]]; + $this->prepareAttributeSet($additional); + $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($this->getProduct()); $actualMeta = $this->eavModifier->modifyMeta([]); - $this->prepareDataForComparison($actualMeta, $expectedMeta); - $this->assertEquals($expectedMeta, $actualMeta); + $this->assertArrayNotHasKey('design', $actualMeta, 'Group "Design" still visible.'); + $this->assertEmpty( + array_intersect($designAttributes, $this->getUsedAttributes($actualMeta)), + 'Attributes from "Design" group still visible.' + ); } /** - * @magentoDataFixture Magento/Catalog/_files/product_simple_with_admin_store.php + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @return void */ - public function testModifyData() + public function testModifyMetaWithRemovedAttribute(): void { - /** @var \Magento\Catalog\Model\Product $product */ - $product = $this->objectManager->create(\Magento\Catalog\Model\Product::class); - $product->load(1); - $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($product); - $expectedData = include __DIR__ . '/_files/eav_expected_data_output.php'; - $actualData = $this->eavModifier->modifyData([]); - $this->prepareDataForComparison($actualData, $expectedData); - $this->assertEquals($expectedData, $actualData); + $attributeId = (int)$this->attributeRepository->get('meta_description')->getAttributeId(); + $entityAttributeId = $this->getEntityIdByAttributeId->execute($this->defaultSetId, $attributeId); + $additional = ['not_attributes' => [$entityAttributeId]]; + $this->prepareAttributeSet($additional); + $this->locatorMock->expects($this->any())->method('getProduct')->willReturn($this->getProduct()); + $actualMeta = $this->eavModifier->modifyMeta([]); + $this->assertArrayNotHasKey('meta_description', $this->getUsedAttributes($actualMeta)); } /** - * Prepare data for comparison to avoid false positive failures. - * - * Make sure that $data contains all the data contained in $expectedData, - * ignore all fields not declared in $expectedData + * Updates default attribute set. * - * @param array &$data - * @param array $expectedData + * @param array $additional * @return void */ - private function prepareDataForComparison(array &$data, array $expectedData) + private function prepareAttributeSet(array $additional): void { - foreach ($data as $key => &$item) { - if (!isset($expectedData[$key])) { - unset($data[$key]); - continue; - } - if ($item instanceof \Magento\Framework\Phrase) { - $item = (string)$item; - } elseif (is_array($item)) { - $this->prepareDataForComparison($item, $expectedData[$key]); - } elseif ($key === 'price_id' || $key === 'sortOrder') { - $data[$key] = '__placeholder__'; - } - } + $set = $this->setRepository->get($this->defaultSetId); + $data = [ + 'attributes' => [], + 'groups' => [], + 'not_attributes' => [], + 'removeGroups' => [], + 'attribute_set_name' => 'Default', + ]; + $set->organizeData(array_merge($data, $additional)); + $this->setRepository->save($set); } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php new file mode 100644 index 0000000000000..ebfbd06d7edad --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/LayoutUpdateTest.php @@ -0,0 +1,187 @@ +locator = $this->getMockForAbstractClass(LocatorInterface::class); + $store = Bootstrap::getObjectManager()->create(StoreInterface::class); + $this->locator->method('getStore')->willReturn($store); + $this->modifier = Bootstrap::getObjectManager()->create(LayoutUpdate::class, ['locator' => $this->locator]); + $this->repo = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); + $this->eavModifier = Bootstrap::getObjectManager()->create( + EavModifier::class, + [ + 'locator' => $this->locator, + 'formElementMapper' => Bootstrap::getObjectManager()->create( + \Magento\Ui\DataProvider\Mapper\FormElement::class, + [ + 'mappings' => [ + "text" => "input", + "hidden" => "input", + "boolean" => "checkbox", + "media_image" => "image", + "price" => "input", + "weight" => "input", + "gallery" => "image" + ] + ] + ) + ] + ); + $this->fakeFiles = Bootstrap::getObjectManager()->get(ProductLayoutUpdateManager::class); + } + + /** + * Test that data is being modified accordingly. + * + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @return void + * @throws \Throwable + */ + public function testModifyData(): void + { + $product = $this->repo->get('simple'); + $this->locator->method('getProduct')->willReturn($product); + $product->setCustomAttribute('custom_layout_update', 'something'); + + $data = $this->modifier->modifyData([$product->getId() => ['product' => []]]); + $this->assertEquals( + LayoutUpdateAttribute::VALUE_USE_UPDATE_XML, + $data[$product->getId()]['product']['custom_layout_update_file'] + ); + } + + /** + * Extract options meta. + * + * @param array $meta + * @return array + */ + private function extractCustomLayoutOptions(array $meta): array + { + $this->assertArrayHasKey('design', $meta); + $this->assertArrayHasKey('children', $meta['design']); + $this->assertArrayHasKey('container_custom_layout_update_file', $meta['design']['children']); + $this->assertArrayHasKey('children', $meta['design']['children']['container_custom_layout_update_file']); + $this->assertArrayHasKey( + 'custom_layout_update_file', + $meta['design']['children']['container_custom_layout_update_file']['children'] + ); + $fieldMeta = $meta['design']['children']['container_custom_layout_update_file']['children']; + $fieldMeta = $fieldMeta['custom_layout_update_file']; + $this->assertArrayHasKey('arguments', $fieldMeta); + $this->assertArrayHasKey('data', $fieldMeta['arguments']); + $this->assertArrayHasKey('config', $fieldMeta['arguments']['data']); + $this->assertArrayHasKey('options', $fieldMeta['arguments']['data']['config']); + + return $fieldMeta['arguments']['data']['config']['options']; + } + + /** + * Check that entity specific options are returned. + * + * @return void + * @throws \Throwable + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + */ + public function testEntitySpecificData(): void + { + //Testing a category without layout xml + $product = $this->repo->get('simple'); + $this->locator->method('getProduct')->willReturn($product); + $this->fakeFiles->setFakeFiles((int)$product->getId(), ['testOne', 'test_two']); + + $meta = $this->eavModifier->modifyMeta([]); + $list = $this->extractCustomLayoutOptions($meta); + $expectedList = [ + [ + 'label' => 'No update', + 'value' => \Magento\Catalog\Model\Attribute\Backend\AbstractLayoutUpdate::VALUE_NO_UPDATE, + '__disableTmpl' => true + ], + ['label' => 'testOne', 'value' => 'testOne', '__disableTmpl' => true], + ['label' => 'test_two', 'value' => 'test_two', '__disableTmpl' => true] + ]; + sort($expectedList); + sort($list); + $this->assertEquals($expectedList, $list); + + //Product with old layout xml + $product->setCustomAttribute('custom_layout_update', 'test'); + $this->fakeFiles->setFakeFiles((int)$product->getId(), ['test3']); + + $meta = $this->eavModifier->modifyMeta([]); + $list = $this->extractCustomLayoutOptions($meta); + $expectedList = [ + [ + 'label' => 'No update', + 'value' => \Magento\Catalog\Model\Attribute\Backend\AbstractLayoutUpdate::VALUE_NO_UPDATE, + '__disableTmpl' => true + ], + [ + 'label' => 'Use existing', + 'value' => LayoutUpdateAttribute::VALUE_USE_UPDATE_XML, + '__disableTmpl' => true + ], + ['label' => 'test3', 'value' => 'test3', '__disableTmpl' => true], + ]; + sort($expectedList); + sort($list); + $this->assertEquals($expectedList, $list); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/_files/eav_expected_meta_output.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/_files/eav_expected_meta_output.php index fa0a664738c1d..f6f4cc0e15159 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/_files/eav_expected_meta_output.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/_files/eav_expected_meta_output.php @@ -40,9 +40,6 @@ "globalScope" => false, "code" => "status", "sortOrder" => "__placeholder__", - "service" => [ - "template" => "ui/form/element/helper/service" - ], "componentType" => "field" ], ], @@ -66,9 +63,6 @@ "globalScope" => false, "code" => "name", "sortOrder" => "__placeholder__", - "service" => [ - "template" => "ui/form/element/helper/service" - ], "componentType" => "field", "validation" => [ "required-entry" => true diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_based_on_default_with_custom_group.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_based_on_default_with_custom_group.php new file mode 100644 index 0000000000000..ccca2b7e4dbce --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_based_on_default_with_custom_group.php @@ -0,0 +1,49 @@ +get(AttributeSetRepositoryInterface::class); +/** @var AttributeSetInterfaceFactory $attributeSetFactory */ +$attributeSetFactory = $objectManager->get(AttributeSetInterfaceFactory::class); +/** @var Type $entityType */ +$entityType = $objectManager->create(Type::class)->loadByCode(ProductAttributeInterface::ENTITY_TYPE_CODE); +$attributeSet = $attributeSetFactory->create( + [ + 'data' => [ + 'id' => null, + 'attribute_set_name' => 'new_attribute_set', + 'entity_type_id' => $entityType->getId(), + 'sort_order' => 300, + ], + ] +); +$attributeSet->isObjectNew(true); +$attributeSet->setHasDataChanges(true); +$attributeSet->validate(); +$attributeSetRepository->save($attributeSet); +$attributeSet->initFromSkeleton($entityType->getDefaultAttributeSetId()); +/** @var AttributeGroupInterface $newGroup */ +$newGroup = $objectManager->get(GroupFactory::class)->create(); +$newGroup->setId(null) + ->setAttributeGroupName('Test attribute group name') + ->setAttributeSetId($attributeSet->getAttributeSetId()) + ->setSortOrder(11) + ->setAttributes([]); +/** @var AttributeGroupInterface[] $groups */ +$groups = $attributeSet->getGroups(); +array_push($groups, $newGroup); +$attributeSet->setGroups($groups); +$attributeSetRepository->save($attributeSet); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_based_on_default_with_custom_group_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_based_on_default_with_custom_group_rollback.php new file mode 100644 index 0000000000000..f8628ea2d6ddb --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_based_on_default_with_custom_group_rollback.php @@ -0,0 +1,21 @@ +get(GetAttributeSetByName::class); +/** @var AttributeSetRepositoryInterface $attributeSetRepository */ +$attributeSetRepository = $objectManager->get(AttributeSetRepositoryInterface::class); +$attributeSet = $getAttributeSetByName->execute('new_attribute_set'); + +if ($attributeSet) { + $attributeSetRepository->delete($attributeSet); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_based_on_default_without_country_of_manufacture.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_based_on_default_without_country_of_manufacture.php new file mode 100644 index 0000000000000..6939031140523 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_based_on_default_without_country_of_manufacture.php @@ -0,0 +1,52 @@ +get(ProductAttributeRepositoryInterface::class); +/** @var ProductAttributeInterface $attributeCountryOfManufacture */ +$attributeCountryOfManufacture = $attributeRepository->get('country_of_manufacture'); + +/** @var Magento\Eav\Model\Entity\Attribute\Set $attributeSet */ +$attributeSet = $objectManager->create(Set::class); +/** @var Type $entityType */ +$entityType = $objectManager->create(Type::class) + ->loadByCode(Magento\Catalog\Model\Product::ENTITY); +$data = [ + 'attribute_set_name' => 'custom_attribute_set_wout_com', + 'entity_type_id' => $entityType->getId(), + 'sort_order' => 300, +]; + +$attributeSet->setData($data); +$attributeSet->validate(); +$attributeSet->save(); +$attributeSet->initFromSkeleton($entityType->getDefaultAttributeSetId()); +/** @var Group $group */ +foreach ($attributeSet->getGroups() as $group) { + $groupAttributes = $group->getAttributes(); + $newAttributes = array_filter( + $groupAttributes, + function ($attribute) use ($attributeCountryOfManufacture) { + /** @var ProductAttributeInterface $attribute */ + return (int)$attribute->getAttributeId() !== (int)$attributeCountryOfManufacture->getAttributeId(); + } + ); + if (count($newAttributes) < count($groupAttributes)) { + $group->setAttributes($newAttributes); + break; + } +} +$attributeSet->save(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_based_on_default_without_country_of_manufacture_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_based_on_default_without_country_of_manufacture_rollback.php new file mode 100644 index 0000000000000..7b4719e151303 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_based_on_default_without_country_of_manufacture_rollback.php @@ -0,0 +1,58 @@ +create(AttributeSetRepositoryInterface::class); +/** @var Type $entityType */ +$entityType = $objectManager->create(Type::class) + ->loadByCode(Magento\Catalog\Model\Product::ENTITY); +$sortOrderBuilder = $objectManager->create(SortOrderBuilder::class); +/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ +$searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); +$searchCriteriaBuilder->addFilter('attribute_set_name', 'custom_attribute_set_wout_com'); +$searchCriteriaBuilder->addFilter('entity_type_id', $entityType->getId()); +$attributeSetIdSortOrder = $sortOrderBuilder + ->setField('attribute_set_id') + ->setDirection(Collection::SORT_ORDER_DESC) + ->create(); +$searchCriteriaBuilder->addSortOrder($attributeSetIdSortOrder); +$searchCriteriaBuilder->setPageSize(1); +$searchCriteriaBuilder->setCurrentPage(1); + +/** @var AttributeSetSearchResults $searchResult */ +$searchResult = $attributeSetRepository->getList($searchCriteriaBuilder->create()); +$items = $searchResult->getItems(); + +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +try { + if (count($items) > 0) { + /** @var Set $attributeSet */ + $attributeSet = reset($items); + $attributeSetRepository->deleteById($attributeSet->getId()); + } +} catch (\Exception $e) { + // In case of test run with DB isolation there is already no object in database + // since rollback fixtures called after transaction rollback. +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_image_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_image_attribute_rollback.php index 6dc8b60739f1f..626eb32a17051 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_image_attribute_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_image_attribute_rollback.php @@ -17,9 +17,10 @@ $attributeCollection->setCodeFilter('funny_image'); $attributeCollection->setEntityTypeFilter($entityType->getId()); $attributeCollection->setPageSize(1); -$attributeCollection->load(); -$attribute = $attributeCollection->fetchItem(); -$attribute->delete(); +$attribute = $attributeCollection->getFirstItem(); +if ($attribute->getId()) { + $attribute->delete(); +} // remove attribute set @@ -31,8 +32,9 @@ $attributeSetCollection->addFilter('entity_type_id', $entityType->getId()); $attributeSetCollection->setOrder('attribute_set_id'); // descending is default value $attributeSetCollection->setPageSize(1); -$attributeSetCollection->load(); /** @var \Magento\Eav\Model\Entity\Attribute\Set $attributeSet */ -$attributeSet = $attributeSetCollection->fetchItem(); -$attributeSet->delete(); +$attributeSet = $attributeSetCollection->getFirstItem(); +if ($attributeSet->getId()) { + $attributeSet->delete(); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_disabled.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_disabled.php new file mode 100644 index 0000000000000..26f4565d70b37 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_disabled.php @@ -0,0 +1,34 @@ +create(\Magento\Catalog\Model\Category::class); +$category->isObjectNew(true); +$category->setId(59) + ->setName('Category 1.1.1.1') + ->setParentId(5) + ->setPath('1/2/3/4/5/59') + ->setLevel(5) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setIsActive(true) + ->setPosition(1) + ->setCustomUseParentSettings(0) + ->setCustomDesign('Magento/blank') + ->setDescription('This is the description for Category 1.1.1.1') + ->save(); + +/** @var $category \Magento\Catalog\Model\Category */ +$category = $objectManager->create(\Magento\Catalog\Model\Category::class); + +// Category 1.1.1 +$category->load(4); +$category->setIsActive(false); +$category->save(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_disabled_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_disabled_rollback.php new file mode 100644 index 0000000000000..cc42bd6a09753 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_disabled_rollback.php @@ -0,0 +1,24 @@ +get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var Magento\Catalog\Model\ResourceModel\Category\Collection $collection */ +$collection = $objectManager->create(\Magento\Catalog\Model\ResourceModel\Category\Collection::class); +foreach ($collection->addAttributeToFilter('level', ['in' => [59]]) as $category) { + /** @var \Magento\Catalog\Model\Category $category */ + $category->delete(); +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_no_products_with_two_tree.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_no_products_with_two_tree.php new file mode 100644 index 0000000000000..3f9191244e78f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_no_products_with_two_tree.php @@ -0,0 +1,63 @@ +get(CategoryFactory::class); +$categoryResource = $objectManager->create(CategoryResource::class); +$categoryCollectionFactory = $objectManager->get(CollectionFactory::class); +/** @var Collection $categoryCollection */ +$categoryCollection = $categoryCollectionFactory->create(); + +/** @var $category2 Category */ +$category2 = $categoryCollection + ->addAttributeToFilter(CategoryInterface::KEY_NAME, 'Category 2') + ->setPageSize(1) + ->getFirstItem(); + +/** @var $category21 Category */ +$category21 = $categoryFactory->create(); +$category21->isObjectNew(true); +$category21->setName('Category 2.1') + ->setParentId($category2->getId()) + ->setPath($category2->getPath()) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setIsActive(true) + ->setPosition(1); +$categoryResource->save($category21); + +/** @var $category22 Category */ +$category22 = $categoryFactory->create(); +$category22->isObjectNew(true); +$category22->setName('Category 2.2') + ->setParentId($category2->getId()) + ->setPath($category2->getPath()) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setIsActive(true) + ->setPosition(2); +$categoryResource->save($category22); + +/** @var $category221 Category */ +$category221 = $categoryFactory->create(); +$category221->isObjectNew(true); +$category221->setName('Category 2.2.1') + ->setParentId($category22->getId()) + ->setPath($category22->getPath()) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setIsActive(true) + ->setPosition(1); +$categoryResource->save($category221); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_no_products_with_two_tree_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_no_products_with_two_tree_rollback.php new file mode 100644 index 0000000000000..f7fcb3a5b4928 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories_no_products_with_two_tree_rollback.php @@ -0,0 +1,8 @@ +create(); +$category->isObjectNew(true); +$category->setName('Root2 Category 1') + ->setParentId($rootCategory->getId()) + ->setAvailableSortBy(['position', 'name']) + ->setDefaultSortBy('name') + ->setIsActive(true) + ->setPosition(1); +$categoryRepository->save($category); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_in_second_root_category_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_in_second_root_category_rollback.php new file mode 100644 index 0000000000000..9c74b2fad7edd --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_in_second_root_category_rollback.php @@ -0,0 +1,8 @@ +get(StoreManagerInterface::class); +$categoryFactory = $objectManager->get(CategoryInterfaceFactory::class); +$productFactory = $objectManager->get(ProductInterfaceFactory::class); +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$categoryRepository = $objectManager->get(CategoryRepositoryInterface::class); +$currentStoreId = $storeManager->getStore()->getId(); + +$storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); +$category = $categoryFactory->create(); +$category->isObjectNew(true); +$category->setName('Category 999') + ->setParentId(2) + ->setLevel(2) + ->setAvailableSortBy('name') + ->setDefaultSortBy('name') + ->setIsActive(true) + ->setPosition(1); +$category = $categoryRepository->save($category); +$storeManager->setCurrentStore($currentStoreId); + +$product = $productFactory->create(); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setStoreId(Store::DEFAULT_STORE_ID) + ->setWebsiteIds([1]) + ->setName('Simple Product With Price 10') + ->setSku('simple1000') + ->setPrice(10) + ->setWeight(1) + ->setStockData(['use_config_manage_stock' => 0]) + ->setCategoryIds([$category->getId()]) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED); +$productRepository->save($product); + +$product = $productFactory->create(); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setStoreId(Store::DEFAULT_STORE_ID) + ->setWebsiteIds([1]) + ->setName('Simple Product With Price 20') + ->setSku('simple1001') + ->setPrice(20) + ->setWeight(1) + ->setStockData(['use_config_manage_stock' => 0]) + ->setCategoryIds([$category->getId()]) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products_on_two_websites.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products_on_two_websites.php new file mode 100644 index 0000000000000..91a5ece179ba6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products_on_two_websites.php @@ -0,0 +1,26 @@ +get(WebsiteRepositoryInterface::class); +$defaultWebsiteId = $websiteRepository->get('base')->getId(); +$websiteId = $websiteRepository->get('test')->getId(); + +$product = $productRepository->get('simple1000'); +$product->setWebsiteIds([$defaultWebsiteId, $websiteId]); +$productRepository->save($product); + +$product = $productRepository->get('simple1001'); +$product->setWebsiteIds([$defaultWebsiteId, $websiteId]); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products_on_two_websites_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products_on_two_websites_rollback.php new file mode 100644 index 0000000000000..d4c531c4da4db --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_different_price_products_on_two_websites_rollback.php @@ -0,0 +1,9 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$categoryCollectionFactory = $objectManager->get(CollectionFactory::class); +$categoryRepository = $objectManager->get(CategoryRepositoryInterface::class); +try { + $productRepository->deleteById('simple1000'); +} catch (NoSuchEntityException $e) { + //Already deleted. +} + +try { + $productRepository->deleteById('simple1001'); +} catch (NoSuchEntityException $e) { + //Already deleted. +} + +try { + $categoryCollection = $categoryCollectionFactory->create(); + $category = $categoryCollection + ->addAttributeToFilter(CategoryInterface::KEY_NAME, 'Category 999') + ->setPageSize(1) + ->getFirstItem(); + $categoryRepository->delete($category); +} catch (NoSuchEntityException $e) { + //Already deleted. +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_two_products.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_two_products.php new file mode 100644 index 0000000000000..31557fbe9a748 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_two_products.php @@ -0,0 +1,15 @@ +create(CategoryLinkManagementInterface::class); +$categoryLinkManagement->assignProductToCategories('simple2', [333]); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_two_products_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_two_products_rollback.php new file mode 100644 index 0000000000000..7500a92d1614c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/category_with_two_products_rollback.php @@ -0,0 +1,9 @@ +create(CategorySetup::class); +/** @var AttributeFactory $attributeFactory */ +$attributeFactory = $objectManager->get(AttributeFactory::class); +/** @var Attribute $attribute */ +$attribute = $attributeFactory->create(); +$entityTypeId = $installer->getEntityTypeId(ProductAttributeInterface::ENTITY_TYPE_CODE); + +if (!$attribute->loadByCode($entityTypeId, 'dropdown_attribute_with_html')->getId()) { + /** @var $installer CategorySetup */ + $installer = $objectManager->create(CategorySetup::class); + $attribute->setData( + [ + 'attribute_code' => 'dropdown_attribute_with_html', + 'entity_type_id' => $entityTypeId, + 'is_global' => 0, + 'is_user_defined' => 1, + 'frontend_input' => 'select', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 0, + 'is_filterable' => 0, + 'is_filterable_in_search' => 0, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 1, + 'used_in_product_listing' => 1, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Drop-Down Attribute'], + 'backend_type' => 'int', + 'option' => [ + 'value' => [ + 'option_1' => ['

Option 1

'], + 'option_2' => ['

Option 2

'], + 'option_3' => ['

Option 3

'], + ], + 'order' => [ + 'option_1' => 1, + 'option_2' => 2, + 'option_3' => 3, + ], + ], + ] + ); + $attribute->save(); + /* Assign attribute to attribute set */ + $installer->addAttributeToGroup( + $entityTypeId, + 'Default', + 'Attributes', + $attribute->getId() + ); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/dropdown_attribute_with_html_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/dropdown_attribute_with_html_rollback.php new file mode 100644 index 0000000000000..e6357b6967112 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/dropdown_attribute_with_html_rollback.php @@ -0,0 +1,27 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); + +try { + $attributeRepository->deleteById('dropdown_attribute_with_html'); +} catch (NoSuchEntityException $e) { + //already deleted +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/inactive_category.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/inactive_category.php new file mode 100644 index 0000000000000..e604d4e9f061b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/inactive_category.php @@ -0,0 +1,28 @@ +create(CategoryResource::class); +/** @var Category $category */ +$category = $objectManager->get(CategoryFactory::class)->create(); +$category->isObjectNew(true); +$data = [ + 'entity_id' => 111, + 'path' => '1/2/111', + 'name' => 'Test Category', + 'attribute_set_id' => $category->getDefaultAttributeSetId(), + 'parent_id' => 2, + 'is_active' => false, + 'include_in_menu' => true, +]; +$category->setData($data); +$categoryResource->save($category); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/inactive_category_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/inactive_category_rollback.php new file mode 100644 index 0000000000000..706a0bd9d05b0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/inactive_category_rollback.php @@ -0,0 +1,28 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var CategoryRepositoryInterface $categoryRepository */ +$categoryRepository = $objectManager->get(CategoryRepositoryInterface::class); + +try { + $category = $categoryRepository->get(111); + $categoryRepository->delete($category); +} catch (NoSuchEntityException $e) { + //category already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_related_products.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_related_products.php new file mode 100644 index 0000000000000..afd8d76a92b13 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_related_products.php @@ -0,0 +1,68 @@ +get(ProductFactory::class); +/** @var ProductLinkInterfaceFactory $linkFactory */ +$linkFactory = Bootstrap::getObjectManager()->get(ProductLinkInterfaceFactory::class); + +$rootProductCount = 10; +$rootSku = 'simple-related-'; +$simpleProducts = []; +for ($i =1; $i <= $rootProductCount; $i++) { + /** @var Product $product */ + $product = $factory->create(); + $product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setName('Simple Related Product #' .$i) + ->setSku($rootSku .$i) + ->setPrice(10) + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setWebsiteIds([1]) + ->setStockData(['qty' => 100, 'is_in_stock' => 1, 'manage_stock' => 1]) + ->save(); + $simpleProducts[$i] = $product; +} + +$linkTypes = ['crosssell', 'related', 'upsell']; +$linkedMaxCount = 10; +foreach ($simpleProducts as $simpleI => $product) { + $linkedCount = rand(1, $linkedMaxCount); + $links = []; + for ($i = 0; $i < $linkedCount; $i++) { + /** @var Product $linkedProduct */ + $linkedProduct = $factory->create(); + $linkedSku = 'related-product-' .$simpleI .'-' .$i; + $linkedProduct->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setName('Related product #' .$simpleI .'-' .$i) + ->setSku($linkedSku) + ->setPrice(10) + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setWebsiteIds([1]) + ->setStockData(['qty' => 100, 'is_in_stock' => 1, 'manage_stock' => 1]) + ->save(); + /** @var ProductLinkInterface $link */ + $link = $linkFactory->create(); + $link->setSku($product->getSku()); + $link->setLinkedProductSku($linkedSku); + $link->setPosition($i + 1); + $link->setLinkType($linkTypes[rand(0, count($linkTypes) - 1)]); + $links[] = $link; + } + $product->setProductLinks($links); + $product->save(); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_related_products_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_related_products_rollback.php new file mode 100644 index 0000000000000..2e728efc103c2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiple_related_products_rollback.php @@ -0,0 +1,34 @@ +get(ProductRepository::class); +/** @var SearchCriteriaBuilder $criteriaBuilder */ +$criteriaBuilder = Bootstrap::getObjectManager()->get(SearchCriteriaBuilder::class); +$listToDelete = $repo->getList($criteriaBuilder->addFilter('sku', $rootSku, 'like')->create()); +foreach ($listToDelete->getItems() as $item) { + try { + $repo->delete($item); + } catch (\Throwable $exception) { + //Could be deleted before + } +} +$listToDelete = $repo->getList($criteriaBuilder->addFilter('sku', $linkedSku, 'like')->create()); +foreach ($listToDelete->getItems() as $item) { + try { + $repo->delete($item); + } catch (\Throwable $exception) { + //Could be deleted before + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute_with_html.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute_with_html.php new file mode 100644 index 0000000000000..bb2bb5016b426 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute_with_html.php @@ -0,0 +1,68 @@ +create(CategorySetup::class); +/** @var $attributeFactory AttributeFactory */ +$attributeFactory = $objectManager->get(AttributeFactory::class); +/** @var $attribute Attribute */ +$attribute = $attributeFactory->create(); +$entityTypeId = $installer->getEntityTypeId(ProductAttributeInterface::ENTITY_TYPE_CODE); +if (!$attribute->loadByCode($entityTypeId, 'multiselect_attribute_with_html')->getAttributeId()) { + $attribute->setData( + [ + 'attribute_code' => 'multiselect_attribute_with_html', + 'entity_type_id' => $entityTypeId, + 'is_global' => 1, + 'is_user_defined' => 1, + 'frontend_input' => 'multiselect', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 0, + 'is_filterable' => 1, + 'is_filterable_in_search' => 0, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 0, + 'used_in_product_listing' => 0, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Multiselect Attribute'], + 'backend_type' => 'varchar', + 'backend_model' => ArrayBackend::class, + 'option' => [ + 'value' => [ + 'option_1' => ['

Option 1

'], + 'option_2' => ['

Option 2

'], + 'option_3' => ['

Option 3

'], + ], + 'order' => [ + 'option_1' => 1, + 'option_2' => 2, + 'option_3' => 3, + ], + ], + ] + ); + $attribute->save(); + /* Assign attribute to attribute set */ + $installer->addAttributeToGroup( + $entityTypeId, + 'Default', + 'General', + $attribute->getId() + ); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute_with_html_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute_with_html_rollback.php new file mode 100644 index 0000000000000..898c9bec0d635 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute_with_html_rollback.php @@ -0,0 +1,27 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); + +try { + $attributeRepository->deleteById('multiselect_attribute_with_html'); +} catch (NoSuchEntityException $e) { + //already deleted +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/out_of_stock_product_with_category.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/out_of_stock_product_with_category.php new file mode 100644 index 0000000000000..43670125e0315 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/out_of_stock_product_with_category.php @@ -0,0 +1,53 @@ +get(ProductFactory::class); +/** @var CategoryLinkManagementInterface $categoryLinkManagement */ +$categoryLinkManagement = $objectManager->get(CategoryLinkManagementInterface::class); +$product = $productFactory->create(); +$product->isObjectNew(true); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([1]) + ->setName('Simple Product Out Of Stock') + ->setSku('out-of-stock-product') + ->setPrice(10) + ->setWeight(1) + ->setShortDescription("Short description") + ->setTaxClassId(0) + ->setDescription('Description with html tag') + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([333]) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 0, + 'is_qty_decimal' => 0, + 'is_in_stock' => 0, + ] + ) + ->setCanSaveCustomOptions(true) + ->setHasOptions(true); +/** @var ProductRepositoryInterface $productRepositoryFactory */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/out_of_stock_product_with_category_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/out_of_stock_product_with_category_rollback.php new file mode 100644 index 0000000000000..ee07b064adc66 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/out_of_stock_product_with_category_rollback.php @@ -0,0 +1,30 @@ +get(Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); + +try { + $productRepository->deleteById('out-of-stock-product'); +} catch (NoSuchEntityException $e) { + //already removed +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_boolean_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_boolean_attribute.php new file mode 100644 index 0000000000000..34dccc2284445 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_boolean_attribute.php @@ -0,0 +1,50 @@ +get(AttributeRepositoryInterface::class); +/** @var Attribute $attribute */ +$attribute = $objectManager->create(Attribute::class); +/** @var $installer CategorySetup */ +$installer = $objectManager->create(CategorySetup::class); + +$attribute->setData( + [ + 'attribute_code' => 'boolean_attribute', + 'entity_type_id' => CategorySetup::CATALOG_PRODUCT_ENTITY_TYPE_ID, + 'is_global' => 0, + 'is_user_defined' => 1, + 'frontend_input' => 'boolean', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 1, + 'is_visible_in_advanced_search' => 1, + 'is_comparable' => 0, + 'is_filterable' => 1, + 'is_filterable_in_search' => 1, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 1, + 'used_in_product_listing' => 1, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Boolean Attribute'], + 'backend_type' => 'int', + 'source_model' => Boolean::class + ] +); + +$attributeRepository->save($attribute); + +/* Assign attribute to attribute set */ +$installer->addAttributeToGroup('catalog_product', 'Default', 'Attributes', $attribute->getId()); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_boolean_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_boolean_attribute_rollback.php new file mode 100644 index 0000000000000..c234eb91c84a6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_boolean_attribute_rollback.php @@ -0,0 +1,21 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var Attribute $attribute */ +$attribute = $objectManager->create(Attribute::class); +$attribute->load('boolean_attribute', 'attribute_code'); +$attribute->delete(); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_date_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_date_attribute.php new file mode 100644 index 0000000000000..43ea543a5909e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_date_attribute.php @@ -0,0 +1,51 @@ +create(CategorySetup::class); +$attribute = $objectManager->create(AttributeFactory::class)->create(); +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$entityType = $installer->getEntityTypeId(ProductAttributeInterface::ENTITY_TYPE_CODE); +if (!$attribute->loadByCode($entityType, 'date_attribute')->getAttributeId()) { + $attribute->setData( + [ + 'attribute_code' => 'date_attribute', + 'entity_type_id' => $entityType, + 'is_global' => 1, + 'is_user_defined' => 1, + 'frontend_input' => 'date', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 0, + 'is_filterable' => 0, + 'is_filterable_in_search' => 0, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 0, + 'used_in_product_listing' => 1, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Date Attribute'], + 'backend_type' => 'datetime', + ] + ); + $attributeRepository->save($attribute); + /* Assign attribute to attribute set */ + $installer->addAttributeToGroup( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'Default', + 'General', + $attribute->getId() + ); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_date_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_date_attribute_rollback.php new file mode 100644 index 0000000000000..b20da89be0136 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_date_attribute_rollback.php @@ -0,0 +1,25 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); + +try { + $attributeRepository->deleteById('date_attribute'); +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_decimal_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_decimal_attribute.php new file mode 100644 index 0000000000000..949475607d773 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_decimal_attribute.php @@ -0,0 +1,52 @@ +create(CategorySetup::class); +$attribute = $objectManager->create(AttributeFactory::class)->create(); +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$entityType = $installer->getEntityTypeId(ProductAttributeInterface::ENTITY_TYPE_CODE); +if (!$attribute->loadByCode($entityType, 'decimal_attribute')->getAttributeId()) { + $attribute->setData( + [ + 'attribute_code' => 'decimal_attribute', + 'entity_type_id' => $entityType, + 'is_global' => 1, + 'is_user_defined' => 1, + 'frontend_input' => 'price', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 0, + 'is_filterable' => 1, + 'is_filterable_in_search' => 0, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 0, + 'used_in_product_listing' => 1, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Decimal Attribute'], + 'backend_type' => 'decimal', + 'backend_model' => Price::class, + ] + ); + $attributeRepository->save($attribute); + /* Assign attribute to attribute set */ + $installer->addAttributeToGroup( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'Default', + 'General', + $attribute->getId() + ); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_decimal_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_decimal_attribute_rollback.php new file mode 100644 index 0000000000000..de187379bc99a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_decimal_attribute_rollback.php @@ -0,0 +1,25 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); + +try { + $attributeRepository->deleteById('decimal_attribute'); +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_dropdown_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_dropdown_attribute.php new file mode 100644 index 0000000000000..ae0c1d3613380 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_dropdown_attribute.php @@ -0,0 +1,67 @@ +create(Attribute::class); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +/** @var $installer CategorySetup */ +$installer = $objectManager->create(CategorySetup::class); +$entityTypeId = $installer->getEntityTypeId(ProductAttributeInterface::ENTITY_TYPE_CODE); + +if (!$attribute->loadByCode($entityTypeId, 'dropdown_attribute')->getId()) { + $attribute->setData( + [ + 'attribute_code' => 'dropdown_attribute', + 'entity_type_id' => $entityTypeId, + 'is_global' => 0, + 'is_user_defined' => 1, + 'frontend_input' => 'select', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 0, + 'is_filterable' => 0, + 'is_filterable_in_search' => 0, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 1, + 'used_in_product_listing' => 1, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Drop-Down Attribute'], + 'backend_type' => 'int', + 'option' => [ + 'value' => [ + 'option_1' => ['Option 1'], + 'option_2' => ['Option 2'], + 'option_3' => ['Option 3'], + ], + 'order' => [ + 'option_1' => 1, + 'option_2' => 2, + 'option_3' => 3, + ], + ], + ] + ); + $attributeRepository->save($attribute); + /* Assign attribute to attribute set */ + $installer->addAttributeToGroup( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'Default', + 'Attributes', + $attribute->getId() + ); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_dropdown_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_dropdown_attribute_rollback.php new file mode 100644 index 0000000000000..b48acc0ca0ac6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_dropdown_attribute_rollback.php @@ -0,0 +1,25 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); + +try { + $attributeRepository->deleteById('dropdown_attribute'); +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_image_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_image_attribute.php new file mode 100644 index 0000000000000..d8f0299f2a876 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_image_attribute.php @@ -0,0 +1,51 @@ +create(CategorySetup::class); +$attribute = $objectManager->create(AttributeFactory::class)->create(); +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$entityType = $installer->getEntityTypeId(ProductAttributeInterface::ENTITY_TYPE_CODE); +if (!$attribute->loadByCode($entityType, 'image_attribute')->getAttributeId()) { + $attribute->setData( + [ + 'attribute_code' => 'image_attribute', + 'entity_type_id' => $entityType, + 'is_global' => 1, + 'is_user_defined' => 1, + 'frontend_input' => 'media_image', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 0, + 'is_filterable' => 0, + 'is_filterable_in_search' => 0, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 0, + 'used_in_product_listing' => 1, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Image Attribute'], + 'backend_type' => 'varchar', + ] + ); + $attributeRepository->save($attribute); + /* Assign attribute to attribute set */ + $installer->addAttributeToGroup( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'Default', + 'General', + $attribute->getId() + ); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_image_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_image_attribute_rollback.php new file mode 100644 index 0000000000000..c9d28686741b8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_image_attribute_rollback.php @@ -0,0 +1,25 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); + +try { + $attributeRepository->deleteById('image_attribute'); +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description.php new file mode 100644 index 0000000000000..4f0d1ce6d47ff --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description.php @@ -0,0 +1,47 @@ +create(ProductRepositoryInterface::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); + +$currentStoreId = $storeManager->getStore()->getId(); +$secondStoreId = $storeManager->getStore('fixturestore')->getId(); + +$product = $productFactory->create(); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$storeManager->getWebsite(true)->getId()]) + ->setName('Simple Product One') + ->setSku('simple-different-short-description') + ->setPrice(10) + ->setWeight(18) + ->setStockData(['use_config_manage_stock' => 0]) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setShortDescription('First store view short description') + ->setStatus(Status::STATUS_ENABLED); +$productRepository->save($product); + +try { + $storeManager->setCurrentStore($secondStoreId); + $product = $productRepository->get('simple-different-short-description', false, $secondStoreId); + $product->setShortDescription('Second store view short description'); + $productRepository->save($product); +} finally { + $storeManager->setCurrentStore($currentStoreId); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description_rollback.php new file mode 100644 index 0000000000000..4191b9ff00acc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_multistore_different_short_description_rollback.php @@ -0,0 +1,29 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +$productRepository = $objectManager->create(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('simple-different-short-description'); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/../../Store/_files/core_fixturestore_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_multistore_with_url_key.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_multistore_with_url_key.php new file mode 100644 index 0000000000000..82a1cd4b98e35 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_multistore_with_url_key.php @@ -0,0 +1,50 @@ +create(Product::class); +$product->isObjectNew(true); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setStoreId(Store::DEFAULT_STORE_ID) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Simple Product 100') + ->setSku('simple_100') + ->setUrlKey('url-key') + ->setPrice(10) + ->setWeight(1) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED); + +/** @var StockItemInterface $stockItem */ +$stockItem = $objectManager->create(StockItemInterface::class); +$stockItem->setQty(100) + ->setIsInStock(true); +$extensionAttributes = $product->getExtensionAttributes(); +$extensionAttributes->setStockItem($stockItem); + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$product = $productRepository->save($product); + +$product->setStoreId(Store::DISTRO_STORE_ID) + ->setName('StoreTitle') + ->setUrlKey('url-key'); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_multistore_with_url_key_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_multistore_with_url_key_rollback.php new file mode 100644 index 0000000000000..7130a7c4a5612 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_multistore_with_url_key_rollback.php @@ -0,0 +1,22 @@ +get(ProductRepositoryInterface::class); +try { + $productRepository->deleteById('simple_100'); +} catch (NoSuchEntityException $e) { + //Entity already deleted +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_out_of_stock.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_out_of_stock.php index 6630c0d69e34f..a0e4369b986e4 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_out_of_stock.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_out_of_stock.php @@ -20,7 +20,7 @@ $product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) ->setAttributeSetId($product->getDefaultAttributeSetId()) ->setWebsiteIds([1]) - ->setName('Simple Product') + ->setName('Simple Product Out Of Stock') ->setSku('simple-out-of-stock') ->setPrice(10) ->setWeight(1) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_tax_none.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_tax_none.php new file mode 100644 index 0000000000000..35bf3705493ed --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_tax_none.php @@ -0,0 +1,42 @@ +get(ProductInterfaceFactory::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +/** @var $product \Magento\Catalog\Model\Product */ +$product = $productFactory->create(); +$product->setTypeId('simple') + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([1]) + ->setName('Simple Product Tax None') + ->setSku('simple-product-tax-none') + ->setPrice(205) + ->setWeight(1) + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_in_stock' => 1 + ] + ); + +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_tax_none_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_tax_none_rollback.php new file mode 100644 index 0000000000000..ceffb1c87d970 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_tax_none_rollback.php @@ -0,0 +1,33 @@ +get(ProductRepositoryInterface::class); +/** @var \Magento\Framework\Registry $registry */ +$registry =$objectManager->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +try { + /** @var ProductInterface $product */ + $product = $productRepository->get('simple-product-tax-none', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + // isolation on +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_country_of_manufacture.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_country_of_manufacture.php new file mode 100644 index 0000000000000..fd09b8bd1f0f2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_country_of_manufacture.php @@ -0,0 +1,49 @@ +reinitialize(); + +/** @var \Magento\TestFramework\ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +/** @var ProductRepositoryInterface $productRepository */ +$productFactory = $objectManager->create(ProductInterfaceFactory::class); +/** @var $product \Magento\Catalog\Model\Product */ + +$product = $productFactory->create(); +$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([1]) + ->setName('Simple Product With Country Of Manufacture') + ->setSku('simple_with_com') + ->setPrice(10) + ->setWeight(1) + ->setCountryOfManufacture('AO') + ->setShortDescription('Short description') + ->setTaxClassId(0) + ->setDescription('Description') + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ); + +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_country_of_manufacture_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_country_of_manufacture_rollback.php new file mode 100644 index 0000000000000..ffeb7eb143410 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_country_of_manufacture_rollback.php @@ -0,0 +1,32 @@ +create(ProductRepositoryInterface::class); + +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +try { + /** @var Product $product */ + $product = $productRepository->get('simple_with_com'); + $productRepository->delete($product); +} catch (\Exception $e) { + // In case of test run with DB isolation there is already no object in database + // since rollback fixtures called after transaction rollback. +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option.php new file mode 100644 index 0000000000000..1cd36e7f4726f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option.php @@ -0,0 +1,85 @@ +create(CategoryLinkManagementInterface::class); + +/** @var $product Product */ +$product = $objectManager->create(Product::class); +$product->isObjectNew(true); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Simple Product') + ->setSku('simple_with_custom_file_option') + ->setPrice(10) + ->setWeight(1) + ->setShortDescription('Short description') + ->setTaxClassId(0) + ->setDescription('Description with html tag') + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ) + ->setCanSaveCustomOptions(true) + ->setHasOptions(true); + +$option = [ + 'title' => 'file option', + 'type' => 'file', + 'is_require' => true, + 'sort_order' => 1, + 'price' => 30.0, + 'price_type' => 'percent', + 'sku' => 'sku3', + 'file_extension' => 'jpg, png, gif', + 'image_size_x' => 100, + 'image_size_y' => 100, +]; + +$customOptions = []; + +/** @var ProductCustomOptionInterfaceFactory $customOptionFactory */ +$customOptionFactory = $objectManager->create(ProductCustomOptionInterfaceFactory::class); +/** @var ProductCustomOptionInterface $customOption */ +$customOption = $customOptionFactory->create(['data' => $option]); +$customOption->setProductSku($product->getSku()); +$customOptions[] = $customOption; + +$product->setOptions($customOptions); + +/** @var ProductRepositoryInterface $productRepositoryFactory */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +$productRepository->save($product); + +$categoryLinkManagement->assignProductToCategories( + $product->getSku(), + [2] +); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option_rollback.php new file mode 100644 index 0000000000000..87321b2a080c0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_file_option_rollback.php @@ -0,0 +1,31 @@ +getInstance()->reinitialize(); + +/** @var Registry $registry */ +$registry = Bootstrap::getObjectManager()->get(Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = Bootstrap::getObjectManager() + ->get(ProductRepositoryInterface::class); +try { + $product = $productRepository->get('simple_with_custom_file_option', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute.php new file mode 100644 index 0000000000000..28a89ff883c4f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute.php @@ -0,0 +1,61 @@ +setData('is_used_for_promo_rules', 1); + +/** @var \Magento\Catalog\Model\ProductFactory $productFactory */ +$productFactory = $objectManager->get(Magento\Catalog\Model\ProductFactory::class); +$product = $productFactory->create(); +$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([1]) + ->setName('Simple Product with date') + ->setSku('simple_with_date') + ->setPrice(10) + ->setDescription('Description with html tag') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setCategoryIds([2]) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setDateAttribute(date('Y-m-d')) + ->setUrlKey('simple_with_date') + ->save(); + +$product2 = $productFactory->create(); +$product2->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([1]) + ->setName('Simple Product with date -1') + ->setSku('simple_with_date2') + ->setPrice(10) + ->setDescription('Description with html tag') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setCategoryIds([2]) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setDateAttribute(date('Y-m-d', strtotime(date('Y-m-d') . '-1 day'))) + ->setUrlKey('simple_with_date2') + ->save(); + +$product3 = $productFactory->create(); +$product3->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([1]) + ->setName('Simple Product with date +1') + ->setSku('simple_with_date3') + ->setPrice(10) + ->setDescription('Description with html tag') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setCategoryIds([2]) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setDateAttribute(date('Y-m-d', strtotime(date('Y-m-d') . '+1 day'))) + ->setUrlKey('simple_with_date3') + ->save(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute_rollback.php new file mode 100644 index 0000000000000..da61eb1d2332b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_date_attribute_rollback.php @@ -0,0 +1,44 @@ +getInstance()->reinitialize(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('simple_with_date', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { +} + +try { + $product = $productRepository->get('simple_with_date2', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { +} + +try { + $product = $productRepository->get('simple_with_date3', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +include __DIR__ . '/product_date_attribute_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites.php new file mode 100644 index 0000000000000..d7150d7ec41b1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites.php @@ -0,0 +1,40 @@ +create(ProductFactory::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->create(WebsiteRepositoryInterface::class); +$websiteId = $websiteRepository->get('test')->getId(); +$defaultWebsiteId = $websiteRepository->get('base')->getId(); + +$product = $productFactory->create(); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$defaultWebsiteId, $websiteId]) + ->setName('Simple Product on two websites') + ->setSku('simple-on-two-websites') + ->setPrice(10) + ->setDescription('Description') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); + +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites_rollback.php new file mode 100644 index 0000000000000..09cafb990a910 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_two_websites_rollback.php @@ -0,0 +1,29 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); + +try { + $productRepository->deleteById('simple-on-two-websites'); +} catch (NoSuchEntityException $e) { + //product already deleted +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/../../Store/_files/second_website_with_two_stores_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_varchar_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_varchar_attribute.php new file mode 100644 index 0000000000000..d23acfcdd196e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_varchar_attribute.php @@ -0,0 +1,51 @@ +create(CategorySetup::class); +$attribute = $objectManager->create(AttributeFactory::class)->create(); +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +$entityType = $installer->getEntityTypeId(ProductAttributeInterface::ENTITY_TYPE_CODE); +if (!$attribute->loadByCode($entityType, 'varchar_attribute')->getAttributeId()) { + $attribute->setData( + [ + 'attribute_code' => 'varchar_attribute', + 'entity_type_id' => $entityType, + 'is_global' => 1, + 'is_user_defined' => 1, + 'frontend_input' => 'text', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 0, + 'is_filterable' => 0, + 'is_filterable_in_search' => 0, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 0, + 'used_in_product_listing' => 1, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Varchar Attribute'], + 'backend_type' => 'varchar', + ] + ); + $attribute->save(); + /* Assign attribute to attribute set */ + $installer->addAttributeToGroup( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'Default', + 'General', + $attribute->getId() + ); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_varchar_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_varchar_attribute_rollback.php new file mode 100644 index 0000000000000..c238803a77108 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_varchar_attribute_rollback.php @@ -0,0 +1,25 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); + +try { + $attributeRepository->deleteById('varchar_attribute'); +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_test_attribute_set.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_test_attribute_set.php new file mode 100644 index 0000000000000..0616a22b1deee --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_test_attribute_set.php @@ -0,0 +1,42 @@ +get(ProductRepositoryInterface::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +/** @var GetAttributeSetByName $attributeSet */ +$attributeSet = $objectManager->get(GetAttributeSetByName::class); +$customAttributeSet = $attributeSet->execute('new_attribute_set'); +$product = $productFactory->create(); +$product + ->setTypeId('simple') + ->setAttributeSetId($customAttributeSet->getAttributeSetId()) + ->setWebsiteIds([1]) + ->setStoreId(Store::DEFAULT_STORE_ID) + ->setName('Simple Product') + ->setSku('simple') + ->setPrice(10) + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 22, 'is_in_stock' => 1]) + ->setQty(22); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_test_attribute_set_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_test_attribute_set_rollback.php new file mode 100644 index 0000000000000..1ec341cd4fd58 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_with_test_attribute_set_rollback.php @@ -0,0 +1,33 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var StockRegistryStorage $stockRegistryStorage */ +$stockRegistryStorage = $objectManager->get(StockRegistryStorage::class); +try { + $product = $productRepository->get('simple'); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already deleted. +} +$stockRegistryStorage->clean(); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +require __DIR__ . '/attribute_set_based_on_default_with_custom_group_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_upsell.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_upsell.php index 8df969a31019c..489666517419f 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_upsell.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_upsell.php @@ -4,6 +4,8 @@ * See COPYING.txt for license details. */ +require __DIR__ . '/products_upsell_rollback.php'; + /** @var $product \Magento\Catalog\Model\Product */ $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); $product->setTypeId( diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_upsell_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_upsell_rollback.php index a9c25dec58547..633eef3371a21 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_upsell_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_upsell_rollback.php @@ -3,6 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +use Magento\CatalogInventory\Model\StockRegistryStorage; + /** @var \Magento\Framework\Registry $registry */ $registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); @@ -25,3 +28,8 @@ $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); + +/** @var StockRegistryStorage $stockRegistryStorage */ +$stockRegistryStorage = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(StockRegistryStorage::class); +$stockRegistryStorage->clean(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_boolean_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_boolean_attribute.php new file mode 100644 index 0000000000000..65c8c5a251881 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_boolean_attribute.php @@ -0,0 +1,35 @@ +get(ProductRepositoryInterface::class); + +$yesIds = [101, 102, 104]; +$noIds = [103, 105]; + +foreach ($yesIds as $id) { + $product = $productRepository->getById($id); + $product->setBooleanAttribute(1); + $productRepository->save($product); +} +foreach ($noIds as $id) { + $product = $productRepository->getById($id); + $product->setBooleanAttribute(0); + $productRepository->save($product); +} +CacheCleaner::cleanAll(); +/** @var \Magento\Indexer\Model\Indexer\Collection $indexerCollection */ +$indexerCollection = $objectManager->get(\Magento\Indexer\Model\Indexer\Collection::class); +$indexerCollection->load(); +/** @var \Magento\Indexer\Model\Indexer $indexer */ +foreach ($indexerCollection->getItems() as $indexer) { + $indexer->reindexAll(); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_boolean_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_boolean_attribute_rollback.php new file mode 100644 index 0000000000000..8a70aead1f36d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_boolean_attribute_rollback.php @@ -0,0 +1,8 @@ +get(StoreManagerInterface::class); +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->create(ProductAttributeRepositoryInterface::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +/** @var CategoryRepositoryInterface $categoryRepository */ +$categoryRepository = $objectManager->create(CategoryRepositoryInterface::class); +$attribute = $attributeRepository->get('test_configurable'); + +$firstProduct = $productRepository->get('simple1'); +$firstProduct->setData('test_configurable', $attribute->getSource()->getOptionId('Option 1')); +$productRepository->save($firstProduct); + +$secondProduct = $productRepository->get('simple2'); +$secondProduct->setData('test_configurable', $attribute->getSource()->getOptionId('Option 2')); +$productRepository->save($secondProduct); + +$thirdProduct = $productRepository->get('simple3'); +$thirdProduct->setData('test_configurable', $attribute->getSource()->getOptionId('Option 2')); +$thirdProduct->setStatus(Status::STATUS_ENABLED); +$productRepository->save($thirdProduct); + +$oldStoreId = $storeManager->getStore()->getId(); +$storeManager->setCurrentStore(Store::DEFAULT_STORE_ID); +$category->addData(['available_sort_by' => 'position,name,price,test_configurable']); +try { + $categoryRepository->save($category); +} finally { + $storeManager->setCurrentStore($oldStoreId); +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_not_empty_layered_navigation_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_not_empty_layered_navigation_attribute_rollback.php new file mode 100644 index 0000000000000..ae6dae0667801 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_not_empty_layered_navigation_attribute_rollback.php @@ -0,0 +1,8 @@ +get('simple', false, null, true); +$tierPrices = $product->getTierPrices() ?? []; +$tierPriceExtensionAttributes = $tpExtensionAttributesFactory->create()->setWebsiteId($adminWebsite->getId()); +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => 1, + 'qty' => 3, + 'value' => 1 + ] + ] +)->setExtensionAttributes($tierPriceExtensionAttributes); +$product->setTierPrices($tierPrices); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_price_for_logged_user_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_price_for_logged_user_rollback.php new file mode 100644 index 0000000000000..e17fdac4904c9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_tier_price_for_logged_user_rollback.php @@ -0,0 +1,8 @@ +create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +/** @var $product \Magento\Catalog\Model\Product */ +$product = $objectManager->create(\Magento\Catalog\Model\Product::class); +$product->isObjectNew(true); + +$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([1]) + ->setName('Simple Product') + ->setSku('simple') + ->setPrice(100) + ->setWeight(1) + ->setTierPrice([0 => ['website_id' => 0, 'cust_group' => 1, 'price_qty' => 5, 'price' => 95]]) + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setCanSaveCustomOptions(true) + ->setStockData( + [ + 'qty' => 10, + 'is_in_stock' => 1, + 'manage_stock' => 1, + ] + ); +$productRepository->save($product); +$product->unsetData()->setOrigData(); + +$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([1]) + ->setName('Second Simple Product') + ->setSku('second_simple') + ->setPrice(200) + ->setWeight(1) + ->setTierPrice( + [ + 0 => [ + 'website_id' => 0, + 'cust_group' => \Magento\Customer\Model\Group::CUST_GROUP_ALL, + 'price_qty' => 10, + 'price' => 3, + 'percentage_value' => 3, + ], + ] + ) + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setCanSaveCustomOptions(true) + ->setStockData( + [ + 'qty' => 10, + 'is_in_stock' => 1, + 'manage_stock' => 1, + ] + ); + +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/two_simple_products_with_tier_price_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/two_simple_products_with_tier_price_rollback.php new file mode 100644 index 0000000000000..d5c3caf35536a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/two_simple_products_with_tier_price_rollback.php @@ -0,0 +1,18 @@ +get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +foreach (['simple', 'second_simple'] as $sku) { + try { + $product = $productRepository->get($sku, false, null, true); + $productRepository->delete($product); + } catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { + //Product already removed + } +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php index 183ba86ca7572..4753d947e9d3c 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php @@ -8,6 +8,10 @@ namespace Magento\CatalogImportExport\Model\Export; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Observer\SwitchPriceAttributeScopeOnConfigChange; +use Magento\Framework\App\Config\ReinitableConfigInterface; + /** * @magentoDataFixtureBeforeTransaction Magento/Catalog/_files/enable_reindex_schedule.php * @magentoAppIsolation enabled @@ -32,6 +36,11 @@ class ProductTest extends \PHPUnit\Framework\TestCase */ protected $fileSystem; + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + /** * Stock item attributes which must be exported * @@ -69,6 +78,7 @@ protected function setUp() $this->model = $this->objectManager->create( \Magento\CatalogImportExport\Model\Export\Product::class ); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); } /** @@ -404,6 +414,33 @@ public function testExportWithCustomOptions(): void self::assertSame($expectedData, $customOptionData); } + /** + * Check that no duplicate entities when multiple custom options used + * + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_options.php + */ + public function testExportWithMultipleOptions() + { + $expectedCount = 1; + $resultsFilename = 'export_results.csv'; + $this->model->setWriter( + $this->objectManager->create( + \Magento\ImportExport\Model\Export\Adapter\Csv::class + ) + ); + $exportData = $this->model->export(); + + $varDirectory = $this->objectManager->get(\Magento\Framework\Filesystem::class) + ->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::VAR_DIR); + $varDirectory->writeFile($resultsFilename, $exportData); + /** @var \Magento\Framework\File\Csv $csv */ + $csv = $this->objectManager->get(\Magento\Framework\File\Csv::class); + $data = $csv->getData($varDirectory->getAbsolutePath($resultsFilename)); + $actualCount = count($data) - 1; + + $this->assertSame($expectedCount, $actualCount); + } + /** * @param string $exportedCustomOption * @return array @@ -432,4 +469,70 @@ function ($input) { return $optionItems; } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php + * @magentoConfigFixture current_store catalog/price/scope 1 + * @magentoDbIsolation disabled + * @magentoAppArea adminhtml + */ + public function testExportProductWithTwoWebsites() + { + $globalStoreCode = 'admin'; + $secondStoreCode = 'fixture_second_store'; + + $expectedData = [ + $globalStoreCode => 10.0, + $secondStoreCode => 9.99 + ]; + + /** @var \Magento\Store\Model\Store $store */ + $store = $this->objectManager->create(\Magento\Store\Model\Store::class); + $reinitiableConfig = $this->objectManager->get(ReinitableConfigInterface::class); + $observer = $this->objectManager->get(\Magento\Framework\Event\Observer::class); + $switchPriceScope = $this->objectManager->get(SwitchPriceAttributeScopeOnConfigChange::class); + /** @var \Magento\Catalog\Model\Product\Action $productAction */ + $productAction = $this->objectManager->create(\Magento\Catalog\Model\Product\Action::class); + /** @var \Magento\Framework\File\Csv $csv */ + $csv = $this->objectManager->get(\Magento\Framework\File\Csv::class); + /** @var $varDirectory \Magento\Framework\Filesystem\Directory\WriteInterface */ + $varDirectory = $this->objectManager->get(\Magento\Framework\Filesystem::class) + ->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::VAR_DIR); + $secondStore = $store->load($secondStoreCode); + + $this->model->setWriter( + $this->objectManager->create( + \Magento\ImportExport\Model\Export\Adapter\Csv::class + ) + ); + + $reinitiableConfig->setValue('catalog/price/scope', \Magento\Store\Model\Store::PRICE_SCOPE_WEBSITE); + $switchPriceScope->execute($observer); + + $product = $this->productRepository->get('simple'); + $productId = $product->getId(); + $productAction->updateWebsites([$productId], [$secondStore->getWebsiteId()], 'add'); + $product->setStoreId($secondStore->getId()); + $product->setPrice('9.99'); + $product->getResource()->save($product); + + $exportData = $this->model->export(); + + $varDirectory->writeFile('test_product_with_two_websites.csv', $exportData); + $data = $csv->getData($varDirectory->getAbsolutePath('test_product_with_two_websites.csv')); + + $columnNumber = array_search('price', $data[0]); + $this->assertNotFalse($columnNumber); + + $pricesData = [ + $globalStoreCode => (float)$data[1][$columnNumber], + $secondStoreCode => (float)$data[2][$columnNumber], + ]; + + self::assertSame($expectedData, $pricesData); + + $reinitiableConfig->setValue('catalog/price/scope', \Magento\Store\Model\Store::PRICE_SCOPE_GLOBAL); + $switchPriceScope->execute($observer); + } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 76e1c640b9fba..4e2b73de301a3 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -3,12 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); /** * Test class for \Magento\CatalogImportExport\Model\Import\Product * * The "CouplingBetweenObjects" warning is caused by tremendous complexity of the original class - * */ namespace Magento\CatalogImportExport\Model\Import; @@ -16,18 +16,23 @@ use Magento\Catalog\Api\ProductCustomOptionRepositoryInterface; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface; use Magento\Framework\App\Bootstrap; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\ObjectManager; +use Magento\Framework\DataObject; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Filesystem; use Magento\Framework\Registry; use Magento\ImportExport\Model\Import; use Magento\ImportExport\Model\Import\Source\Csv; use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection; use Psr\Log\LoggerInterface; +use Magento\TestFramework\Helper\Bootstrap as BootstrapHelper; /** * Class ProductTest @@ -311,7 +316,6 @@ public function testStockState() * @throws \Magento\Framework\Exception\LocalizedException * @throws \Magento\Framework\Exception\NoSuchEntityException * @magentoAppIsolation enabled - * * @return void */ @@ -383,14 +387,14 @@ public function testSaveCustomOptions(string $importFile, string $sku, int $expe public function testSaveCustomOptionsWithMultipleStoreViews() { $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** @var \Magento\Store\Model\StoreManagerInterface $storeManager */ - $storeManager = $objectManager->get(\Magento\Store\Model\StoreManagerInterface::class); + /** @var StoreManagerInterface $storeManager */ + $storeManager = $objectManager->get(StoreManagerInterface::class); $storeCodes = [ 'admin', 'default', 'secondstore', ]; - /** @var \Magento\Store\Model\StoreManagerInterface $storeManager */ + /** @var StoreManagerInterface $storeManager */ $importFile = 'product_with_custom_options_and_multiple_store_views.csv'; $sku = 'simple'; $pathToFile = __DIR__ . '/_files/' . $importFile; @@ -846,6 +850,37 @@ public function testSaveMediaImage() $this->assertEquals('Additional Image Label Two', $additionalImageTwoItem->getLabel()); } + /** + * Tests that "hide_from_product_page" attribute is hidden after importing product images. + * + * @magentoDataFixture mediaImportImageFixture + * @magentoAppIsolation enabled + */ + public function testSaveHiddenImages() + { + $this->importDataForMediaTest('import_media_hidden_images.csv'); + $product = $this->getProductBySku('simple_new'); + $images = $product->getMediaGalleryEntries(); + + $hiddenImages = array_filter( + $images, + static function (DataObject $image) { + return (int)$image->getDisabled() === 1; + } + ); + + $this->assertCount(3, $hiddenImages); + + $imageItem = array_shift($hiddenImages); + $this->assertEquals('/m/a/magento_image.jpg', $imageItem->getFile()); + + $imageItem = array_shift($hiddenImages); + $this->assertEquals('/m/a/magento_thumbnail.jpg', $imageItem->getFile()); + + $imageItem = array_shift($hiddenImages); + $this->assertEquals('/m/a/magento_additional_image_two.jpg', $imageItem->getFile()); + } + /** * Test that new images should be added after the existing ones. * @@ -917,15 +952,15 @@ public function testSaveMediaImageError() */ public static function mediaImportImageFixture() { - /** @var \Magento\Framework\Filesystem\Directory\Write $mediaDirectory */ - $mediaDirectory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + /** @var \Magento\Framework\Filesystem\Directory\Write $varDirectory */ + $varDirectory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( \Magento\Framework\Filesystem::class )->getDirectoryWrite( - DirectoryList::MEDIA + DirectoryList::VAR_DIR ); - $mediaDirectory->create('import'); - $dirPath = $mediaDirectory->getAbsolutePath('import'); + $varDirectory->create('import' . DIRECTORY_SEPARATOR . 'images'); + $dirPath = $varDirectory->getAbsolutePath('import' . DIRECTORY_SEPARATOR . 'images'); $items = [ [ @@ -968,13 +1003,15 @@ public static function mediaImportImageFixture() */ public static function mediaImportImageFixtureRollback() { - /** @var \Magento\Framework\Filesystem\Directory\Write $mediaDirectory */ - $mediaDirectory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + $fileSystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( \Magento\Framework\Filesystem::class - )->getDirectoryWrite( - DirectoryList::MEDIA ); - $mediaDirectory->delete('import'); + /** @var \Magento\Framework\Filesystem\Directory\Write $mediaDirectory */ + $mediaDirectory = $fileSystem->getDirectoryWrite(DirectoryList::MEDIA); + + /** @var \Magento\Framework\Filesystem\Directory\Write $varDirectory */ + $varDirectory = $fileSystem->getDirectoryWrite(DirectoryList::VAR_DIR); + $varDirectory->delete('import'); $mediaDirectory->delete('catalog'); } @@ -983,13 +1020,13 @@ public static function mediaImportImageFixtureRollback() */ public static function mediaImportImageFixtureError() { - /** @var \Magento\Framework\Filesystem\Directory\Write $mediaDirectory */ - $mediaDirectory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + /** @var \Magento\Framework\Filesystem\Directory\Write $varDirectory */ + $varDirectory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( \Magento\Framework\Filesystem::class )->getDirectoryWrite( - DirectoryList::MEDIA + DirectoryList::VAR_DIR ); - $dirPath = $mediaDirectory->getAbsolutePath('import'); + $dirPath = $varDirectory->getAbsolutePath('import' . DIRECTORY_SEPARATOR . 'images'); $items = [ [ 'source' => __DIR__ . '/_files/magento_additional_image_error.jpg', @@ -1151,7 +1188,7 @@ public function testProductsWithMultipleStores() $product->load($id); $this->assertEquals('1', $product->getHasOptions()); - $objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->setCurrentStore('fixturestore'); + $objectManager->get(StoreManagerInterface::class)->setCurrentStore('fixturestore'); /** @var \Magento\Catalog\Model\Product $simpleProduct */ $simpleProduct = $objectManager->create(\Magento\Catalog\Model\Product::class); @@ -1540,6 +1577,49 @@ public function testValidateUrlKeysMultipleStores() $this->assertTrue($errors->getErrorsCount() == 0); } + /** + * @magentoDataFixture Magento/CatalogImportExport/_files/product_export_with_product_links_data.php + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ + public function testProductLinksWithEmptyValue() + { + // import data from CSV file + $pathToFile = __DIR__ . '/_files/products_to_import_with_product_links_with_empty_value.csv'; + $filesystem = BootstrapHelper::getObjectManager()->create(Filesystem::class); + + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + $source = $this->objectManager->create( + Csv::class, + [ + 'file' => $pathToFile, + 'directory' => $directory + ] + ); + $errors = $this->_model->setSource( + $source + )->setParameters( + [ + 'behavior' => Import::BEHAVIOR_APPEND, + 'entity' => 'catalog_product' + ] + )->validateData(); + + $this->assertTrue($errors->getErrorsCount() == 0); + $this->_model->importData(); + + $objectManager = BootstrapHelper::getObjectManager(); + $resource = $objectManager->get(ProductResource::class); + $productId = $resource->getIdBySku('simple'); + /** @var \Magento\Catalog\Model\Product $product */ + $product = BootstrapHelper::getObjectManager()->create(Product::class); + $product->load($productId); + + $this->assertEmpty($product->getCrossSellProducts()); + $this->assertEmpty($product->getUpSellProducts()); + } + /** * @magentoAppArea adminhtml * @magentoDbIsolation enabled @@ -2133,8 +2213,13 @@ private function importDataForMediaTest(string $fileName, int $expectedErrors = $uploader = $this->_model->getUploader(); $mediaPath = $appParams[DirectoryList::MEDIA][DirectoryList::PATH]; - $destDir = $directory->getRelativePath($mediaPath . '/catalog/product'); - $tmpDir = $directory->getRelativePath($mediaPath . '/import'); + $varPath = $appParams[DirectoryList::VAR_DIR][DirectoryList::PATH]; + $destDir = $directory->getRelativePath( + $mediaPath . DIRECTORY_SEPARATOR . 'catalog' . DIRECTORY_SEPARATOR . 'product' + ); + $tmpDir = $directory->getRelativePath( + $varPath . DIRECTORY_SEPARATOR . 'import' . DIRECTORY_SEPARATOR . 'images' + ); $directory->create($destDir); $this->assertTrue($uploader->setDestDir($destDir)); @@ -2162,13 +2247,20 @@ function ($output, $error) { * Load product by given product sku * * @param string $sku + * @param mixed $store * @return \Magento\Catalog\Model\Product */ - private function getProductBySku($sku) + private function getProductBySku($sku, $store = null) { $resource = $this->objectManager->get(\Magento\Catalog\Model\ResourceModel\Product::class); $productId = $resource->getIdBySku($sku); $product = $this->objectManager->create(\Magento\Catalog\Model\Product::class); + if ($store) { + /** @var StoreManagerInterface $storeManager */ + $storeManager = $this->objectManager->get(StoreManagerInterface::class); + $store = $storeManager->getStore($store); + $product->setStoreId($store->getId()); + } $product->load($productId); return $product; @@ -2333,6 +2425,7 @@ public function testImportWithFilesystemImages() * @magentoDataFixture Magento/Catalog/_files/attribute_set_with_renamed_group.php * @magentoDataFixture Magento/Catalog/_files/product_without_options.php * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDbIsolation enabled */ public function testImportDataChangeAttributeSet() { @@ -2584,6 +2677,73 @@ private function importFile(string $fileName): void $this->_model->importData(); } + /** + * Hide product images via hide_from_product_page attribute during import CSV. + * + * @magentoDataFixture mediaImportImageFixture + * @magentoDataFixture Magento/Catalog/_files/product_with_image.php + * + * @return void + */ + public function testImagesAreHiddenAfterImport(): void + { + $expectedActiveImages = [ + [ + 'file' => '/m/a/magento_additional_image_one.jpg', + 'label' => 'Additional Image Label One', + 'disabled' => '0', + ], + [ + 'file' => '/m/a/magento_additional_image_two.jpg', + 'label' => 'Additional Image Label Two', + 'disabled' => '0', + ], + ]; + + $expectedHiddenImage = [ + 'file' => '/m/a/magento_image.jpg', + 'label' => 'Image Alt Text', + 'disabled' => '1', + ]; + $expectedAllProductImages = array_merge( + [$expectedHiddenImage], + $expectedActiveImages + ); + + $this->importDataForMediaTest('hide_from_product_page_images.csv'); + $actualAllProductImages = []; + $product = $this->getProductBySku('simple'); + + // Check that new images were imported and existing image is disabled after import + $productMediaData = $product->getData('media_gallery'); + + $this->assertNotEmpty($productMediaData['images']); + $allProductImages = $productMediaData['images']; + $this->assertCount(3, $allProductImages, 'Images were imported incorrectly'); + + foreach ($allProductImages as $image) { + $actualAllProductImages[] = [ + 'file' => $image['file'], + 'label' => $image['label'], + 'disabled' => $image['disabled'], + ]; + } + + $this->assertEquals( + $expectedAllProductImages, + $actualAllProductImages, + 'Images are incorrect after import' + ); + + // Check that on storefront only enabled images are shown + $actualActiveImages = $product->getMediaGalleryImages(); + $this->assertSame( + $expectedActiveImages, + $actualActiveImages->toArray(['file', 'label', 'disabled'])['items'], + 'Hidden image is present on frontend after import' + ); + } + /** * Set the current admin session user based on a username * @@ -2601,4 +2761,184 @@ private function loginAdminUserWithUsername(string $username) ); $session->setUser($user); } + + /** + * Checking product images after Add/Update import failure + * + * @magentoDataFixture mediaImportImageFixture + * @magentoDataFixture Magento/CatalogImportExport/Model/Import/_files/import_with_filesystem_images.php + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * + * @return void + */ + public function testProductBaseImageAfterImport() + { + $this->importDataForMediaTest('import_media.csv'); + + $this->testImportWithNonExistingImage(); + + /** @var $productAfterImport \Magento\Catalog\Model\Product */ + $productAfterImport = $this->getProductBySku('simple_new'); + $this->assertNotEquals('/no/exists/image/magento_image.jpg', $productAfterImport->getData('image')); + } + + /** + * Tests that images are hidden only for a store view in "store_view_code". + * + * @magentoDataFixture mediaImportImageFixture + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/Catalog/_files/product_with_image.php + */ + public function testHideImageForStoreView() + { + $expectedImageFile = '/m/a/magento_image.jpg'; + $secondStoreCode = 'fixturestore'; + $productSku = 'simple'; + $this->importDataForMediaTest('import_hide_image_for_storeview.csv'); + $product = $this->getProductBySku($productSku); + $imageItems = $product->getMediaGalleryImages()->getItems(); + $this->assertCount(1, $imageItems); + $imageItem = array_shift($imageItems); + $this->assertEquals($expectedImageFile, $imageItem->getFile()); + $product = $this->getProductBySku($productSku, $secondStoreCode); + $imageItems = $product->getMediaGalleryImages()->getItems(); + $this->assertCount(0, $imageItems); + } + + /** + * Test that images labels are updated only for a store view in "store_view_code". + * + * @magentoDataFixture mediaImportImageFixture + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/Catalog/_files/product_with_image.php + */ + public function testChangeImageLabelForStoreView() + { + $expectedImageFile = '/m/a/magento_image.jpg'; + $expectedLabelForDefaultStoreView = 'Image Alt Text'; + $expectedLabelForSecondStoreView = 'Magento Logo'; + $secondStoreCode = 'fixturestore'; + $productSku = 'simple'; + $this->importDataForMediaTest('import_change_image_label_for_storeview.csv'); + $product = $this->getProductBySku($productSku); + $imageItems = $product->getMediaGalleryImages()->getItems(); + $this->assertCount(1, $imageItems); + $imageItem = array_shift($imageItems); + $this->assertEquals($expectedImageFile, $imageItem->getFile()); + $this->assertEquals($expectedLabelForDefaultStoreView, $imageItem->getLabel()); + $product = $this->getProductBySku($productSku, $secondStoreCode); + $imageItems = $product->getMediaGalleryImages()->getItems(); + $this->assertCount(1, $imageItems); + $imageItem = array_shift($imageItems); + $this->assertEquals($expectedImageFile, $imageItem->getFile()); + $this->assertEquals($expectedLabelForSecondStoreView, $imageItem->getLabel()); + } + + /** + * Test that configurable product images are imported correctly. + * + * @magentoDataFixture mediaImportImageFixture + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_attribute.php + */ + public function testImportConfigurableProductImages() + { + $this->importDataForMediaTest('import_configurable_product_multistore.csv'); + $expected = [ + 'import-configurable-option-1' => [ + [ + 'file' => '/m/a/magento_image.jpg', + 'label' => 'Base Image Label - Option 1', + ], + [ + 'file' => '/m/a/magento_small_image.jpg', + 'label' => 'Small Image Label - Option 1', + ], + [ + 'file' => '/m/a/magento_thumbnail.jpg', + 'label' => 'Thumbnail Image Label - Option 1', + ], + [ + 'file' => '/m/a/magento_additional_image_one.jpg', + 'label' => '', + ], + ], + 'import-configurable-option-2' => [ + [ + 'file' => '/m/a/magento_image.jpg', + 'label' => 'Base Image Label - Option 2', + ], + [ + 'file' => '/m/a/magento_small_image.jpg', + 'label' => 'Small Image Label - Option 2', + ], + [ + 'file' => '/m/a/magento_thumbnail.jpg', + 'label' => 'Thumbnail Image Label - Option 2', + ], + [ + 'file' => '/m/a/magento_additional_image_two.jpg', + 'label' => '', + ], + ], + 'import-configurable' => [ + [ + 'file' => '/m/a/magento_image.jpg', + 'label' => 'Base Image Label - Configurable', + ], + [ + 'file' => '/m/a/magento_small_image.jpg', + 'label' => 'Small Image Label - Configurable', + ], + [ + 'file' => '/m/a/magento_thumbnail.jpg', + 'label' => 'Thumbnail Image Label - Configurable', + ], + [ + 'file' => '/m/a/magento_additional_image_three.jpg', + 'label' => '', + ], + ] + ]; + $actual = []; + $products = ['import-configurable-option-1', 'import-configurable-option-2', 'import-configurable']; + foreach ($products as $sku) { + $product = $this->getProductBySku($sku); + $gallery = $product->getMediaGalleryImages(); + foreach ($gallery->getItems() as $item) { + $actual[$sku][] = $item->toArray(['file', 'label']); + } + } + $this->assertEquals($expected, $actual); + + $expected['import-configurable'] = [ + [ + 'file' => '/m/a/magento_image.jpg', + 'label' => 'Base Image Label - Configurable (fixturestore)', + ], + [ + 'file' => '/m/a/magento_small_image.jpg', + 'label' => 'Small Image Label - Configurable (fixturestore)', + ], + [ + 'file' => '/m/a/magento_thumbnail.jpg', + 'label' => 'Thumbnail Image Label - Configurable (fixturestore)', + ], + [ + 'file' => '/m/a/magento_additional_image_three.jpg', + 'label' => '', + ], + ]; + + $actual = []; + foreach ($products as $sku) { + $product = $this->getProductBySku($sku, 'fixturestore'); + $gallery = $product->getMediaGalleryImages(); + foreach ($gallery->getItems() as $item) { + $actual[$sku][] = $item->toArray(['file', 'label']); + } + } + $this->assertEquals($expected, $actual); + } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/UploaderTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/UploaderTest.php index 3961a77927314..d1d87b6916eb6 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/UploaderTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/UploaderTest.php @@ -16,6 +16,10 @@ */ class UploaderTest extends \Magento\TestFramework\Indexer\TestCase { + /** + * Random string appended to downloaded image name + */ + const RANDOM_STRING = 'BRV8TAuR2AT88OH0'; /** * @var \Magento\Framework\ObjectManagerInterface */ @@ -30,6 +34,10 @@ class UploaderTest extends \Magento\TestFramework\Indexer\TestCase * @var \Magento\CatalogImportExport\Model\Import\Uploader */ private $uploader; + /** + * @var \Magento\Framework\Filesystem\File\ReadInterface|\PHPUnit\Framework\MockObject\MockObject + */ + private $fileReader; /** * @inheritdoc @@ -37,7 +45,18 @@ class UploaderTest extends \Magento\TestFramework\Indexer\TestCase protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->uploader = $this->objectManager->create(\Magento\CatalogImportExport\Model\Import\Uploader::class); + $this->fileReader = $this->getMockForAbstractClass(\Magento\Framework\Filesystem\File\ReadInterface::class); + $fileReadFactory = $this->createMock(\Magento\Framework\Filesystem\File\ReadFactory::class); + $fileReadFactory->method('create')->willReturn($this->fileReader); + $random = $this->createMock(\Magento\Framework\Math\Random::class); + $random->method('getRandomString')->willReturn(self::RANDOM_STRING); + $this->uploader = $this->objectManager->create( + \Magento\CatalogImportExport\Model\Import\Uploader::class, + [ + 'random' => $random, + 'readFactory' => $fileReadFactory + ] + ); $filesystem = $this->objectManager->create(\Magento\Framework\Filesystem::class); @@ -60,16 +79,32 @@ protected function setUp() parent::setUp(); } + /** + * Tests move with external url + * + * @magentoAppIsolation enabled + * @return void + */ + public function testMoveWithExternalURL(): void + { + $fileName = 'http://magento.com/static/images/random_image.jpg'; + $this->fileReader->method('readAll')->willReturn(file_get_contents($this->getTestImagePath())); + $this->uploader->move($fileName); + $destFilePath = $this->uploader->getTmpDir() . '/' . 'random_image_' . self::RANDOM_STRING . '.jpg'; + $this->assertTrue($this->directory->isExist($destFilePath)); + } + /** * @magentoAppIsolation enabled * @return void */ public function testMoveWithValidFile(): void { - $fileName = 'magento_additional_image_one.jpg'; + $testImagePath = $this->getTestImagePath(); + $fileName = basename($testImagePath); $filePath = $this->directory->getAbsolutePath($this->uploader->getTmpDir() . '/' . $fileName); //phpcs:ignore - copy(__DIR__ . '/_files/' . $fileName, $filePath); + copy($testImagePath, $filePath); $this->uploader->move($fileName); $this->assertTrue($this->directory->isExist($this->uploader->getTmpDir() . '/' . $fileName)); } @@ -84,15 +119,17 @@ public function testMoveWithValidFile(): void public function testMoveWithFileOutsideTemp(): void { $tmpDir = $this->uploader->getTmpDir(); - if (!$this->directory->create($newTmpDir = $tmpDir .'/test1')) { + $newTmpDir = $tmpDir . '/test1'; + if (!$this->directory->create($newTmpDir)) { throw new \RuntimeException('Failed to create temp dir'); } $this->uploader->setTmpDir($newTmpDir); - $fileName = 'magento_additional_image_one.jpg'; + $testImagePath = $this->getTestImagePath(); + $fileName = basename($testImagePath); $filePath = $this->directory->getAbsolutePath($tmpDir . '/' . $fileName); //phpcs:ignore - copy(__DIR__ . '/_files/' . $fileName, $filePath); - $this->uploader->move('../' .$fileName); + copy($testImagePath, $filePath); + $this->uploader->move('../' . $fileName); $this->assertTrue($this->directory->isExist($tmpDir . '/' . $fileName)); } @@ -111,4 +148,14 @@ public function testMoveWithInvalidFile(): void $this->uploader->move($fileName); $this->assertFalse($this->directory->isExist($this->uploader->getTmpDir() . '/' . $fileName)); } + + /** + * Get the full path to the test image + * + * @return string + */ + private function getTestImagePath(): string + { + return __DIR__ . '/_files/magento_additional_image_one.jpg'; + } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/hide_from_product_page_images.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/hide_from_product_page_images.csv new file mode 100644 index 0000000000000..5902723ae5024 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/hide_from_product_page_images.csv @@ -0,0 +1,2 @@ +sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_label1,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,crosssell_skus,upsell_skus,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values +simple,,Default,simple,,base,Simple Product,,,,1,Taxable Goods,"Catalog, Search",10,,,,,meta title,meta keyword,meta description,magento_additional_image_one.jpg,Additional Image Label One,magento_additional_image_one.jpg,Small Additional Image Label One,magento_additional_image_one.jpg,Thumbnail Label,magento_additional_image_one.jpg,Additional Image Label One,,,,,Block after Info Column,,,,,,,,,,,,,"has_options=1,quantity_and_stock_status=In Stock,required_options=1",100,0,1,0,0,1,1,1,10000,1,1,1,1,1,0,1,1,0,0,0,1,,,,"magento_additional_image_one.jpg,magento_additional_image_two.jpg","Additional Image Label One,Additional Image Label Two",/m/a/magento_image.jpg,,,,,, diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_change_image_label_for_storeview.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_change_image_label_for_storeview.csv new file mode 100644 index 0000000000000..95321df2b6b01 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_change_image_label_for_storeview.csv @@ -0,0 +1,2 @@ +"sku","store_view_code","base_image", "base_image_label" +"simple","fixturestore","/m/a/magento_image.jpg", "Magento Logo" diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_configurable_product_multistore.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_configurable_product_multistore.csv new file mode 100644 index 0000000000000..c13e9b10e90ae --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_configurable_product_multistore.csv @@ -0,0 +1,5 @@ +"sku","store_view_code","product_type","categories","name","base_image","base_image_label","thumbnail_image","thumbnail_image_label","small_image","small_image_label","additional_images","weight","product_online","tax_class_name","visibility","price","url_key","allow_backorders","min_cart_qty","max_cart_qty","is_in_stock","additional_attributes","configurable_variations","configurable_variation_labels","qty","attribute_set_code" +"import-configurable-option-1",,"simple","Default Category","import-configurable-option-1","magento_image.jpg","Base Image Label - Option 1","magento_thumbnail.jpg","Thumbnail Image Label - Option 1","magento_small_image.jpg","Small Image Label - Option 1","magento_additional_image_one.jpg","1","1","Taxable Goods","Not Visible Individually","14.99","import-configurable-option-1-key","0","1","10","1","test_configurable=Option 1",,,"100","Default" +"import-configurable-option-2",,"simple","Default Category","import-configurable-option-2","magento_image.jpg","Base Image Label - Option 2","magento_thumbnail.jpg","Thumbnail Image Label - Option 2","magento_small_image.jpg","Small Image Label - Option 2","magento_additional_image_two.jpg","1","1","Taxable Goods","Not Visible Individually","14.99","import-configurable-option-2-key","0","1","10","1","test_configurable=Option 2",,,"100","Default" +"import-configurable",,"configurable","Default Category","import-configurable","magento_image.jpg","Base Image Label - Configurable","magento_thumbnail.jpg","Thumbnail Image Label - Configurable","magento_small_image.jpg","Small Image Label - Configurable","magento_additional_image_three.jpg","1","1","Taxable Goods","Catalog, Search","14.99","import-configurable-key","0",,,,,"sku=import-configurable-option-1,test_configurable=Option 1|sku=import-configurable-option-2,test_configurable=Option 2","test_configurable=test_configurable=test_configurable",,"Default" +"import-configurable","fixturestore","configurable",,"import-configurable (fixturestore)","magento_image.jpg","Base Image Label - Configurable (fixturestore)","magento_thumbnail.jpg","Thumbnail Image Label - Configurable (fixturestore)","magento_small_image.jpg","Small Image Label - Configurable (fixturestore)",,,,,,,,,,,,,,,,"Default" diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_hide_image_for_storeview.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_hide_image_for_storeview.csv new file mode 100644 index 0000000000000..8400dc17d2a29 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_hide_image_for_storeview.csv @@ -0,0 +1,2 @@ +"sku","store_view_code","hide_from_product_page" +"simple","fixturestore","/m/a/magento_image.jpg" diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_hidden_images.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_hidden_images.csv new file mode 100644 index 0000000000000..1c1bebee57578 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_hidden_images.csv @@ -0,0 +1,2 @@ +sku,store_view_code,attribute_set_code,product_type,categories,product_websites,name,description,short_description,weight,product_online,tax_class_name,visibility,price,special_price,special_price_from_date,special_price_to_date,url_key,meta_title,meta_keywords,meta_description,base_image,base_image_label,small_image,small_image_label,thumbnail_image,thumbnail_image_label,swatch_image,swatch_image_label1,created_at,updated_at,new_from_date,new_to_date,display_product_options_in,map_price,msrp_price,map_enabled,gift_message_available,custom_design,custom_design_from,custom_design_to,custom_layout_update,page_layout,product_options_container,msrp_display_actual_price_type,country_of_manufacture,additional_attributes,qty,out_of_stock_qty,use_config_min_qty,is_qty_decimal,allow_backorders,use_config_backorders,min_cart_qty,use_config_min_sale_qty,max_cart_qty,use_config_max_sale_qty,is_in_stock,notify_on_stock_below,use_config_notify_stock_qty,manage_stock,use_config_manage_stock,use_config_qty_increments,qty_increments,use_config_enable_qty_inc,enable_qty_increments,is_decimal_divided,website_id,related_skus,crosssell_skus,upsell_skus,additional_images,additional_image_labels,hide_from_product_page,custom_options,bundle_price_type,bundle_sku_type,bundle_price_view,bundle_weight_type,bundle_values,associated_skus +simple_new,,Default,simple,,base,New Product,,,,1,Taxable Goods,"Catalog, Search",10,,,,new-product,New Product,New Product,New Product,magento_image.jpg,Image Label,magento_small_image.jpg,Small Image Label,magento_thumbnail.jpg,Thumbnail Label,magento_image.jpg,Image Label,10/20/2015 7:05,10/20/2015 7:05,,,Block after Info Column,,,,,,,,,,,,,"has_options=1,quantity_and_stock_status=In Stock,required_options=1",100,0,1,0,0,1,1,1,10000,1,1,1,1,1,0,1,1,0,0,0,1,,,,"magento_additional_image_one.jpg, magento_additional_image_two.jpg","Additional Image Label One,Additional Image Label Two","magento_image.jpg,magento_thumbnail.jpg,magento_additional_image_two.jpg",,,,,,, diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_with_filesystem_images.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_with_filesystem_images.php index 04b3092c8fa8a..0ee59aedd8979 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_with_filesystem_images.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_with_filesystem_images.php @@ -4,16 +4,23 @@ * See COPYING.txt for license details. */ -/** @var \Magento\Framework\Filesystem\Directory\Write $mediaDirectory */ -$mediaDirectory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( +/** @var \Magento\Framework\Filesystem $fileSystem */ +$fileSystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( \Magento\Framework\Filesystem::class -)->getDirectoryWrite( +); +/** @var \Magento\Framework\Filesystem\Directory\Write $mediaDirectory */ +$mediaDirectory = $fileSystem->getDirectoryWrite( \Magento\Framework\App\Filesystem\DirectoryList::MEDIA ); +/** @var \Magento\Framework\Filesystem\Directory\Write $varDirectory */ +$varDirectory = $fileSystem->getDirectoryWrite( + \Magento\Framework\App\Filesystem\DirectoryList::VAR_DIR +); $path = 'catalog' . DIRECTORY_SEPARATOR . 'product'; +$varImagesPath = 'import' . DIRECTORY_SEPARATOR . 'images'; // Is required for using importDataForMediaTest method. -$mediaDirectory->create('import'); +$varDirectory->create($varImagesPath); $mediaDirectory->create($path); $dirPath = $mediaDirectory->getAbsolutePath($path); diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_with_filesystem_images_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_with_filesystem_images_rollback.php index 5c1db3ca045a6..a984cbf2e3529 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_with_filesystem_images_rollback.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_with_filesystem_images_rollback.php @@ -4,11 +4,17 @@ * See COPYING.txt for license details. */ -/** @var \Magento\Framework\Filesystem\Directory\Write $mediaDirectory */ -$mediaDirectory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( +/** @var \Magento\Framework\Filesystem $fileSystem */ +$fileSystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( \Magento\Framework\Filesystem::class -)->getDirectoryWrite( +); +/** @var \Magento\Framework\Filesystem\Directory\Write $mediaDirectory */ +$mediaDirectory = $fileSystem->getDirectoryWrite( \Magento\Framework\App\Filesystem\DirectoryList::MEDIA ); -$mediaDirectory->delete('import'); +/** @var \Magento\Framework\Filesystem\Directory\Write $varDirectory */ +$varDirectory = $fileSystem->getDirectoryWrite( + \Magento\Framework\App\Filesystem\DirectoryList::VAR_DIR +); +$varDirectory->delete('import'); $mediaDirectory->delete('catalog'); diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_product_links_with_empty_value.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_product_links_with_empty_value.csv new file mode 100644 index 0000000000000..fbbf6e2fb33f2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_product_links_with_empty_value.csv @@ -0,0 +1,2 @@ +sku,crosssell_skus,crosssell_position,upsell_skus,upsell_position +simple,__EMPTY__VALUE__,__EMPTY__VALUE__,__EMPTY__VALUE__,__EMPTY__VALUE__ diff --git a/dev/tests/integration/testsuite/Magento/CatalogInventory/Block/Adminhtml/Form/Field/CustomergroupTest.php b/dev/tests/integration/testsuite/Magento/CatalogInventory/Block/Adminhtml/Form/Field/CustomergroupTest.php index 527828a92dea8..b7be72e9ff827 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogInventory/Block/Adminhtml/Form/Field/CustomergroupTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogInventory/Block/Adminhtml/Form/Field/CustomergroupTest.php @@ -22,11 +22,12 @@ protected function setUp() public function testToHtml() { - $this->_block->setClass('customer_group_select'); + $this->_block->setClass('customer_group_select admin__control-select'); $this->_block->setId('123'); $this->_block->setTitle('Customer Group'); $this->_block->setInputName('groups[item_options]'); - $expectedResult = ''; diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php index 495d19a2745e5..ce182f56898ef 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/Model/Indexer/Product/PriceTest.php @@ -5,12 +5,13 @@ */ namespace Magento\CatalogRule\Model\Indexer\Product; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\CatalogRule\Model\ResourceModel\Rule; -use Magento\Catalog\Model\ResourceModel\Product\Collection; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ProductRepository; +use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\CatalogRule\Model\ResourceModel\Rule; use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\Api\SortOrder; +use Magento\TestFramework\Helper\Bootstrap; class PriceTest extends \PHPUnit\Framework\TestCase { @@ -56,6 +57,46 @@ public function testPriceApplying() $this->assertEquals($simpleProduct->getFinalPrice(), $confProduct->getMinimalPrice()); } + /** + * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/simple_products.php + * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/catalog_rule_50_percent_off.php + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ + public function testPriceForSecondStore() + { + $customerGroupId = 1; + $websiteId = 2; + /** @var ProductRepository $productRepository */ + $productRepository = Bootstrap::getObjectManager()->create( + ProductRepository::class + ); + $simpleProduct = $productRepository->get('simple3'); + $simpleProduct->setPriceCalculation(true); + $this->assertEquals('simple3', $simpleProduct->getSku()); + $this->assertFalse( + $this->resourceRule->getRulePrice( + new \DateTime(), + $websiteId, + $customerGroupId, + $simpleProduct->getId() + ) + ); + $indexerBuilder = Bootstrap::getObjectManager()->get( + \Magento\CatalogRule\Model\Indexer\IndexBuilder::class + ); + $indexerBuilder->reindexById($simpleProduct->getId()); + $this->assertEquals( + $this->resourceRule->getRulePrice( + new \DateTime(), + $websiteId, + $customerGroupId, + $simpleProduct->getId() + ), + 25 + ); + } + /** * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/simple_products.php * @magentoDataFixtureBeforeTransaction Magento/CatalogRule/_files/rule_by_attribute.php diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged.php index 48ed1956c88a1..52357706b0610 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged.php @@ -31,3 +31,4 @@ $indexBuilder = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() ->get(\Magento\CatalogRule\Model\Indexer\IndexBuilder::class); $indexBuilder->reindexFull(); +sleep(1); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged_rollback.php index e7985e8d6b149..7c97c5926ce4f 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged_rollback.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_10_off_not_logged_rollback.php @@ -26,3 +26,4 @@ /** @var \Magento\CatalogRule\Model\Indexer\IndexBuilder $indexBuilder */ $indexBuilder = $objectManager->get(\Magento\CatalogRule\Model\Indexer\IndexBuilder::class); $indexBuilder->reindexFull(); +sleep(1); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_50_percent_off.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_50_percent_off.php new file mode 100644 index 0000000000000..ca5c8ecbbd59f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_50_percent_off.php @@ -0,0 +1,36 @@ +get(\Magento\CatalogRule\Model\RuleFactory::class)->create(); +$catalogRule->loadPost( + [ + 'name' => 'Test Catalog Rule 50% off', + 'is_active' => '1', + 'stop_rules_processing' => 0, + 'website_ids' => [2], + 'customer_group_ids' => [0, 1], + 'discount_amount' => 50, + 'simple_action' => 'by_percent', + 'from_date' => '', + 'to_date' => '', + 'sort_order' => 0, + 'sub_is_enable' => 0, + 'sub_discount_amount' => 0, + 'conditions' => [], + ] +); +$catalogRule->save(); +/** @var \Magento\CatalogRule\Model\Indexer\IndexBuilder $indexBuilder */ +$indexBuilder = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\CatalogRule\Model\Indexer\IndexBuilder::class); +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_50_percent_off_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_50_percent_off_rollback.php new file mode 100644 index 0000000000000..404bfd021492d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_50_percent_off_rollback.php @@ -0,0 +1,28 @@ +create(\Magento\CatalogRule\Model\ResourceModel\Rule::class); + +//Retrieve second rule by name +$select = $catalogRuleResource->getConnection()->select(); +$select->from($catalogRuleResource->getMainTable(), 'rule_id'); +$select->where('name = ?', 'Test Catalog Rule 50% off'); +$ruleId = $catalogRuleResource->getConnection()->fetchOne($select); + +try { + /** @var \Magento\CatalogRule\Api\CatalogRuleRepositoryInterface $ruleRepository */ + $ruleRepository = $objectManager->create(\Magento\CatalogRule\Api\CatalogRuleRepositoryInterface::class); + $ruleRepository->deleteById($ruleId); +} catch (\Exception $ex) { + //Nothing to remove +} + +/** @var \Magento\CatalogRule\Model\Indexer\IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(\Magento\CatalogRule\Model\Indexer\IndexBuilder::class); +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_6_off_logged_user.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_6_off_logged_user.php new file mode 100644 index 0000000000000..3343a837d17db --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_6_off_logged_user.php @@ -0,0 +1,38 @@ +get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $catalogRuleRepository */ +$catalogRuleRepository = $objectManager->get(CatalogRuleRepositoryInterface::class); +/** @var RuleInterfaceFactory $catalogRuleFactory */ +$catalogRuleFactory = $objectManager->get(RuleInterfaceFactory::class); +$catalogRule = $catalogRuleFactory->create( + [ + 'data' => [ + RuleInterface::IS_ACTIVE => 1, + RuleInterface::NAME => 'Test Catalog Rule for logged user', + 'customer_group_ids' => 1, + RuleInterface::DISCOUNT_AMOUNT => 6, + 'website_ids' => [1], + RuleInterface::SIMPLE_ACTION => 'by_fixed', + RuleInterface::STOP_RULES_PROCESSING => false, + RuleInterface::SORT_ORDER => 0, + 'sub_is_enable' => 0, + 'sub_discount_amount' => 0, + ] + ] +); +$catalogRuleRepository->save($catalogRule); +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_6_off_logged_user_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_6_off_logged_user_rollback.php new file mode 100644 index 0000000000000..37c2c8c1f2173 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/catalog_rule_6_off_logged_user_rollback.php @@ -0,0 +1,29 @@ +get(IndexBuilder::class); +/** @var CatalogRuleRepositoryInterface $ruleRepository */ +$ruleRepository = $objectManager->create(CatalogRuleRepositoryInterface::class); +/** @var CollectionFactory $ruleCollectionFactory */ +$ruleCollectionFactory = $objectManager->get(CollectionFactory::class); +$ruleCollection = $ruleCollectionFactory->create(); +$ruleCollection->addFieldToFilter('name', ['eq' => 'Test Catalog Rule for logged user']); +$ruleCollection->setPageSize(1); +/** @var Rule $rule */ +$rule = $ruleCollection->getFirstItem(); +if ($rule->getId()) { + $ruleRepository->delete($rule); +} +$indexBuilder->reindexFull(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php index 84ce4e1bca87c..c40b641e58b1d 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products.php @@ -26,12 +26,14 @@ ->setPrice(10) ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setStockData([ - 'use_config_manage_stock' => 1, - 'qty' => 100, - 'is_qty_decimal' => 0, - 'is_in_stock' => 1, - ]); + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ); $productRepository->save($product); $productAction = $objectManager->get(\Magento\Catalog\Model\Product\Action::class); $productAction->updateAttributes([$product->getId()], ['test_attribute' => 'test_attribute_value'], $store->getId()); @@ -46,10 +48,52 @@ ->setPrice(9.9) ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setStockData([ - 'use_config_manage_stock' => 1, - 'qty' => 100, - 'is_qty_decimal' => 0, - 'is_in_stock' => 1, - ]); + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ); $productRepository->save($product); +$store = $objectManager->create(\Magento\Store\Model\Store::class); +$store->load('second_store_view', 'code'); +/** + * @var Website $website + */ +$website2 = $objectManager->get(\Magento\Store\Model\Website::class); +$website2->load('second_website', 'code'); +if (!$website2->getId()) { + /** @var \Magento\Store\Model\Website $website */ + $website2->setData( + [ + 'code' => 'second_website', + 'name' => 'Second Website', + + ] + ); + + $website2->save(); +} +$product = $objectManager->create(\Magento\Catalog\Model\Product::class) + ->setTypeId('simple') + ->setId(3) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([$website2->getId()]) + ->setName('Simple Product 3') + ->setSku('simple3') + ->setPrice(50) + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ); +$productRepository->save($product); +$productAction = $objectManager->get(\Magento\Catalog\Model\Product\Action::class); +$productAction->updateAttributes([$product->getId()], ['test_attribute' => 'test_attribute_value'], $store->getId()); diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products_rollback.php index 6625b1926fc10..e641f9f32df40 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products_rollback.php +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/simple_products_rollback.php @@ -18,7 +18,7 @@ /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ $productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); -foreach (['simple1', 'simple2'] as $sku) { +foreach (['simple1', 'simple2','simple3'] as $sku) { try { $product = $productRepository->get($sku, false, null, true); $productRepository->delete($product); diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Advanced/ResultTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Advanced/ResultTest.php new file mode 100644 index 0000000000000..32b7df03f922d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Advanced/ResultTest.php @@ -0,0 +1,174 @@ +productAttributeRepository = $this->_objectManager->create(ProductAttributeRepositoryInterface::class); + } + + /** + * Advanced search test by difference product attributes. + * + * @magentoConfigFixture default/catalog/search/engine mysql + * @magentoAppArea frontend + * @magentoDataFixture Magento/CatalogSearch/_files/product_for_search.php + * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php + * @dataProvider searchStringDataProvider + * + * @param array $searchParams + * @return void + */ + public function testExecute(array $searchParams): void + { + if ('' !== $searchParams['test_searchable_attribute']) { + $searchParams['test_searchable_attribute'] = $this->getAttributeOptionValueByOptionLabel( + 'test_searchable_attribute', + $searchParams['test_searchable_attribute'] + ); + } + + $this->getRequest()->setQuery( + $this->_objectManager->create( + Parameters::class, + [ + 'values' => $searchParams + ] + ) + ); + $this->dispatch('catalogsearch/advanced/result'); + $responseBody = $this->getResponse()->getBody(); + $this->assertContains('Simple product name', $responseBody); + } + + /** + * Data provider with strings for quick search. + * + * @return array + */ + public function searchStringDataProvider(): array + { + return [ + 'search_product_by_name' => [ + [ + 'name' => 'Simple product name', + 'sku' => '', + 'description' => '', + 'short_description' => '', + 'price' => [ + 'from' => '', + 'to' => '', + ], + 'test_searchable_attribute' => '', + ], + ], + 'search_product_by_sku' => [ + [ + 'name' => '', + 'sku' => 'simple_for_search', + 'description' => '', + 'short_description' => '', + 'price' => [ + 'from' => '', + 'to' => '', + ], + 'test_searchable_attribute' => '', + ], + ], + 'search_product_by_description' => [ + [ + 'name' => '', + 'sku' => '', + 'description' => 'Product description', + 'short_description' => '', + 'price' => [ + 'from' => '', + 'to' => '', + ], + 'test_searchable_attribute' => '', + ], + ], + 'search_product_by_short_description' => [ + [ + 'name' => '', + 'sku' => '', + 'description' => '', + 'short_description' => 'Product short description', + 'price' => [ + 'from' => '', + 'to' => '', + ], + 'test_searchable_attribute' => '', + ], + ], + 'search_product_by_price_range' => [ + [ + 'name' => '', + 'sku' => '', + 'description' => '', + 'short_description' => '', + 'price' => [ + 'from' => '50', + 'to' => '150', + ], + 'test_searchable_attribute' => '', + ], + ], + 'search_product_by_custom_attribute' => [ + [ + 'name' => '', + 'sku' => '', + 'description' => '', + 'short_description' => '', + 'price' => [ + 'from' => '', + 'to' => '', + ], + 'test_searchable_attribute' => 'Option 1', + ], + ], + ]; + } + + /** + * Return attribute option value by option label. + * + * @param string $attributeCode + * @param string $optionLabel + * @return null|string + */ + private function getAttributeOptionValueByOptionLabel(string $attributeCode, string $optionLabel): ?string + { + /** @var Attribute $attribute */ + $attribute = $this->productAttributeRepository->get($attributeCode); + + return $attribute->getSource()->getOptionId($optionLabel); + } +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Result/IndexTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Result/IndexTest.php new file mode 100644 index 0000000000000..0068d6cbaa015 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Controller/Result/IndexTest.php @@ -0,0 +1,55 @@ +getRequest()->setParam('q', $searchString); + $this->dispatch('catalogsearch/result'); + $responseBody = $this->getResponse()->getBody(); + $this->assertContains('Simple product name', $responseBody); + } + + /** + * Data provider with strings for quick search. + * + * @return array + */ + public function searchStringDataProvider(): array + { + return [ + 'search_product_by_name' => ['Simple product name'], + 'search_product_by_sku' => ['simple_for_search'], + 'search_product_by_description' => ['Product description'], + 'search_product_by_short_description' => ['Product short description'], + 'search_product_by_custom_attribute' => ['Option 1'], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProviderTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProviderTest.php index d503c9678dfd6..c090f4ea0183c 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProviderTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProviderTest.php @@ -7,71 +7,84 @@ namespace Magento\CatalogSearch\Model\Indexer\Fulltext\Action; -use Magento\Catalog\Model\Product; -use Magento\Catalog\Model\ProductRepository as ProductRepository; -use Magento\CatalogSearch\Model\Indexer\Fulltext; +use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\Api\Search\Document as SearchDocument; -use Magento\Framework\Indexer\IndexerRegistry; -use Magento\Framework\Search\AdapterInterface as AdapterInterface; use Magento\Framework\Search\Request\Builder as SearchRequestBuilder; use Magento\Framework\Search\Request\Config as SearchRequestConfig; use Magento\Search\Model\AdapterFactory as AdapterFactory; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; -class DataProviderTest extends \PHPUnit\Framework\TestCase +/** + * Search products by attribute value using mysql search engine. + */ +class DataProviderTest extends TestCase { + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var SearchRequestConfig + */ + private $searchRequestConfig; + + /** + * @var SearchRequestBuilder + */ + private $requestBuilder; + + /** + * @var AdapterFactory + */ + private $adapterFactory; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + /** * @inheritdoc */ - public static function setUpBeforeClass() + protected function setUp() { - /* - * Due to insufficient search engine isolation for Elasticsearch, this class must explicitly perform - * a fulltext reindex prior to running its tests. - * - * This should be removed upon completing MC-19455. - */ - $indexRegistry = Bootstrap::getObjectManager()->get(IndexerRegistry::class); - $fulltextIndexer = $indexRegistry->get(Fulltext::INDEXER_ID); - $fulltextIndexer->reindexAll(); + $this->objectManager = Bootstrap::getObjectManager(); + $this->searchRequestConfig = $this->objectManager->create(SearchRequestConfig::class); + $this->requestBuilder = $this->objectManager->create( + SearchRequestBuilder::class, + ['config' => $this->searchRequestConfig] + ); + $this->adapterFactory = $this->objectManager->get(AdapterFactory::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + parent::setUp(); } /** + * Search product by custom attribute value. + * + * @magentoConfigFixture default/catalog/search/engine mysql * @magentoDataFixture Magento/CatalogSearch/_files/product_for_search.php + * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php * @magentoDbIsolation disabled + * + * @return void */ - public function testSearchProductByAttribute() + public function testSearchProductByAttribute(): void { - /** @var ObjectManager $objectManager */ - $objectManager = Bootstrap::getObjectManager(); - - /** @var SearchRequestConfig $config */ - $config = $objectManager->create(SearchRequestConfig::class); - - /** @var SearchRequestBuilder $requestBuilder */ - $requestBuilder = $objectManager->create( - SearchRequestBuilder::class, - ['config' => $config] - ); - - $requestBuilder->bind('search_term', 'VALUE1'); - $requestBuilder->setRequestName('quick_search_container'); - $queryRequest = $requestBuilder->create(); - - /** @var AdapterInterface $adapter */ - $adapterFactory = $objectManager->create(AdapterFactory::class); - $adapter = $adapterFactory->create(); + $this->requestBuilder->bind('search_term', 'Option 1'); + $this->requestBuilder->setRequestName('quick_search_container'); + $queryRequest = $this->requestBuilder->create(); + $adapter = $this->adapterFactory->create(); $queryResponse = $adapter->query($queryRequest); $actualIds = []; - + /** @var SearchDocument $document */ foreach ($queryResponse as $document) { - /** @var SearchDocument $document */ $actualIds[] = $document->getId(); } - - /** @var Product $product */ - $product = $objectManager->create(ProductRepository::class)->get('simple'); + $product = $this->productRepository->get('simple_for_search'); $this->assertContains($product->getId(), $actualIds, 'Product not found by searchable attribute.'); } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Advanced/CollectionTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Advanced/CollectionTest.php index 87fda534be6d9..3eea0aa117452 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Advanced/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Advanced/CollectionTest.php @@ -5,6 +5,10 @@ */ namespace Magento\CatalogSearch\Model\ResourceModel\Advanced; +use Magento\CatalogSearch\Model\Indexer\Fulltext; +use Magento\Framework\Indexer\IndexerRegistry; +use Magento\TestFramework\Helper\Bootstrap; + /** * Test class for \Magento\CatalogSearch\Model\ResourceModel\Advanced\Collection. * @magentoDbIsolation disabled @@ -21,6 +25,9 @@ protected function setUp() $advanced = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() ->create(\Magento\CatalogSearch\Model\Search\ItemCollectionProvider::class); $this->advancedCollection = $advanced->getCollection(); + $indexerRegistry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(IndexerRegistry::class); + $indexerRegistry->get(Fulltext::INDEXER_ID)->reindexAll(); } /** diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Fulltext/CollectionTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Fulltext/CollectionTest.php index ae4fbc8d0d98e..55465e938e71b 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Fulltext/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/ResourceModel/Fulltext/CollectionTest.php @@ -29,6 +29,9 @@ public function testLoadWithFilterSearch($request, $filters, $expectedCount) foreach ($filters as $field => $value) { $fulltextCollection->addFieldToFilter($field, $value); } + if ($request == 'quick_search_container' && isset($filters['search_term'])) { + $fulltextCollection->addSearchFilter($filters['search_term']); + } $fulltextCollection->loadWithFilter(); $items = $fulltextCollection->getItems(); $this->assertCount($expectedCount, $items); @@ -37,6 +40,7 @@ public function testLoadWithFilterSearch($request, $filters, $expectedCount) /** * @dataProvider filtersDataProviderQuickSearch * @magentoDataFixture Magento/Framework/Search/_files/products.php + * @magentoAppIsolation enabled */ public function testLoadWithFilterQuickSearch($filters, $expectedCount) { @@ -47,6 +51,9 @@ public function testLoadWithFilterQuickSearch($filters, $expectedCount) foreach ($filters as $field => $value) { $fulltextCollection->addFieldToFilter($field, $value); } + if (isset($filters['search_term'])) { + $fulltextCollection->addSearchFilter($filters['search_term']); + } $fulltextCollection->loadWithFilter(); $items = $fulltextCollection->getItems(); $this->assertCount($expectedCount, $items); @@ -55,6 +62,7 @@ public function testLoadWithFilterQuickSearch($filters, $expectedCount) /** * @dataProvider filtersDataProviderCatalogView * @magentoDataFixture Magento/Framework/Search/_files/products.php + * @magentoAppIsolation enabled */ public function testLoadWithFilterCatalogView($filters, $expectedCount) { @@ -72,6 +80,7 @@ public function testLoadWithFilterCatalogView($filters, $expectedCount) /** * @magentoDataFixture Magento/Framework/Search/_files/products_with_the_same_search_score.php + * @magentoAppIsolation enabled */ public function testSearchResultsAreTheSameForSameRequests() { @@ -85,7 +94,7 @@ public function testSearchResultsAreTheSameForSameRequests() /** @var \Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection $fulltextCollection */ $fulltextCollection = $searchLayer->getProductCollection(); - $fulltextCollection->addFieldToFilter('search_term', 'shorts'); + $fulltextCollection->addSearchFilter('shorts'); $fulltextCollection->setOrder('relevance'); $fulltextCollection->load(); $items = $fulltextCollection->getItems(); @@ -136,4 +145,157 @@ public function filtersDataProviderCatalogView() [[], 5], ]; } + + /** + * Test configurable product with multiple options + * + * @magentoDataFixture Magento/CatalogSearch/_files/product_configurable_two_options.php + * @magentoConfigFixture default/catalog/search/engine mysql + * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php + * @magentoAppIsolation enabled + * @dataProvider configurableProductWithMultipleOptionsDataProvider + * @param array $filters + * @param bool $found + * @param array $outOfStock + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testConfigurableProductWithMultipleOptions(array $filters, bool $found, array $outOfStock = []) + { + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /**@var $stockRegistry \Magento\CatalogInventory\Model\StockRegistry */ + $stockRegistry = $objectManager->get(\Magento\CatalogInventory\Model\StockRegistry::class); + /**@var $stockItemRepository \Magento\CatalogInventory\Api\StockItemRepositoryInterface */ + $stockItemRepository = $objectManager->get(\Magento\CatalogInventory\Api\StockItemRepositoryInterface::class); + $collection = $objectManager->create( + \Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection::class, + ['searchRequestName' => 'filter_by_configurable_product_options'] + ); + foreach ($outOfStock as $sku) { + $stockItem = $stockRegistry->getStockItemBySku($sku); + $stockItem->setQty(0); + $stockItem->setIsInStock(0); + $stockItemRepository->save($stockItem); + } + + $options = ['test_configurable', 'test_configurable_2']; + foreach ($options as $option) { + if (isset($filters[$option])) { + $filters[$option] = $this->getOptionValue($option, $filters[$option]); + } + } + $filters['category_ids'] = 2; + foreach ($filters as $field => $value) { + $collection->addFieldToFilter($field, $value); + } + $collection->load(); + $items = $collection->getItems(); + if ($found) { + $this->assertCount(1, $items); + $item = array_shift($items); + $this->assertEquals('configurable_with_2_opts', $item['sku']); + } + $this->assertCount(0, $items); + } + + /** + * Provide filters to test configurable product with multiple options + * + * @return array + */ + public function configurableProductWithMultipleOptionsDataProvider(): array + { + return [ + [ + [], + true + ], + [ + ['test_configurable' => 'Option 1'], + true + ], + [ + ['test_configurable' => 'Option 2'], + true + ], + [ + ['test_configurable_2' => 'Option 1'], + true + ], + [ + ['test_configurable_2' => 'Option 2'], + true + ], + [ + ['test_configurable' => 'Option 1', 'test_configurable_2' => 'Option 1'], + true + ], + [ + ['test_configurable' => 'Option 1', 'test_configurable_2' => 'Option 2'], + true + ], + [ + ['test_configurable' => 'Option 2', 'test_configurable_2' => 'Option 1'], + true + ], + [ + ['test_configurable' => 'Option 2', 'test_configurable_2' => 'Option 2'], + true + ], + [ + ['test_configurable' => 'Option 2', 'test_configurable_2' => 'Option 2'], + false, + [ + 'configurable2_option_12', + 'configurable2_option_22', + ] + ], + [ + ['test_configurable' => 'Option 2', 'test_configurable_2' => 'Option 2'], + false, + [ + 'configurable2_option_21', + 'configurable2_option_22', + ] + ], + [ + ['test_configurable' => 'Option 2'], + false, + [ + 'configurable2_option_21', + 'configurable2_option_22', + ] + ], + [ + [], + false, + [ + 'configurable2_option_11', + 'configurable2_option_12', + 'configurable2_option_21', + 'configurable2_option_22', + ] + ], + ]; + } + + /** + * Get attribute option value by label + * + * @param string $attributeName + * @param string $optionLabel + * @return string|null + */ + private function getOptionValue(string $attributeName, string $optionLabel): ?string + { + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $eavConfig = $objectManager->get(\Magento\Eav\Model\Config::class); + $attribute = $eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeName); + $option = null; + foreach ($attribute->getOptions() as $option) { + if ($option->getLabel() === $optionLabel) { + return $option->getValue(); + } + } + return null; + } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/AttributeSearchWeightTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/AttributeSearchWeightTest.php index 775210669abd8..4ca8e0b0726d4 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/AttributeSearchWeightTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/AttributeSearchWeightTest.php @@ -8,172 +8,232 @@ namespace Magento\CatalogSearch\Model\Search; -use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Model\Layer\Search as CatalogLayerSearch; use Magento\Catalog\Model\Product; -use Magento\Catalog\Model\ResourceModel\Eav\Attribute; -use Magento\Eav\Api\AttributeRepositoryInterface; -use Magento\Elasticsearch\SearchAdapter\ConnectionManager; -use Magento\Elasticsearch6\Model\Client\Elasticsearch as ElasticsearchClient; -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Framework\Exception\StateException; +use Magento\CatalogSearch\Model\ResourceModel\Fulltext\CollectionFactory; use Magento\Framework\Search\Request\Builder; use Magento\Framework\Search\Request\Config as RequestConfig; -use Magento\Framework\Search\Response\QueryResponse; -use Magento\Framework\Search\SearchEngineInterface; -use Magento\Indexer\Model\Indexer; +use Magento\Search\Model\Search; use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\Helper\CacheCleaner; use Magento\TestFramework\ObjectManager; use PHPUnit\Framework\TestCase; /** - * Test for name over sku search weight of product attributes + * Test founded products order after quick search with changed attribute search weight using mysql search engine. * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @magentoAppIsolation enabled - * @magentoDbIsolation enabled */ class AttributeSearchWeightTest extends TestCase { - - /** @var $objectManager ObjectManager */ + /** + * @var $objectManager ObjectManager + */ private $objectManager; /** - * @var ConnectionManager + * @var ProductAttributeRepositoryInterface */ - private $connectionManager; + private $productAttributeRepository; /** - * @var ElasticsearchClient + * @var array */ - private $client; + private $collectedAttributesWeight = []; /** - * @var ProductRepositoryInterface + * @var CatalogLayerSearch */ - private $productRepository; + private $catalogLayerSearch; + /** + * @inheritdoc + */ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); - $this->connectionManager = $this->objectManager->create(ConnectionManager::class); - $this->client = $this->connectionManager->getConnection(); - $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->productAttributeRepository = $this->objectManager->get(ProductAttributeRepositoryInterface::class); + $this->catalogLayerSearch = $this->objectManager->get(CatalogLayerSearch::class); + $this->collectCurrentProductAttributesWeights(); } /** - * @param string $attributeName - * @param int $searchWeight - * @throws NoSuchEntityException - * @throws StateException + * @inheritdoc */ - private function setAttributeSearchWeight(string $attributeName, int $searchWeight) + protected function tearDown() { - /** @var AttributeRepositoryInterface $attributeRepository */ - $attributeRepository = $this->objectManager->create(AttributeRepositoryInterface::class); + $this->updateAttributesWeight($this->collectedAttributesWeight); + } - /** @var Attribute $attribute */ - $attribute = $attributeRepository->get('catalog_product', $attributeName); + /** + * Perform search by word and check founded product order in different cases. + * + * @magentoConfigFixture default/catalog/search/engine mysql + * @magentoDataFixture Magento/CatalogSearch/_files/products_for_sku_search_weight_score.php + * @magentoDataFixture Magento/CatalogSearch/_files/full_reindex.php + * @dataProvider attributeSearchWeightDataProvider + * @magentoDbIsolation disabled + * + * @param string $searchQuery + * @param array $attributeWeights + * @param array $expectedProductNames + * @return void + */ + public function testAttributeSearchWeight( + string $searchQuery, + array $attributeWeights, + array $expectedProductNames + ): void { + $this->updateAttributesWeight($attributeWeights); + $this->removeInstancesCache(); + $products = $this->findProducts($searchQuery); + $actualProductNames = $this->collectProductsName($products); + $this->assertEquals($expectedProductNames, $actualProductNames, 'Products order is not as expected.'); + } - if ($attribute) { - $attribute->setSearchWeight($searchWeight); - $attributeRepository->save($attribute); - } + /** + * Data provider with word for quick search, attributes weight and expected products name order. + * + * @return array + */ + public function attributeSearchWeightDataProvider(): array + { + return [ + 'sku_order_more_than_name' => [ + '1234-1234-1234-1234', + [ + 'sku' => 6, + 'name' => 5, + ], + [ + 'Simple', + '1234-1234-1234-1234', + ], + ], + 'name_order_more_than_sku' => [ + '1234-1234-1234-1234', + [ + 'name' => 6, + 'sku' => 5, + ], + [ + '1234-1234-1234-1234', + 'Simple', + ], + ], + 'search_by_word_from_description' => [ + 'Simple', + [ + 'test_searchable_attribute' => 8, + 'sku' => 6, + 'name' => 5, + 'description' => 1, + ], + [ + 'Product with attribute', + '1234-1234-1234-1234', + 'Simple', + 'Product with description', + ], + ], + 'search_by_attribute_option' => [ + 'Simple', + [ + 'description' => 10, + 'test_searchable_attribute' => 8, + 'sku' => 6, + 'name' => 1, + ], + [ + 'Product with description', + 'Product with attribute', + '1234-1234-1234-1234', + 'Simple', + ], + ], + ]; } /** - * @throws \Throwable + * Update attributes weight. + * + * @param array $attributeWeights + * @return void */ - private function reindex() + protected function updateAttributesWeight(array $attributeWeights): void { - CacheCleaner::cleanAll(); + foreach ($attributeWeights as $attributeCode => $weight) { + $attribute = $this->productAttributeRepository->get($attributeCode); - /** @var Indexer $indexer */ - $indexer = $this->objectManager->create(Indexer::class); - $indexer->load('catalogsearch_fulltext'); - $indexer->reindexAll(); + if ($attribute) { + $attribute->setSearchWeight($weight); + $this->productAttributeRepository->save($attribute); + } + } } /** - * @param string $query + * Get all names from founded products. + * + * @param Product[] $products * @return array - * @throws NoSuchEntityException */ - private function findProducts(string $query): array + protected function collectProductsName(array $products): array { - $config = $this->objectManager->create(RequestConfig::class); - - /** @var Builder $requestBuilder */ - $requestBuilder = $this->objectManager->create( - Builder::class, - ['config' => $config] - ); - $requestBuilder->bind('search_term', $query); - $requestBuilder->setRequestName('quick_search_container'); - - /** @var QueryResponse $searchResult */ - $searchResults = $this->objectManager->create(SearchEngineInterface::class) - ->search($requestBuilder->create()); - - $products = []; - foreach ($searchResults as $searchResult) { - $products [] = $this->productRepository->getById($searchResult->getId()); + $result = []; + foreach ($products as $product) { + $result[] = $product->getName(); } - return $products; + return $result; } /** - * @dataProvider skuOverNameAttributeSearchWeightDataProvider - * @magentoConfigFixture default/catalog/search/engine elasticsearch6 - * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search - * @magentoDataFixture Magento/CatalogSearch/_files/products_for_sku_search_weight_score.php - * @param string $searchQuery - * @param int $skuSearchWeight - * @param int $nameSearchWeight - * @param string $firstMatchProductName - * @param string $secondMatchProductName - * @throws NoSuchEntityException - * @throws \Throwable + * Reindex catalogsearch fulltext index. + * + * @return void */ - public function testSkuOverNameAttributeSearchWeight( - string $searchQuery, - int $skuSearchWeight, - int $nameSearchWeight, - string $firstMatchProductName, - string $secondMatchProductName - ) { - $this->setAttributeSearchWeight('sku', $skuSearchWeight); - $this->setAttributeSearchWeight('name', $nameSearchWeight); - $this->reindex(); - - /** @var Product $products [] */ - $products = $this->findProducts($searchQuery); + protected function removeInstancesCache(): void + { + $this->objectManager->removeSharedInstance(RequestConfig::class); + $this->objectManager->removeSharedInstance(Builder::class); + $this->objectManager->removeSharedInstance(Search::class); + $this->objectManager->removeSharedInstance(CatalogLayerSearch::class); + } - $this->assertCount( - 2, - $products, - 'Expected to find 2 products, found ' . count($products) . '.' - ); - - $this->assertEquals( - $firstMatchProductName, - $products[0]->getData('name'), - 'Products order is not as expected.' - ); - $this->assertEquals( - $secondMatchProductName, - $products[1]->getData('name'), - 'Products order is not as expected.' - ); + /** + * Find products by search query. + * + * @param string $query + * @return Product[] + */ + protected function findProducts(string $query): array + { + $testProductCollection = $this->catalogLayerSearch->getProductCollection(); + $testProductCollection->addSearchFilter($query); + $testProductCollection->setOrder('relevance', 'desc'); + + return $testProductCollection->getItems(); } - public function skuOverNameAttributeSearchWeightDataProvider(): array + /** + * Collect weight of attributes which use in test. + * + * @return void + */ + private function collectCurrentProductAttributesWeights(): void { - return [ - ['1-2-3-4', 10, 5, 'test', '1-2-3-4'], - ['1-2-3-4', 5, 10, '1-2-3-4', 'test'], - ]; + if (empty($this->collectedAttributesWeight)) { + $attributeCodes = [ + 'sku', + 'name', + 'description', + 'test_searchable_attribute' + ]; + foreach ($attributeCodes as $attributeCode) { + $attribute = $this->productAttributeRepository->get($attributeCode); + $this->collectedAttributesWeight[$attribute->getAttributeCode()] = $attribute->getSearchWeight(); + } + } } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_configurable_two_options.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_configurable_two_options.php new file mode 100644 index 0000000000000..67bd8b831b878 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_configurable_two_options.php @@ -0,0 +1,166 @@ +create(AttributeRepositoryInterface::class); +/** @var \Magento\Eav\Model\Config $eavConfig */ +$eavConfig = $objectManager->get(\Magento\Eav\Model\Config::class); +$attributes = ['test_configurable', 'test_configurable_2']; +foreach ($attributes as $attributeName) { + $attributeModel = $eavConfig->getAttribute(Product::ENTITY, $attributeName); + $attributeModel->addData([ + 'is_searchable' => 1, + 'is_filterable' => 1, + 'is_filterable_in_search' => 1, + 'is_visible_in_advanced_search' => 1, + ]); + $attributeRepository->save($attributeModel); +} + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); + +/** @var $installer CategorySetup */ +$installer = $objectManager->create(CategorySetup::class); + +/** @var AttributeOptionInterface[] $options */ +$options = $attribute->getOptions(); + +$attribute1Values = []; +$attribute2Values = []; +$attributeSetId = $installer->getAttributeSetId('catalog_product', 'Default'); +$associatedProductIds = []; +array_shift($options); +$index1 = 1; +foreach ($options as $option1) { + /** @var AttributeOptionInterface[] $options */ + $options2 = $attribute2->getOptions(); + array_shift($options2); + $index2 = 1; + foreach ($options2 as $option2) { + /** @var $product Product */ + $product = $objectManager->create(Product::class); + $product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('Configurable2 Option' . $index1 . $index2) + ->setSku('configurable2_option_' . $index1 . $index2) + ->setPrice(random_int(10, 100)) + ->setTestConfigurable($option1->getValue()) + ->setTestConfigurable2($option2->getValue()) + ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); + + $product = $productRepository->save($product); + + /** @var Item $stockItem */ + $stockItem = $objectManager->create(Item::class); + $stockItem->load($product->getId(), 'product_id'); + + if (!$stockItem->getProductId()) { + $stockItem->setProductId($product->getId()); + } + $stockItem->setUseConfigManageStock(1); + $stockItem->setQty(1000); + $stockItem->setIsQtyDecimal(0); + $stockItem->setIsInStock(1); + $stockItem->save(); + + $attribute1Values[] = [ + 'label' => 'test1', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option1->getValue(), + ]; + $attribute2Values[] = [ + 'label' => 'test2', + 'attribute_id' => $attribute2->getId(), + 'value_index' => $option2->getValue(), + ]; + $associatedProductIds[] = $product->getId(); + $index2++; + } + $index1++; +} + +/** @var $product Product */ +$product = $objectManager->create(Product::class); + +/** @var Factory $optionsFactory */ +$optionsFactory = $objectManager->create(Factory::class); + +$configurableAttributesData = [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attribute1Values, + ], + [ + 'attribute_id' => $attribute2->getId(), + 'code' => $attribute2->getAttributeCode(), + 'label' => $attribute2->getStoreLabel(), + 'position' => '1', + 'values' => $attribute2Values, + ], +]; + +$configurableOptions = $optionsFactory->create($configurableAttributesData); + +$extensionConfigurableAttributes = $product->getExtensionAttributes(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); + +$product->setExtensionAttributes($extensionConfigurableAttributes); + +$product->setTypeId(Configurable::TYPE_CODE) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('configurable with 2 opts') + ->setSku('configurable_with_2_opts') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); + +$productRepository->save($product); + +/** @var CategoryLinkManagementInterface $categoryLinkManagement */ +$categoryLinkManagement = $objectManager->create(CategoryLinkManagementInterface::class); + +$categoryLinkManagement->assignProductToCategories( + $product->getSku(), + [2] +); + +/** @var Converter $converter */ +$converter = $objectManager->create(Converter::class); +$document = new DOMDocument(); +$document->load(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'requests.xml'); +$requestConfig = $converter->convert($document); +/** @var Config $config */ +$config = $objectManager->get(Config::class); +$config->merge($requestConfig); diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_configurable_two_options_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_configurable_two_options_rollback.php new file mode 100644 index 0000000000000..4bb2ea4fa8178 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_configurable_two_options_rollback.php @@ -0,0 +1,49 @@ +get(Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$list = [ + 'configurable2_option_11', + 'configurable2_option_12', + 'configurable2_option_21', + 'configurable2_option_22', + 'configurable_with_2_opts' +]; + +foreach ($list as $sku) { + try { + $product = $productRepository->get($sku, true); + + $stockStatus = $objectManager->create(Status::class); + $stockStatus->load($product->getEntityId(), 'product_id'); + $stockStatus->delete(); + + $productRepository->delete($product); + } catch (NoSuchEntityException $e) { + //Product already removed + } +} + +require __DIR__ . '/../../../Magento/ConfigurableProduct/_files/configurable_attribute_rollback.php'; +require __DIR__ . '/../../../Magento/ConfigurableProduct/_files/configurable_attribute_2_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_for_search.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_for_search.php index afeb250a5921a..75654f8d7d272 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_for_search.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_for_search.php @@ -3,22 +3,49 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + declare(strict_types=1); require 'searchable_attribute.php'; -require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; - -/** @var $objectManager \Magento\TestFramework\ObjectManager */ -$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); -/** @var \Magento\Store\Model\StoreManager $storeManager */ -$storeManager = $objectManager->get(\Magento\Store\Model\StoreManager::class); -$storeManager->setIsSingleStoreModeAllowed(false); -/** @var \Magento\Store\Model\Store $store */ -$store = $storeManager->getStore('default'); +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; -/** @var \Magento\Catalog\Model\Product $product */ -$product = $objectManager->create(\Magento\Catalog\Model\ProductRepository::class)->get('simple'); -/** @var \Magento\Catalog\Model\Product\Action $productAction */ -$productAction = $objectManager->create(\Magento\Catalog\Model\Product\Action::class); -$productAction->updateAttributes([$product->getId()], ['test_searchable_attribute' => 'VALUE1'], $store->getId()); +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$product = $productFactory->create(); +$product->isObjectNew(true); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([1]) + ->setName('Simple product name') + ->setSku('simple_for_search') + ->setPrice(100) + ->setWeight(1) + ->setShortDescription('Product short description') + ->setTaxClassId(0) + ->setDescription('Product description') + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setTestSearchableAttribute($attribute->getSource()->getOptionId('Option 1')) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_for_search_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_for_search_rollback.php index 00e2096ca734a..84f2e12e03b2d 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_for_search_rollback.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/product_for_search_rollback.php @@ -3,25 +3,37 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + declare(strict_types=1); -/** @var $objectManager \Magento\TestFramework\ObjectManager */ -$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); -$registry = $objectManager->get(\Magento\Framework\Registry::class); +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Eav\Setup\EavSetup; +use Magento\Eav\Setup\EavSetupFactory; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var EavSetupFactory $eavSetupFactory */ +$eavSetupFactory = $objectManager->create(EavSetupFactory::class); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); $registry->unregister('isSecureArea'); $registry->register('isSecureArea', true); -/** @var \Magento\Catalog\Model\Product $product */ -$product = $objectManager->create(\Magento\Catalog\Model\ProductRepository::class)->get('simple'); -/** @var \Magento\Catalog\Model\ResourceModel\Product $productResource */ -$productResource = $objectManager->create(\Magento\Catalog\Model\ResourceModel\Product::class); -$productResource->delete($product); - -$eavSetupFactory = $objectManager->create(\Magento\Eav\Setup\EavSetupFactory::class); -/** @var \Magento\Eav\Setup\EavSetup $eavSetup */ +try { + $productRepository->deleteById('simple_for_search'); +} catch (NoSuchEntityException $e) { + //Product already deleted. +} +/** @var EavSetup $eavSetup */ $eavSetup = $eavSetupFactory->create(); -$eavSetup->removeAttribute(\Magento\Catalog\Model\Product::ENTITY, 'test_searchable_attribute'); +$eavSetup->removeAttribute(Product::ENTITY, 'test_searchable_attribute'); $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/products_for_sku_search_weight_score.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/products_for_sku_search_weight_score.php index 96d5c256dc727..cb7aaa9f16a19 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/products_for_sku_search_weight_score.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/products_for_sku_search_weight_score.php @@ -3,22 +3,69 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + +require 'searchable_attribute.php'; use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Attribute\Source\Status; use Magento\Catalog\Model\Product\Type; use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; use Magento\TestFramework\Helper\Bootstrap; +$objectManager = Bootstrap::getObjectManager(); /** @var ProductRepositoryInterface $productRepository */ -$productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); -$product = Bootstrap::getObjectManager()->create(Product::class); +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$product = $productFactory->create(); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([1]) + ->setName('Simple') + ->setSku('1234-1234-1234-1234') + ->setPrice(10) + ->setTaxClassId(0) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ); +$productRepository->save($product); + +$product = $productFactory->create(); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([1]) + ->setName('1234-1234-1234-1234') + ->setSku('Simple') + ->setPrice(10) + ->setTaxClassId(0) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ); +$productRepository->save($product); + +$product = $productFactory->create(); $product->setTypeId(Type::TYPE_SIMPLE) - ->setAttributeSetId(4) + ->setAttributeSetId($product->getDefaultAttributeSetId()) ->setWebsiteIds([1]) - ->setName('1-2-3-4') - ->setSku('testsku') + ->setName('Product with description') + ->setSku('product_with_description') + ->setDescription('Simple') ->setPrice(10) ->setTaxClassId(0) ->setVisibility(Visibility::VISIBILITY_BOTH) @@ -33,16 +80,17 @@ ); $productRepository->save($product); -$product = Bootstrap::getObjectManager()->create(Product::class); +$product = $productFactory->create(); $product->setTypeId(Type::TYPE_SIMPLE) - ->setAttributeSetId(4) + ->setAttributeSetId($product->getDefaultAttributeSetId()) ->setWebsiteIds([1]) - ->setName('test') - ->setSku('1-2-3-4') + ->setName('Product with attribute') + ->setSku('product_with_attribute') ->setPrice(10) ->setTaxClassId(0) ->setVisibility(Visibility::VISIBILITY_BOTH) ->setStatus(Status::STATUS_ENABLED) + ->setTestSearchableAttribute($attribute->getSource()->getOptionId('Simple')) ->setStockData( [ 'use_config_manage_stock' => 1, diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/products_for_sku_search_weight_score_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/products_for_sku_search_weight_score_rollback.php new file mode 100644 index 0000000000000..775d405654fdf --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/products_for_sku_search_weight_score_rollback.php @@ -0,0 +1,41 @@ +get(ProductAttributeRepositoryInterface::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$productSkus = ['1234-1234-1234-1234', 'Simple', 'product_with_description', 'product_with_attribute']; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +foreach ($productSkus as $productSku) { + try { + $productRepository->deleteById($productSku); + } catch (NoSuchEntityException $e) { + //Product already deleted. + } +} + +try { + $productAttributeRepository->deleteById('test_searchable_attribute'); +} catch (NoSuchEntityException $e) { + //attribute already deleted. +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/requests.xml b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/requests.xml new file mode 100644 index 0000000000000..660aa36cc291d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/requests.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 10 + + diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/searchable_attribute.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/searchable_attribute.php index b25d39b1d40b8..0d20dcb24dfbf 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/searchable_attribute.php +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/searchable_attribute.php @@ -3,25 +3,62 @@ * 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->addAttribute( - \Magento\Catalog\Model\Product::ENTITY, - 'test_searchable_attribute', - [ - 'label' => 'Test-attribute', - 'global' => \Magento\Catalog\Model\ResourceModel\Eav\Attribute::SCOPE_STORE, - 'required' => 0, - 'user_defined' => 1, - 'searchable' => 1, - 'visible_on_front' => 1, - 'filterable_in_search' => 1, - 'used_in_product_listing' => 1, - 'is_used_in_grid' => 1, - 'is_filterable_in_grid' => 1, - ] -); +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory; +use Magento\Eav\Setup\EavSetup; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var AttributeFactory $attributeFactory */ +$attributeFactory = $objectManager->get(AttributeFactory::class); +/** @var ProductAttributeRepositoryInterface $productAttributeRepository */ +$productAttributeRepository = $objectManager->get(ProductAttributeRepositoryInterface::class); +$attribute = $attributeFactory->create()->loadByCode(Product::ENTITY, 'test_searchable_attribute'); +if (!$attribute->getId()) { + /** @var EavSetup $installer */ + $installer = $objectManager->create(EavSetup::class); + $attribute->setData( + [ + 'attribute_code' => 'test_searchable_attribute', + 'is_global' => 0, + 'is_user_defined' => 1, + 'frontend_input' => 'select', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 1, + 'is_visible_in_advanced_search' => 1, + 'is_comparable' => 0, + 'is_filterable' => 1, + 'is_filterable_in_search' => 1, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 0, + 'used_in_product_listing' => 0, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Test Drop-Down Attribute'], + 'backend_type' => 'int', + 'option' => [ + 'value' => [ + 'option_1' => ['Option 1'], + 'option_2' => ['Option 2'], + 'option_3' => ['Option 3'], + 'option_4' => ['Simple'] + ], + 'order' => [ + 'option_1' => 1, + 'option_2' => 2, + 'option_3' => 3, + 'option_4' => 4, + ], + ], + ] + ); + $productAttributeRepository->save($attribute); + $attribute = $productAttributeRepository->get('test_searchable_attribute'); + /* Assign attribute to attribute set */ + $installer->addAttributeToGroup(Product::ENTITY, 'Default', 'Attributes', $attribute->getId()); +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/AbstractUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/AbstractUrlRewriteTest.php new file mode 100644 index 0000000000000..251a79f46e38c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/AbstractUrlRewriteTest.php @@ -0,0 +1,133 @@ +objectManager = Bootstrap::getObjectManager(); + $this->storeRepository = $this->objectManager->create(StoreRepositoryInterface::class); + $this->config = $this->objectManager->get(ScopeConfigInterface::class); + $this->urlRewriteCollectionFactory = $this->objectManager->get(UrlRewriteCollectionFactory::class); + } + + /** + * Retrieve all rewrite ids + * + * @return array + */ + protected function getAllRewriteIds(): array + { + $urlRewriteCollection = $this->urlRewriteCollectionFactory->create(); + + return $urlRewriteCollection->getAllIds(); + } + + /** + * Check that actual data contains of expected values + * + * @param UrlRewriteCollection $collection + * @param array $expectedData + * @return void + */ + protected function assertRewrites(UrlRewriteCollection $collection, array $expectedData): void + { + $collectionItems = $collection->toArray()['items']; + $this->assertTrue(count($collectionItems) === count($expectedData)); + foreach ($expectedData as $expectedItem) { + $found = false; + foreach ($collectionItems as $item) { + $found = array_intersect_assoc($item, $expectedItem) == $expectedItem; + if ($found) { + break; + } + } + $this->assertTrue($found, 'The actual data does not contains of expected values'); + } + } + + /** + * Get category url rewrites collection + * + * @param string|array $entityId + * @return UrlRewriteCollection + */ + protected function getEntityRewriteCollection($entityId): UrlRewriteCollection + { + $condition = is_array($entityId) ? ['in' => $entityId] : $entityId; + $entityRewriteCollection = $this->urlRewriteCollectionFactory->create(); + $entityRewriteCollection->addFieldToFilter(UrlRewrite::ENTITY_ID, $condition) + ->addFieldToFilter(UrlRewrite::ENTITY_TYPE, ['eq' => $this->getEntityType()]); + + return $entityRewriteCollection; + } + + /** + * Prepare expected data + * + * @param array $expectedData + * @param int|null $id + * @return array + */ + protected function prepareData(array $expectedData, ?int $id = null): array + { + $newData = []; + foreach ($expectedData as $key => $expectedItem) { + $newData[$key] = str_replace(['%suffix%', '%id%'], [$this->getUrlSuffix(), $id], $expectedItem); + } + + return $newData; + } + + /** + * Get entity type + * + * @return string + */ + abstract protected function getEntityType(): string; + + /** + * Get config value for url suffix + * + * @return string + */ + abstract protected function getUrlSuffix(): string; +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteGeneratorTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteGeneratorTest.php index 1c2ee602c3bd4..b6fa2fac2ca80 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteGeneratorTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteGeneratorTest.php @@ -98,12 +98,6 @@ public function testGenerateUrlRewritesWithoutSaveHistory() 'catalog/product/view/id/' . $productForTest . '/category/4', 1, 0 - ], - [ - '/simple-product-two.html', - 'catalog/product/view/id/' . $productForTest . '/category/2', - 1, - 0 ] ]; @@ -187,12 +181,6 @@ public function testGenerateUrlRewritesWithSaveHistory() 1, 0 ], - [ - '/simple-product-two.html', - 'catalog/product/view/id/' . $productForTest . '/category/2', - 1, - 0 - ], [ 'category-1/simple-product-two.html', 'new-url/simple-product-two.html', @@ -329,6 +317,8 @@ public function testGenerateUrlRewritesWithoutGenerateProductRewrites() * @magentoAppIsolation enabled * * @return void + * @throws NoSuchEntityException + * @throws \Magento\Framework\Exception\StateException */ public function testRemoveCatalogUrlRewrites() { diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php index 687b997eedde7..1431148c5f868 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteTest.php @@ -7,55 +7,62 @@ namespace Magento\CatalogUrlRewrite\Model; +use Magento\Catalog\Api\CategoryLinkManagementInterface; +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\Data\CategoryInterface; use Magento\Catalog\Model\CategoryFactory; -use Magento\Catalog\Model\CategoryRepository; use Magento\Catalog\Model\ResourceModel\Category as CategoryResource; use Magento\CatalogUrlRewrite\Model\Map\DataCategoryUrlRewriteDatabaseMap; -use Magento\Framework\ObjectManagerInterface; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollectionFactory; +use Magento\CatalogUrlRewrite\Model\Map\DataProductUrlRewriteDatabaseMap; +use Magento\CatalogUrlRewrite\Model\ResourceModel\Category\Product; +use Magento\Store\Model\ScopeInterface; +use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; +use Magento\UrlRewrite\Model\OptionProvider; +use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; -use PHPUnit\Framework\TestCase; /** * Class for category url rewrites tests * - * @magentoAppArea adminhtml * @magentoDbIsolation enabled + * @magentoConfigFixture default/catalog/seo/generate_category_product_rewrites 1 + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class CategoryUrlRewriteTest extends TestCase +class CategoryUrlRewriteTest extends AbstractUrlRewriteTest { - /** @var ObjectManagerInterface */ - private $objectManager; - - /** @var CategoryFactory */ - private $categoryFactory; - - /** @var UrlRewriteCollectionFactory */ - private $urlRewriteCollectionFactory; - - /** @var CategoryRepository */ + /** @var CategoryRepositoryInterface */ private $categoryRepository; /** @var CategoryResource */ private $categoryResource; + /** @var CategoryLinkManagementInterface */ + private $categoryLinkManagement; + + /** @var CategoryFactory */ + private $categoryFactory; + + /** @var string */ + private $suffix; + /** - * @inheritDoc + * @inheritdoc */ protected function setUp() { parent::setUp(); - $this->objectManager = Bootstrap::getObjectManager(); - $this->categoryFactory = $this->objectManager->get(CategoryFactory::class); - $this->urlRewriteCollectionFactory = $this->objectManager->get(UrlRewriteCollectionFactory::class); - $this->categoryRepository = $this->objectManager->get(CategoryRepository::class); + $this->categoryRepository = $this->objectManager->create(CategoryRepositoryInterface::class); $this->categoryResource = $this->objectManager->get(CategoryResource::class); + $this->categoryLinkManagement = $this->objectManager->create(CategoryLinkManagementInterface::class); + $this->categoryFactory = $this->objectManager->get(CategoryFactory::class); + $this->suffix = $this->config->getValue( + CategoryUrlPathGenerator::XML_PATH_CATEGORY_URL_SUFFIX, + ScopeInterface::SCOPE_STORE + ); } /** - * @magentoConfigFixture default/catalog/seo/generate_category_product_rewrites 1 * @magentoDataFixture Magento/Catalog/_files/category_with_position.php * @dataProvider categoryProvider * @param array $data @@ -63,24 +70,13 @@ protected function setUp() */ public function testUrlRewriteOnCategorySave(array $data): void { - $categoryModel = $this->categoryFactory->create(); - $categoryModel->isObjectNew(true); - $categoryModel->setData($data['data']); - $this->categoryResource->save($categoryModel); + $categoryModel = $this->saveCategory($data['data']); $this->assertNotNull($categoryModel->getId(), 'The category was not created'); - $urlRewriteCollection = $this->urlRewriteCollectionFactory->create(); - $urlRewriteCollection->addFieldToFilter(UrlRewrite::ENTITY_ID, ['eq' => $categoryModel->getId()]) - ->addFieldToFilter(UrlRewrite::ENTITY_TYPE, ['eq' => DataCategoryUrlRewriteDatabaseMap::ENTITY_TYPE]); - - foreach ($urlRewriteCollection as $item) { - foreach ($data['expected_data'] as $field => $expectedItem) { - $this->assertEquals( - sprintf($expectedItem, $categoryModel->getId()), - $item[$field], - 'The expected data does not match actual value' - ); - } - } + $urlRewriteCollection = $this->getEntityRewriteCollection($categoryModel->getId()); + $this->assertRewrites( + $urlRewriteCollection, + $this->prepareData($data['expected_data'], (int)$categoryModel->getId()) + ); } /** @@ -99,8 +95,10 @@ public function categoryProvider(): array 'is_active' => true, ], 'expected_data' => [ - 'request_path' => 'test-category.html', - 'target_path' => 'catalog/category/view/id/%s', + [ + 'request_path' => 'test-category%suffix%', + 'target_path' => 'catalog/category/view/id/%id%', + ], ], ], ], @@ -114,11 +112,298 @@ public function categoryProvider(): array 'is_active' => true, ], 'expected_data' => [ - 'request_path' => 'category-1/test-sub-category.html', - 'target_path' => 'catalog/category/view/id/%s', + [ + 'request_path' => 'category-1/test-sub-category%suffix%', + 'target_path' => 'catalog/category/view/id/%id%', + ], ], ], ], ]; } + + /** + * @magentoDataFixture Magento/Catalog/_files/category_tree.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @dataProvider productRewriteProvider + * @param array $data + * @return void + */ + public function testCategoryProductUrlRewrite(array $data): void + { + $category = $this->categoryRepository->get(402); + $this->categoryLinkManagement->assignProductToCategories('simple2', [$category->getId()]); + $productRewriteCollection = $this->getCategoryProductRewriteCollection( + array_keys($category->getParentCategories()) + ); + $this->assertRewrites($productRewriteCollection, $this->prepareData($data)); + } + + /** + * @return array + */ + public function productRewriteProvider(): array + { + return [ + [ + [ + [ + 'request_path' => 'category-1/category-1-1/category-1-1-1/simple-product2%suffix%', + 'target_path' => 'catalog/product/view/id/6/category/402', + ], + [ + 'request_path' => 'category-1/simple-product2%suffix%', + 'target_path' => 'catalog/product/view/id/6/category/400', + ], + [ + 'request_path' => 'category-1/category-1-1/simple-product2%suffix%', + 'target_path' => 'catalog/product/view/id/6/category/401', + ], + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/CatalogUrlRewrite/_files/categories_with_products.php + * @magentoAppIsolation enabled + * @dataProvider existingUrlProvider + * @param array $data + * @return void + */ + public function testUrlRewriteOnCategorySaveWithExistingUrlKey(array $data): void + { + $this->expectException(UrlAlreadyExistsException::class); + $this->expectExceptionMessage((string)__('URL key for specified store already exists.')); + $this->saveCategory($data); + } + + /** + * @return array + */ + public function existingUrlProvider(): array + { + return [ + 'with_specified_existing_product_url_key' => [ + 'data' => [ + 'name' => 'Test Category', + 'attribute_set_id' => '3', + 'parent_id' => 2, + 'path' => '1/2', + 'is_active' => true, + 'url_key' => 'simple-product', + ], + ], + 'with_autogenerated_existing_product_url_key' => [ + 'data' => [ + 'name' => 'Simple Product', + 'attribute_set_id' => '3', + 'parent_id' => 2, + 'path' => '1/2', + 'is_active' => true, + ], + ], + 'with_specified_existing_category_url_key' => [ + 'data' => [ + 'name' => 'Test Category', + 'attribute_set_id' => '3', + 'parent_id' => 2, + 'path' => '1/2', + 'is_active' => true, + 'url_key' => 'category-1', + ], + ], + 'with_autogenerated_existing_category_url_key' => [ + 'data' => [ + 'name' => 'Category 1', + 'attribute_set_id' => '3', + 'parent_id' => 2, + 'path' => '1/2', + 'is_active' => true, + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/Catalog/_files/category_product.php + * @magentoDataFixture Magento/Catalog/_files/catalog_category_with_slash.php + * @dataProvider categoryMoveProvider + * @param array $data + * @return void + */ + public function testUrlRewriteOnCategoryMove(array $data): void + { + $categoryId = $data['data']['id']; + $category = $this->categoryRepository->get($categoryId); + $category->move($data['data']['pid'], $data['data']['aid']); + $productRewriteCollection = $this->getCategoryProductRewriteCollection( + array_keys($category->getParentCategories()) + ); + $categoryRewriteCollection = $this->getEntityRewriteCollection($categoryId); + $this->assertRewrites($categoryRewriteCollection, $this->prepareData($data['expected_data']['category'])); + $this->assertRewrites($productRewriteCollection, $this->prepareData($data['expected_data']['product'])); + } + + /** + * @return array + */ + public function categoryMoveProvider(): array + { + return [ + 'append_category' => [ + [ + 'data' => [ + 'id' => '333', + 'pid' => '3331', + 'aid' => '0', + ], + 'expected_data' => [ + 'category' => [ + [ + 'request_path' => 'category-1.html', + 'target_path' => 'category-with-slash-symbol/category-1%suffix%', + 'redirect_type' => OptionProvider::PERMANENT, + ], + [ + 'request_path' => 'category-with-slash-symbol/category-1%suffix%', + 'target_path' => 'catalog/category/view/id/333', + ], + ], + 'product' => [ + [ + 'request_path' => 'category-with-slash-symbol/simple-product-three%suffix%', + 'target_path' => 'catalog/product/view/id/333/category/3331', + ], + [ + 'request_path' => 'category-with-slash-symbol/category-1/simple-product-three%suffix%', + 'target_path' => 'catalog/product/view/id/333/category/333', + ], + ], + ], + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/Catalog/_files/category.php + * @return void + */ + public function testUrlRewritesAfterCategoryDelete(): void + { + $categoryId = 333; + $categoryItemIds = $this->getEntityRewriteCollection($categoryId)->getAllIds(); + $this->categoryRepository->deleteByIdentifier($categoryId); + $this->assertEmpty( + array_intersect($this->getAllRewriteIds(), $categoryItemIds), + 'Not all expected category url rewrites were deleted' + ); + } + + /** + * @magentoAppArea adminhtml + * @magentoDataFixture Magento/CatalogUrlRewrite/_files/categories_with_product_ids.php + * @return void + */ + public function testUrlRewritesAfterCategoryWithProductsDelete(): void + { + $category = $this->categoryRepository->get(3); + $childIds = explode(',', $category->getAllChildren()); + $productRewriteIds = $this->getCategoryProductRewriteCollection($childIds)->getAllIds(); + $categoryItemIds = $this->getEntityRewriteCollection($childIds)->getAllIds(); + $this->categoryRepository->deleteByIdentifier($category->getId()); + $allIds = $this->getAllRewriteIds(); + $this->assertEmpty( + array_intersect($allIds, $categoryItemIds), + 'Not all expected category url rewrites were deleted' + ); + $this->assertEmpty( + array_intersect($allIds, $productRewriteIds), + 'Not all expected category-product url rewrites were deleted' + ); + } + + /** + * @magentoDataFixture Magento/Store/_files/second_store.php + * @magentoDataFixture Magento/Catalog/_files/category.php + * @return void + */ + public function testCategoryUrlRewritePerStoreViews(): void + { + $urlSuffix = $this->config->getValue( + CategoryUrlPathGenerator::XML_PATH_CATEGORY_URL_SUFFIX, + ScopeInterface::SCOPE_STORE + ); + $urlKeySecondStore = 'url-key-for-second-store'; + $secondStoreId = $this->storeRepository->get('fixture_second_store')->getId(); + $categoryId = 333; + $category = $this->categoryRepository->get($categoryId); + $urlKeyFirstStore = $category->getUrlKey(); + $this->saveCategory( + ['store_id' => $secondStoreId, 'url_key' => $urlKeySecondStore], + $category + ); + $urlRewriteItems = $this->getEntityRewriteCollection($categoryId)->getItems(); + $this->assertTrue(count($urlRewriteItems) == 2); + foreach ($urlRewriteItems as $item) { + $item->getData('store_id') == $secondStoreId + ? $this->assertEquals($urlKeySecondStore . $urlSuffix, $item->getRequestPath()) + : $this->assertEquals($urlKeyFirstStore . $urlSuffix, $item->getRequestPath()); + } + } + + /** + * @inheritdoc + */ + protected function getUrlSuffix(): string + { + return $this->suffix; + } + + /** + * @inheritdoc + */ + protected function getEntityType(): string + { + return DataCategoryUrlRewriteDatabaseMap::ENTITY_TYPE; + } + + /** + * Save product with data using resource model directly + * + * @param array $data + * @param CategoryInterface|null $category + * @return CategoryInterface + */ + private function saveCategory(array $data, $category = null): CategoryInterface + { + $category = $category ?: $this->categoryFactory->create(); + $category->addData($data); + $this->categoryResource->save($category); + + return $category; + } + + /** + * Get products url rewrites collection referred to categories + * + * @param string|array $categoryId + * @return UrlRewriteCollection + */ + private function getCategoryProductRewriteCollection($categoryId): UrlRewriteCollection + { + $condition = is_array($categoryId) ? ['in' => $categoryId] : $categoryId; + $productRewriteCollection = $this->urlRewriteCollectionFactory->create(); + $productRewriteCollection + ->join( + ['p' => $this->categoryResource->getTable(Product::TABLE_NAME)], + 'main_table.url_rewrite_id = p.url_rewrite_id', + 'category_id' + ) + ->addFieldToFilter('category_id', $condition) + ->addFieldToFilter(UrlRewrite::ENTITY_TYPE, ['eq' => DataProductUrlRewriteDatabaseMap::ENTITY_TYPE]); + + return $productRewriteCollection; + } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php new file mode 100644 index 0000000000000..f8fe68c2e0a2d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php @@ -0,0 +1,302 @@ +productResource = $this->objectManager->create(ProductResource::class); + $this->productFactory = $this->objectManager->get(ProductFactory::class); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->suffix = $this->config->getValue( + ProductUrlPathGenerator::XML_PATH_PRODUCT_URL_SUFFIX, + ScopeInterface::SCOPE_STORE + ); + } + + /** + * @dataProvider productDataProvider + * @param array $data + * @return void + */ + public function testUrlRewriteOnProductSave(array $data): void + { + $product = $this->saveProduct($data['data']); + $this->assertNotNull($product->getId(), 'The product was not created'); + $productUrlRewriteCollection = $this->getEntityRewriteCollection($product->getId()); + $this->assertRewrites( + $productUrlRewriteCollection, + $this->prepareData($data['expected_data'], (int)$product->getId()) + ); + } + + /** + * @return array + */ + public function productDataProvider(): array + { + return [ + 'without_url_key' => [ + [ + 'data' => [ + 'type_id' => Type::TYPE_SIMPLE, + 'visibility' => Visibility::VISIBILITY_BOTH, + 'attribute_set_id' => 4, + 'sku' => 'test-product', + 'name' => 'test product', + 'price' => 150, + ], + 'expected_data' => [ + [ + 'request_path' => 'test-product%suffix%', + 'target_path' => 'catalog/product/view/id/%id%', + ], + ], + ], + ], + 'with_url_key' => [ + [ + 'data' => [ + 'type_id' => Type::TYPE_SIMPLE, + 'attribute_set_id' => 4, + 'sku' => 'test-product', + 'visibility' => Visibility::VISIBILITY_BOTH, + 'name' => 'test product', + 'price' => 150, + 'url_key' => 'test-product-url-key', + ], + 'expected_data' => [ + [ + 'request_path' => 'test-product-url-key%suffix%', + 'target_path' => 'catalog/product/view/id/%id%', + ], + ], + ], + ], + 'with_invisible_product' => [ + [ + 'data' => [ + 'type_id' => Type::TYPE_SIMPLE, + 'attribute_set_id' => 4, + 'sku' => 'test-product', + 'visibility' => Visibility::VISIBILITY_NOT_VISIBLE, + 'name' => 'test product', + 'price' => 150, + 'url_key' => 'test-product-url-key', + ], + 'expected_data' => [], + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_simple.php + * @dataProvider productEditProvider + * @param array $expectedData + * @return void + */ + public function testUrlRewriteOnProductEdit(array $expectedData): void + { + $product = $this->productRepository->get('simple'); + $data = [ + 'url_key' => 'new-url-key', + 'url_key_create_redirect' => $product->getUrlKey(), + 'save_rewrites_history' => true, + ]; + $product = $this->saveProduct($data, $product); + $productRewriteCollection = $this->getEntityRewriteCollection($product->getId()); + $this->assertRewrites( + $productRewriteCollection, + $this->prepareData($expectedData, (int)$product->getId()) + ); + } + + /** + * @return array + */ + public function productEditProvider(): array + { + return [ + [ + 'expected_data' => [ + [ + 'request_path' => 'new-url-key%suffix%', + 'target_path' => 'catalog/product/view/id/%id%', + ], + [ + 'request_path' => 'simple-product%suffix%', + 'target_path' => 'new-url-key%suffix%', + 'redirect_type' => OptionProvider::PERMANENT, + ], + ], + ], + ]; + } + + /** + * @magentoDataFixture Magento/CatalogUrlRewrite/_files/category_with_products.php + * @dataProvider existingUrlKeyProvider + * @param array $data + * @return void + */ + public function testUrlRewriteOnProductSaveWithExistingUrlKey(array $data): void + { + $this->expectException(UrlAlreadyExistsException::class); + $this->expectExceptionMessage((string)__('URL key for specified store already exists.')); + $this->saveProduct($data); + } + + /** + * @return array + */ + public function existingUrlKeyProvider(): array + { + return [ + [ + 'with_specified_existing_product_url_key' => [ + 'type_id' => Type::TYPE_SIMPLE, + 'attribute_set_id' => 4, + 'sku' => 'test-simple-product', + 'name' => 'test-simple-product', + 'price' => 150, + 'url_key' => 'simple-product', + ], + 'with_autogenerated_existing_product_url_key' => [ + 'type_id' => Type::TYPE_SIMPLE, + 'attribute_set_id' => 4, + 'sku' => 'test-simple-product', + 'name' => 'simple product', + 'price' => 150, + ], + 'with_specified_existing_category_url_key' => [ + 'type_id' => Type::TYPE_SIMPLE, + 'attribute_set_id' => 4, + 'sku' => 'test-simple-product', + 'name' => 'test-simple-product', + 'price' => 150, + 'url_key' => 'category-1', + ], + 'with_autogenerated_existing_category_url_key' => [ + 'type_id' => Type::TYPE_SIMPLE, + 'attribute_set_id' => 4, + 'sku' => 'test-simple-product', + 'name' => 'category 1', + 'price' => 150, + ], + ], + ]; + } + + /** + * @magentoAppArea adminhtml + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + */ + public function testUrlRewritesAfterProductDelete(): void + { + $product = $this->productRepository->get('simple2'); + $rewriteIds = $this->getEntityRewriteCollection($product->getId())->getAllIds(); + $this->productRepository->delete($product); + $this->assertEmpty( + array_intersect($this->getAllRewriteIds(), $rewriteIds), + 'Not all expected category url rewrites were deleted' + ); + } + + /** + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/Store/_files/second_store.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @return void + */ + public function testProductUrlRewritePerStoreViews(): void + { + $urlKeySecondStore = 'url-key-for-second-store'; + $secondStoreId = $this->storeRepository->get('fixture_second_store')->getId(); + $product = $this->productRepository->get('simple2'); + $urlKeyFirstStore = $product->getUrlKey(); + $product = $this->saveProduct( + ['store_id' => $secondStoreId, 'url_key' => $urlKeySecondStore], + $product + ); + $urlRewriteItems = $this->getEntityRewriteCollection($product->getId())->getItems(); + $this->assertTrue(count($urlRewriteItems) == 2); + foreach ($urlRewriteItems as $item) { + $item->getData('store_id') == $secondStoreId + ? $this->assertEquals($urlKeySecondStore . $this->suffix, $item->getRequestPath()) + : $this->assertEquals($urlKeyFirstStore . $this->suffix, $item->getRequestPath()); + } + } + + /** + * Save product with data using resource model directly + * + * @param array $data + * @param ProductInterface|null $product + * @return ProductInterface + */ + protected function saveProduct(array $data, $product = null): ProductInterface + { + $product = $product ?: $this->productFactory->create(); + $product->addData($data); + $this->productResource->save($product); + + return $product; + } + + /** + * @inheritdoc + */ + protected function getUrlSuffix(): string + { + return $this->suffix; + } + + /** + * @inheritdoc + */ + protected function getEntityType(): string + { + return DataProductUrlRewriteDatabaseMap::ENTITY_TYPE; + } +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Catalog/Block/Adminhtml/Category/Tab/AttributesTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Catalog/Block/Adminhtml/Category/Tab/AttributesTest.php index 0cd3ad644197b..8ca84c4b066fe 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Catalog/Block/Adminhtml/Category/Tab/AttributesTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Catalog/Block/Adminhtml/Category/Tab/AttributesTest.php @@ -51,8 +51,8 @@ public function testGetAttributesMeta() $urlKeyData = $meta['search_engine_optimization']['children']['url_key']['arguments']['data']['config']; $this->assertEquals('text', $urlKeyData['dataType']); $this->assertEquals('input', $urlKeyData['formElement']); - $this->assertEquals('1', $urlKeyData['visible']); - $this->assertEquals('0', $urlKeyData['required']); + $this->assertEquals(true, $urlKeyData['visible']); + $this->assertEquals(false, $urlKeyData['required']); $this->assertEquals('[STORE VIEW]', $urlKeyData['scopeLabel']); } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php b/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php index 2695948e78314..85cd5331a29c4 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php @@ -172,4 +172,29 @@ public function createCollectionForSkuDataProvider() . '`attribute`:`sku`,`operator`:`!^[^]`,`value`:`virtual`^]^]', 'product-with-xss'] ]; } + + /** + * Check that collection returns correct result if use date attribute. + * + * @magentoDbIsolation disabled + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_date_attribute.php + * @return void + */ + public function testProductListWithDateAttribute() + { + $encodedConditions = '^[`1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Combine`,' + . '`aggregator`:`all`,`value`:`1`,`new_child`:``^],' + . '`1--1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Product`,' + . '`attribute`:`date_attribute`,`operator`:`==`,`value`:`' . date('Y-m-d') . '`^]^]'; + $this->block->setData('conditions_encoded', $encodedConditions); + + // Load products collection filtered using specified conditions and perform assertions + $productCollection = $this->block->createCollection(); + $productCollection->load(); + $this->assertEquals( + 1, + $productCollection->count(), + "Product collection was not filtered according to the widget condition." + ); + } } diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php index 4682453012952..e0e390e89c97c 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php @@ -7,6 +7,7 @@ use Magento\Catalog\Api\Data\ProductTierPriceInterface; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Model\Session as CustomerSession; use Magento\Framework\Api\FilterBuilder; @@ -17,7 +18,7 @@ use Magento\TestFramework\Helper\Bootstrap; /** - * Class SessionTest + * Checkout Session model test. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -54,6 +55,35 @@ protected function setUp() $this->checkoutSession = $this->objectManager->create(Session::class); } + /** + * Tests that quote items and totals are correct when product becomes unavailable. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Sales/_files/quote.php + * @magentoAppIsolation enabled + */ + public function testGetQuoteWithUnavailableProduct() + { + $reservedOrderId = 'test01'; + $quoteGrandTotal = 10; + + $quote = $this->getQuote($reservedOrderId); + $this->assertEquals(1, $quote->getItemsCount()); + $this->assertCount(1, $quote->getItems()); + $this->assertEquals($quoteGrandTotal, $quote->getShippingAddress()->getBaseGrandTotal()); + + $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $product = $productRepository->get('simple'); + $product->setStatus(Status::STATUS_DISABLED); + $productRepository->save($product); + $this->checkoutSession->setQuoteId($quote->getId()); + $quote = $this->checkoutSession->getQuote(); + + $this->assertEquals(0, $quote->getItemsCount()); + $this->assertEmpty($quote->getItems()); + $this->assertEquals(0, $quote->getShippingAddress()->getBaseGrandTotal()); + } + /** * Test covers case when quote is not yet initialized and customer data is set to checkout session model. * diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/ValidatorFileMock.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/ValidatorFileMock.php index 440f437e74e7c..9b5650b1826c3 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/_files/ValidatorFileMock.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/ValidatorFileMock.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Checkout\_files; use Magento\Catalog\Model\Product\Option\Type\File\ValidatorFile; @@ -14,27 +16,29 @@ class ValidatorFileMock extends \PHPUnit\Framework\TestCase { /** * Returns mock. - * + * @param array|null $fileData * @return ValidatorFile|\PHPUnit_Framework_MockObject_MockObject */ - public function getInstance() + public function getInstance($fileData = null) { - $userValue = [ - 'type' => 'image/jpeg', - 'title' => "test.jpg", - 'quote_path' => "custom_options/quote/s/t/4624d2.jpg", - 'order_path' => "custom_options/order/s/t/89d25b4624d2.jpg", - "fullpath" => "pub/media/custom_options/quote/s/t/e47389d25b4624d2.jpg", - "size"=> "71901", - "width" => 5, - "height" => 5, - "secret_key" => "10839ec1631b77e5e473", - ]; + if (empty($fileData)) { + $fileData = [ + 'type' => 'image/jpeg', + 'title' => "test.jpg", + 'quote_path' => "custom_options/quote/s/t/4624d2.jpg", + 'order_path' => "custom_options/order/s/t/89d25b4624d2.jpg", + "fullpath" => "pub/media/custom_options/quote/s/t/e47389d25b4624d2.jpg", + "size" => "71901", + "width" => 5, + "height" => 5, + "secret_key" => "10839ec1631b77e5e473", + ]; + } $instance = $this->getMockBuilder(ValidatorFile::class) ->disableOriginalConstructor() ->getMock(); $instance->method('SetProduct')->willReturnSelf(); - $instance->method('validate')->willReturn($userValue); + $instance->method('validate')->willReturn($fileData); return $instance; } diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/active_quote_customer_not_default_store_rollback.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/active_quote_customer_not_default_store_rollback.php index e3e1513cb6144..0ae87725529b8 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/_files/active_quote_customer_not_default_store_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/active_quote_customer_not_default_store_rollback.php @@ -6,3 +6,6 @@ $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); $quote = $objectManager->create(\Magento\Quote\Model\Quote::class); $quote->load('test_order_1_not_default_store', 'reserved_order_id')->delete(); + +require __DIR__ . '/../../../Magento/Customer/_files/customer_rollback.php'; +require __DIR__ . '/../../../Magento/Store/_files/second_store_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php new file mode 100644 index 0000000000000..8bc7a89280559 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php @@ -0,0 +1,225 @@ +aclBuilder = Bootstrap::getObjectManager()->get(Builder::class); + $this->pageRetriever = Bootstrap::getObjectManager()->get(GetPageByIdentifierInterface::class); + $this->scopeConfig = Bootstrap::getObjectManager()->get(ScopeConfigInterface::class); + } + + /** + * @inheritDoc + */ + protected function tearDown() + { + parent::tearDown(); + + foreach ($this->pagesToDelete as $identifier) { + $page = $this->pageRetriever->execute($identifier); + $page->delete(); + } + $this->pagesToDelete = []; + } + + /** + * Check whether additional authorization is required for the design fields. + * + * @magentoDbIsolation disabled + * @return void + */ + public function testSaveDesign(): void + { + //Expected list of sessions messages collected throughout the controller calls. + $sessionMessages = ['You are not allowed to change CMS pages design settings']; + //Test page data. + $id = 'test-page' .rand(1111, 9999); + $requestData = [ + PageInterface::IDENTIFIER => $id, + PageInterface::TITLE => 'Page title', + PageInterface::CUSTOM_THEME => '1', + PageInterface::PAGE_LAYOUT => 'empty' + ]; + + //Creating a new page with design properties without the required permissions. + $this->aclBuilder->getAcl()->deny(null, 'Magento_Cms::save_design'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->dispatch($this->uri); + $this->assertSessionMessages( + self::equalTo($sessionMessages), + MessageInterface::TYPE_ERROR + ); + + //Trying again with the permissions. + $this->aclBuilder->getAcl()->allow(null, ['Magento_Cms::save', 'Magento_Cms::save_design']); + $this->getRequest()->setDispatched(false); + $this->dispatch($this->uri); + /** @var Page $page */ + $page = Bootstrap::getObjectManager()->create(PageInterface::class); + $page->load($id, PageInterface::IDENTIFIER); + $this->assertNotEmpty($page->getId()); + $this->assertEquals(1, $page->getCustomTheme()); + $requestData['page_id'] = $page->getId(); + $this->getRequest()->setPostValue($requestData); + + //Updating the page without the permissions but not touching design settings. + $this->aclBuilder->getAcl()->deny(null, 'Magento_Cms::save_design'); + $this->getRequest()->setDispatched(false); + $this->dispatch($this->uri); + $this->assertSessionMessages(self::equalTo($sessionMessages), MessageInterface::TYPE_ERROR); + + //Updating design settings without the permissions. + $requestData[PageInterface::CUSTOM_THEME] = '2'; + $this->getRequest()->setPostValue($requestData); + $this->getRequest()->setDispatched(false); + $this->dispatch($this->uri); + $sessionMessages[] = $sessionMessages[0]; + $this->assertSessionMessages( + self::equalTo($sessionMessages), + MessageInterface::TYPE_ERROR + ); + } + + /** + * Check that default design values are accepted without the permissions. + * + * @magentoDbIsolation disabled + * @return void + */ + public function testSaveDesignWithDefaults(): void + { + //Test page data. + $id = 'test-page' .rand(1111, 9999); + $defaultLayout = $this->scopeConfig->getValue('web/default_layouts/default_cms_layout'); + $requestData = [ + PageInterface::IDENTIFIER => $id, + PageInterface::TITLE => 'Page title', + PageInterface::PAGE_LAYOUT => $defaultLayout + ]; + //Creating a new page with design properties without the required permissions but with default values. + $this->aclBuilder->getAcl()->deny(null, 'Magento_Cms::save_design'); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->dispatch($this->uri); + + //Validating saved page + /** @var Page $page */ + $page = Bootstrap::getObjectManager()->create(PageInterface::class); + $page->load($id, PageInterface::IDENTIFIER); + $this->assertNotEmpty($page->getId()); + $this->assertNotNull($page->getPageLayout()); + $this->assertEquals($defaultLayout, $page->getPageLayout()); + } + + /** + * Test that custom layout update fields are dealt with properly. + * + * @magentoDataFixture Magento/Cms/_files/pages_with_layout_xml.php + * @throws \Throwable + * @return void + */ + public function testSaveLayoutXml(): void + { + $page = $this->pageRetriever->execute('test_custom_layout_page_1', 0); + $requestData = [ + Page::PAGE_ID => $page->getId(), + PageInterface::IDENTIFIER => 'test_custom_layout_page_1', + PageInterface::TITLE => 'Page title', + PageInterface::CUSTOM_LAYOUT_UPDATE_XML => $page->getCustomLayoutUpdateXml(), + PageInterface::LAYOUT_UPDATE_XML => $page->getLayoutUpdateXml(), + 'layout_update_selected' => '_existing_' + ]; + + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->dispatch($this->uri); + $this->getRequest()->setDispatched(false); + + $updated = $this->pageRetriever->execute('test_custom_layout_page_1', 0); + $this->assertEquals($updated->getCustomLayoutUpdateXml(), $page->getCustomLayoutUpdateXml()); + $this->assertEquals($updated->getLayoutUpdateXml(), $page->getLayoutUpdateXml()); + + $requestData = [ + Page::PAGE_ID => $page->getId(), + PageInterface::IDENTIFIER => 'test_custom_layout_page_1', + PageInterface::TITLE => 'Page title', + PageInterface::CUSTOM_LAYOUT_UPDATE_XML => $page->getCustomLayoutUpdateXml(), + PageInterface::LAYOUT_UPDATE_XML => $page->getLayoutUpdateXml(), + 'layout_update_selected' => '_no_update_' + ]; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue($requestData); + $this->dispatch($this->uri); + $this->getRequest()->setDispatched(false); + + $updated = $this->pageRetriever->execute('test_custom_layout_page_1', 0); + $this->assertEmpty($updated->getCustomLayoutUpdateXml()); + $this->assertEmpty($updated->getLayoutUpdateXml()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/PageTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/PageTest.php index 8a0c715cfaf75..4600cd28fd3fc 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Controller/PageTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/PageTest.php @@ -9,6 +9,10 @@ */ namespace Magento\Cms\Controller; +use Magento\Cms\Api\GetPageByIdentifierInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; + class PageTest extends \Magento\TestFramework\TestCase\AbstractController { public function testViewAction() @@ -62,4 +66,23 @@ public static function cmsPageWithSystemRouteFixture() ->setPageLayout('1column') ->save(); } + + /** + * Check that custom handles are applied when rendering a page. + * + * @return void + * @throws \Throwable + * @magentoDataFixture Magento/Cms/_files/pages_with_layout_xml.php + */ + public function testCustomHandles(): void + { + /** @var GetPageByIdentifierInterface $pageFinder */ + $pageFinder = Bootstrap::getObjectManager()->get(GetPageByIdentifierInterface::class); + $page = $pageFinder->execute('test_custom_layout_page_3', 0); + $this->dispatch('/cms/page/view/page_id/' .$page->getId()); + /** @var LayoutInterface $layout */ + $layout = Bootstrap::getObjectManager()->get(LayoutInterface::class); + $handles = $layout->getUpdate()->getHandles(); + $this->assertContains('cms_page_view_selectable_test_custom_layout_page_3_test_selected', $handles); + } } diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php new file mode 100644 index 0000000000000..e741b95ff4371 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutManagerTest.php @@ -0,0 +1,102 @@ +resultFactory = $objectManager->get(PageResultFactory::class); + //Mocking available list of files for the page. + $handles = [ + 'cms_page_view_selectable_page100_select1', + 'cms_page_view_selectable_page100_select2' + ]; + $processor = $this->getMockBuilder(Merge::class)->disableOriginalConstructor()->getMock(); + $processor->method('getAvailableHandles')->willReturn($handles); + $processorFactory = $this->getMockBuilder(MergeFactory::class)->disableOriginalConstructor()->getMock(); + $processorFactory->method('create')->willReturn($processor); + + $this->manager = $objectManager->create( + CustomLayoutManagerInterface::class, + ['layoutProcessorFactory' => $processorFactory] + ); + $this->repo = $objectManager->create( + CustomLayoutRepositoryInterface::class, + ['manager' => $this->manager] + ); + $this->pageFactory = $objectManager->get(PageFactory::class); + $this->identityMap = $objectManager->get(IdentityMap::class); + } + + /** + * Test updating a page's custom layout. + * + * @magentoDataFixture Magento/Cms/_files/pages.php + * @throws \Throwable + * @return void + */ + public function testCustomLayoutUpdate(): void + { + /** @var Page $page */ + $page = $this->pageFactory->create(['customLayoutRepository' => $this->repo]); + $page->load('page100', 'identifier'); + $pageId = (int)$page->getId(); + $this->identityMap->add($page); + //Set file ID + $this->repo->save(new CustomLayoutSelected($pageId, 'select2')); + + //Test handles + $result = $this->resultFactory->create(); + $this->manager->applyUpdate($result, $this->repo->getFor($pageId)); + $this->identityMap->remove((int)$page->getId()); + $this->assertContains('___selectable_page100_select2', $result->getLayout()->getUpdate()->getHandles()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutRepositoryTest.php new file mode 100644 index 0000000000000..e3422cd81638b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/CustomLayoutRepositoryTest.php @@ -0,0 +1,152 @@ +fakeManager = $objectManager->get(CustomLayoutManager::class); + $this->repo = $objectManager->create(CustomLayoutRepositoryInterface::class, ['manager' => $this->fakeManager]); + $this->pageFactory = $objectManager->get(PageFactory::class); + $this->identityMap = $objectManager->get(IdentityMap::class); + } + + /** + * Create page instance. + * + * @param string $id + * @return Page + */ + private function createPage(string $id): Page + { + $page = $this->pageFactory->create(['customLayoutRepository' => $this->repo]); + $page->load($id, 'identifier'); + $this->identityMap->add($page); + + return $page; + } + + /** + * Test updating a page's custom layout. + * + * @magentoDataFixture Magento/Cms/_files/pages.php + * @return void + */ + public function testCustomLayout(): void + { + $page = $this->createPage('page100'); + $pageId = (int)$page->getId(); + $this->fakeManager->fakeAvailableFiles($pageId, ['select1', 'select2']); + + //Invalid file ID + $exceptionRaised = null; + try { + $this->repo->save(new CustomLayoutSelected($pageId, 'some_file')); + } catch (\Throwable $exception) { + $exceptionRaised = $exception; + } + $this->assertNotEmpty($exceptionRaised); + $this->assertInstanceOf(\InvalidArgumentException::class, $exceptionRaised); + + //Set file ID + $this->repo->save(new CustomLayoutSelected($pageId, 'select2')); + + //Test saved + $saved = $this->repo->getFor($pageId); + $this->assertEquals('select2', $saved->getLayoutFileId()); + + //Removing custom file + $this->repo->deleteFor($pageId); + + //Test saved + $notFound = false; + try { + $this->repo->getFor($pageId); + } catch (NoSuchEntityException $exception) { + $notFound = true; + } + $this->assertTrue($notFound); + } + + /** + * Test that layout updates are saved with save method. + * + * @magentoDataFixture Magento/Cms/_files/pages.php + * @return void + */ + public function testSaved(): void + { + $page = $this->createPage('page100'); + $this->fakeManager->fakeAvailableFiles((int)$page->getId(), ['select1', 'select2']); + + //Special no-update instruction + $page->setData('layout_update_selected', '_no_update_'); + $page->save(); + $this->assertNull($page->getData('layout_update_selected')); + + //Existing file update + $page->setData('layout_update_selected', 'select1'); + $page->save(); + /** @var Page $page */ + $page = $this->pageFactory->create(); + $page->load('page100', 'identifier'); + $this->assertEquals('select1', $page->getData('layout_update_selected')); + $this->assertEquals('select1', $this->repo->getFor((int)$page->getId())->getLayoutFileId()); + + //Invalid file + $caught = null; + $page->setData('layout_update_selected', 'nonExisting'); + try { + $page->save(); + } catch (\Throwable $exception) { + $caught = $exception; + } + $this->assertInstanceOf(LocalizedException::class, $caught); + $this->assertEquals($caught->getMessage(), 'Invalid Custom Layout Update selected'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php new file mode 100644 index 0000000000000..2028f5d8a04b6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Page/DataProviderTest.php @@ -0,0 +1,153 @@ +repo = $objectManager->get(GetPageByIdentifierInterface::class); + $this->filesFaker = $objectManager->get(CustomLayoutManager::class); + $this->request = $objectManager->get(HttpRequest::class); + $this->provider = $objectManager->create( + DataProvider::class, + [ + 'name' => 'test', + 'primaryFieldName' => 'page_id', + 'requestFieldName' => 'page_id', + 'customLayoutManager' => $this->filesFaker + ] + ); + } + + /** + * Check that custom layout date is handled properly. + * + * @magentoDataFixture Magento/Cms/_files/pages_with_layout_xml.php + * @throws \Throwable + * @return void + */ + public function testCustomLayoutData(): void + { + $data = $this->provider->getData(); + $page1Data = null; + $page2Data = null; + $page3Data = null; + foreach ($data as $pageData) { + if ($pageData[PageModel::IDENTIFIER] === 'test_custom_layout_page_1') { + $page1Data = $pageData; + } elseif ($pageData[PageModel::IDENTIFIER] === 'test_custom_layout_page_2') { + $page2Data = $pageData; + } elseif ($pageData[PageModel::IDENTIFIER] === 'test_custom_layout_page_3') { + $page3Data = $pageData; + } + } + $this->assertNotEmpty($page1Data); + $this->assertNotEmpty($page2Data); + $this->assertEquals('_existing_', $page1Data['layout_update_selected']); + $this->assertEquals(null, $page2Data['layout_update_selected']); + $this->assertEquals('test_selected', $page3Data['layout_update_selected']); + } + + /** + * Check that proper meta for custom layout field is returned. + * + * @return void + * @throws \Throwable + * @magentoDataFixture Magento/Cms/_files/pages_with_layout_xml.php + */ + public function testCustomLayoutMeta(): void + { + //Testing a page without layout xml + $page = $this->repo->execute('test_custom_layout_page_3', 0); + $this->filesFaker->fakeAvailableFiles((int)$page->getId(), ['test1', 'test2']); + $this->request->setParam('page_id', $page->getId()); + + $meta = $this->provider->getMeta(); + $this->assertArrayHasKey('design', $meta); + $this->assertArrayHasKey('children', $meta['design']); + $this->assertArrayHasKey('custom_layout_update_select', $meta['design']['children']); + $this->assertArrayHasKey('arguments', $meta['design']['children']['custom_layout_update_select']); + $this->assertArrayHasKey('data', $meta['design']['children']['custom_layout_update_select']['arguments']); + $this->assertArrayHasKey( + 'options', + $meta['design']['children']['custom_layout_update_select']['arguments']['data'] + ); + $expectedList = [ + ['label' => 'No update', 'value' => '_no_update_'], + ['label' => 'test1', 'value' => 'test1'], + ['label' => 'test2', 'value' => 'test2'] + ]; + $metaList = $meta['design']['children']['custom_layout_update_select']['arguments']['data']['options']; + sort($expectedList); + sort($metaList); + $this->assertEquals($expectedList, $metaList); + + //Page with old layout xml + $page = $this->repo->execute('test_custom_layout_page_1', 0); + $this->filesFaker->fakeAvailableFiles((int)$page->getId(), ['test3']); + $this->request->setParam('page_id', $page->getId()); + + $meta = $this->provider->getMeta(); + $this->assertArrayHasKey('design', $meta); + $this->assertArrayHasKey('children', $meta['design']); + $this->assertArrayHasKey('custom_layout_update_select', $meta['design']['children']); + $this->assertArrayHasKey('arguments', $meta['design']['children']['custom_layout_update_select']); + $this->assertArrayHasKey('data', $meta['design']['children']['custom_layout_update_select']['arguments']); + $this->assertArrayHasKey( + 'options', + $meta['design']['children']['custom_layout_update_select']['arguments']['data'] + ); + $expectedList = [ + ['label' => 'No update', 'value' => '_no_update_'], + ['label' => 'Use existing layout update XML', 'value' => '_existing_'], + ['label' => 'test3', 'value' => 'test3'], + ]; + $metaList = $meta['design']['children']['custom_layout_update_select']['arguments']['data']['options']; + sort($expectedList); + sort($metaList); + $this->assertEquals($expectedList, $metaList); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/PageRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/PageRepositoryTest.php index cd4674f95d722..5e7e0c962fcde 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/PageRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/PageRepositoryTest.php @@ -3,96 +3,81 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + declare(strict_types=1); namespace Magento\Cms\Model; -use Magento\Backend\Model\Auth; +use Magento\Cms\Api\GetPageByIdentifierInterface; use Magento\Cms\Api\PageRepositoryInterface; -use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Exception\CouldNotSaveException; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; -use Magento\TestFramework\Bootstrap as TestBootstrap; -use Magento\Framework\Acl\Builder; /** - * Test class for page repository. + * Test page repo. */ class PageRepositoryTest extends TestCase { /** - * Test subject. - * * @var PageRepositoryInterface */ private $repo; /** - * @var Auth - */ - private $auth; - - /** - * @var SearchCriteriaBuilder + * @var GetPageByIdentifierInterface */ - private $criteriaBuilder; + private $retriever; /** - * @var Builder - */ - private $aclBuilder; - - /** - * Sets up common objects. - * * @inheritDoc */ protected function setUp() { - $this->repo = Bootstrap::getObjectManager()->create(PageRepositoryInterface::class); - $this->auth = Bootstrap::getObjectManager()->get(Auth::class); - $this->criteriaBuilder = Bootstrap::getObjectManager()->get(SearchCriteriaBuilder::class); - $this->aclBuilder = Bootstrap::getObjectManager()->get(Builder::class); + $this->repo = Bootstrap::getObjectManager()->get(PageRepositoryInterface::class); + $this->retriever = Bootstrap::getObjectManager()->get(GetPageByIdentifierInterface::class); } /** - * @inheritDoc - */ - protected function tearDown() - { - parent::tearDown(); - - $this->auth->logout(); - } - - /** - * Test authorization when saving page's design settings. + * Test that the field is deprecated. * - * @magentoDataFixture Magento/Cms/_files/pages.php - * @magentoAppArea adminhtml - * @magentoDbIsolation enabled - * @magentoAppIsolation enabled + * @throws \Throwable + * @magentoDataFixture Magento/Cms/_files/pages_with_layout_xml.php + * @return void */ - public function testSaveDesign() + public function testSaveUpdateXml(): void { - $pages = $this->repo->getList( - $this->criteriaBuilder->addFilter('identifier', 'page_design_blank')->create() - )->getItems(); - $page = array_pop($pages); - $this->auth->login(TestBootstrap::ADMIN_NAME, TestBootstrap::ADMIN_PASSWORD); + $page = $this->retriever->execute('test_custom_layout_page_1', 0); + $page->setTitle($page->getTitle() .'TEST'); - //Admin doesn't have access to page's design. - $this->aclBuilder->getAcl()->deny(null, 'Magento_Cms::save_design'); - - $page->setCustomTheme('test'); + //Is successfully saved without changes to the custom layout xml. $page = $this->repo->save($page); - $this->assertNotEquals('test', $page->getCustomTheme()); - //Admin has access to page' design. - $this->aclBuilder->getAcl()->allow(null, ['Magento_Cms::save', 'Magento_Cms::save_design']); + //New value is not accepted. + $page->setCustomLayoutUpdateXml(''); + $forbidden = false; + try { + $page = $this->repo->save($page); + } catch (CouldNotSaveException $exception) { + $forbidden = true; + } + $this->assertTrue($forbidden); + + //New value is not accepted. + $page->setLayoutUpdateXml(''); + $forbidden = false; + try { + $page = $this->repo->save($page); + } catch (CouldNotSaveException $exception) { + $forbidden = true; + } + $this->assertTrue($forbidden); - $page->setCustomTheme('test'); + //Can be removed + $page->setCustomLayoutUpdateXml(null); + $page->setLayoutUpdateXml(null); $page = $this->repo->save($page); - $this->assertEquals('test', $page->getCustomTheme()); + $this->assertEmpty($page->getCustomLayoutUpdateXml()); + $this->assertEmpty($page->getLayoutUpdateXml()); } } diff --git a/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php b/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php new file mode 100644 index 0000000000000..9734ed3abaeed --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php @@ -0,0 +1,47 @@ +get(PageModelFactory::class); +/** @var CustomLayoutManager $fakeManager */ +$fakeManager = $objectManager->get(CustomLayoutManager::class); +$layoutRepo = $objectManager->create(PageModel\CustomLayoutRepositoryInterface::class, ['manager' => $fakeManager]); + +/** @var PageModel $page */ +$page = $pageFactory->create(['customLayoutRepository' => $layoutRepo]); +$page->setIdentifier('test_custom_layout_page_1'); +$page->setTitle('Test Page'); +$page->setCustomLayoutUpdateXml(''); +$page->setLayoutUpdateXml(''); +$page->setIsActive(true); +$page->setStoreId(0); +$page->save(); +/** @var PageModel $page2 */ +$page2 = $pageFactory->create(['customLayoutRepository' => $layoutRepo]); +$page2->setIdentifier('test_custom_layout_page_2'); +$page2->setTitle('Test Page 2'); +$page->setIsActive(true); +$page->setStoreId(0); +$page2->save(); +/** @var PageModel $page3 */ +$page3 = $pageFactory->create(['customLayoutRepository' => $layoutRepo]); +$page3->setIdentifier('test_custom_layout_page_3'); +$page3->setTitle('Test Page 3'); +$page3->setStores([0]); +$page3->setIsActive(1); +$page3->setContent('

Test Page

'); +$page3->setPageLayout('1column'); +$page3->save(); +$fakeManager->fakeAvailableFiles((int)$page3->getId(), ['test_selected']); +$page3->setData('layout_update_selected', 'test_selected'); +$page3->save(); diff --git a/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml_rollback.php b/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml_rollback.php new file mode 100644 index 0000000000000..3217b94d7392b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml_rollback.php @@ -0,0 +1,32 @@ +get(PageModelFactory::class); +/** @var PageModel $page */ +$page = $pageFactory->create(); +$page->load('test_custom_layout_page_1', PageModel::IDENTIFIER); +if ($page->getId()) { + $page->delete(); +} +/** @var PageModel $page2 */ +$page2 = $pageFactory->create(); +$page2->load('test_custom_layout_page_2', PageModel::IDENTIFIER); +if ($page2->getId()) { + $page2->delete(); +} +/** @var PageModel $page3 */ +$page3 = $pageFactory->create(); +$page3->load('test_custom_layout_page_3', PageModel::IDENTIFIER); +if ($page3->getId()) { + $page3->delete(); +} diff --git a/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/config.xml b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/config.xml index f473af1bce37c..5b482e2a61ed0 100644 --- a/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/config.xml +++ b/dev/tests/integration/testsuite/Magento/Config/Model/Config/Structure/Reader/_files/config.xml @@ -13,7 +13,7 @@ Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Expanded - If not specified, Default Country from General Config will be used + If not specified, Default Country from General Config will be used. Magento\Paypal\Block\Adminhtml\System\Config\Field\Country Magento\Paypal\Model\System\Config\Source\MerchantCountry Magento\Paypal\Model\System\Config\Backend\MerchantCountry diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php index 78fa4733a2562..0d2043434d359 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php @@ -254,6 +254,33 @@ public function testGetUsedProducts() } } + /** + * Tests the $requiredAttributes parameter; uses meta_description as an example of an attribute that is not + * included in default attribute select. + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable_with_metadescription.php + */ + public function testGetUsedProductsWithRequiredAttributes() + { + $requiredAttributeIds = [86]; + $products = $this->model->getUsedProducts($this->product, $requiredAttributeIds); + foreach ($products as $product) { + self::assertNotNull($product->getData('meta_description')); + } + } + + /** + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable_with_metadescription.php + */ + public function testGetUsedProductsWithoutRequiredAttributes() + { + $products = $this->model->getUsedProducts($this->product); + foreach ($products as $product) { + self::assertNull($product->getData('meta_description')); + } + } + /** * Test getUsedProducts returns array with same indexes regardless collections was cache or not. * diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_disable_first_child.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_disable_first_child.php new file mode 100644 index 0000000000000..51d192f76c807 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_disable_first_child.php @@ -0,0 +1,23 @@ +get($childSku); +$productAction = Bootstrap::getObjectManager()->get(Action::class); +$productAction->updateAttributes( + [$childProduct->getEntityId()], + [ProductAttributeInterface::CODE_STATUS => Status::STATUS_DISABLED], + $childProduct->getStoreId() +); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_disable_first_child_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_disable_first_child_rollback.php new file mode 100644 index 0000000000000..7c73fa65cfcd1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_disable_first_child_rollback.php @@ -0,0 +1,8 @@ +reinitialize(); + +require __DIR__ . '/configurable_attribute.php'; + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = Bootstrap::getObjectManager() + ->create(ProductRepositoryInterface::class); + +/** @var $installer CategorySetup */ +$installer = Bootstrap::getObjectManager()->create(CategorySetup::class); + +/* Create simple products per each option value*/ +/** @var AttributeOptionInterface[] $options */ +$options = $attribute->getOptions(); + +$attributeValues = []; +$attributeSetId = $installer->getAttributeSetId('catalog_product', 'Default'); +$associatedProductIds = []; +$productIds = [10, 20]; +array_shift($options); //remove the first option which is empty + +foreach ($options as $option) { + /** @var $product Product */ + $product = Bootstrap::getObjectManager()->create(Product::class); + $productId = array_shift($productIds); + $product->setTypeId(Type::TYPE_SIMPLE) + ->setId($productId) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('Configurable Option' . $option->getLabel()) + ->setSku('simple_' . $productId) + ->setPrice($productId) + ->setTestConfigurable($option->getValue()) + ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE) + ->setStatus(Status::STATUS_ENABLED) + ->setMetaDescription('meta_description' . $productId) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]); + + $product = $productRepository->save($product); + + /** @var \Magento\CatalogInventory\Model\Stock\Item $stockItem */ + $stockItem = Bootstrap::getObjectManager()->create(\Magento\CatalogInventory\Model\Stock\Item::class); + $stockItem->load($productId, 'product_id'); + + if (!$stockItem->getProductId()) { + $stockItem->setProductId($productId); + } + $stockItem->setUseConfigManageStock(1); + $stockItem->setQty(1000); + $stockItem->setIsQtyDecimal(0); + $stockItem->setIsInStock(1); + $stockItem->save(); + + $attributeValues[] = [ + 'label' => 'test', + 'attribute_id' => $attribute->getId(), + 'value_index' => $option->getValue(), + ]; + $associatedProductIds[] = $product->getId(); +} + +/** @var $product Product */ +$product = Bootstrap::getObjectManager()->create(Product::class); + +/** @var Factory $optionsFactory */ +$optionsFactory = Bootstrap::getObjectManager()->create(Factory::class); + +$configurableAttributesData = [ + [ + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'label' => $attribute->getStoreLabel(), + 'position' => '0', + 'values' => $attributeValues, + ], +]; + +$configurableOptions = $optionsFactory->create($configurableAttributesData); + +$extensionConfigurableAttributes = $product->getExtensionAttributes(); +$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions); +$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds); + +$product->setExtensionAttributes($extensionConfigurableAttributes); + +// Remove any previously created product with the same id. +/** @var \Magento\Framework\Registry $registry */ +$registry = Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +try { + $productToDelete = $productRepository->getById(1); + $productRepository->delete($productToDelete); + + /** @var \Magento\Quote\Model\ResourceModel\Quote\Item $itemResource */ + $itemResource = Bootstrap::getObjectManager()->get(\Magento\Quote\Model\ResourceModel\Quote\Item::class); + $itemResource->getConnection()->delete( + $itemResource->getMainTable(), + 'product_id = ' . $productToDelete->getId() + ); +} catch (\Exception $e) { + // Nothing to remove +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +$product->setTypeId(Configurable::TYPE_CODE) + ->setId(1) + ->setAttributeSetId($attributeSetId) + ->setWebsiteIds([1]) + ->setName('Configurable Product') + ->setSku('configurable') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); + +$productRepository->save($product); + +/** @var \Magento\Catalog\Api\CategoryLinkManagementInterface $categoryLinkManagement */ +$categoryLinkManagement = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Catalog\Api\CategoryLinkManagementInterface::class); + +$categoryLinkManagement->assignProductToCategories( + $product->getSku(), + [2] +); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_with_metadescription_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_with_metadescription_rollback.php new file mode 100644 index 0000000000000..21953dea6f587 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_with_metadescription_rollback.php @@ -0,0 +1,39 @@ +get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +foreach (['simple_10', 'simple_20', 'configurable'] as $sku) { + try { + $product = $productRepository->get($sku, true); + + $stockStatus = $objectManager->create(\Magento\CatalogInventory\Model\Stock\Status::class); + $stockStatus->load($product->getEntityId(), 'product_id'); + $stockStatus->delete(); + + if ($product->getId()) { + $productRepository->delete($product); + } + } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + //Product already removed + } +} + +require __DIR__ . '/configurable_attribute_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_zero_qty_first_child.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_zero_qty_first_child.php new file mode 100644 index 0000000000000..b923ae6399cc3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_zero_qty_first_child.php @@ -0,0 +1,21 @@ +get($childSku); +$childProduct->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 0, + 'is_qty_decimal' => 0, + 'is_in_stock' => 0 + ] +); +$productRepository->save($childProduct); diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_zero_qty_first_child_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_zero_qty_first_child_rollback.php new file mode 100644 index 0000000000000..7c73fa65cfcd1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_zero_qty_first_child_rollback.php @@ -0,0 +1,8 @@ +getBody()), mb_strtolower($search)) !== false) { + return true; + } + + foreach ($response->getHeaders() as $header) { + if (mb_stripos(mb_strtolower($header->toString()), mb_strtolower($search)) !== false) { + return true; + } + } + + return false; + } + + /** + * Check that configured policies are rendered on frontend. + * + * @magentoAppArea frontend + * @magentoConfigFixture default_store csp/policies/storefront/default_src/policy_id default-src + * @magentoConfigFixture default_store csp/policies/storefront/default_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/default_src/hosts/example http://magento.com + * @magentoConfigFixture default_store csp/policies/storefront/default_src/hosts/example2 http://devdocs.magento.com + * @magentoConfigFixture default_store csp/policies/storefront/default_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/script_src/policy_id script-src + * @magentoConfigFixture default_store csp/policies/storefront/script_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/script_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/script_src/inline 1 + * @magentoConfigFixture default_store csp/policies/admin/font_src/policy_id font-src + * @magentoConfigFixture default_store csp/policies/admin/font_src/none 0 + * @magentoConfigFixture default_store csp/policies/admin/font_src/self 1 + * @return void + */ + public function testStorefrontPolicies(): void + { + $this->dispatch('/'); + $response = $this->getResponse(); + + $this->assertTrue($this->searchInResponse($response, 'Content-Security-Policy')); + $this->assertTrue($this->searchInResponse($response, 'default-src')); + $this->assertTrue($this->searchInResponse($response, 'http://magento.com')); + $this->assertTrue($this->searchInResponse($response, 'http://devdocs.magento.com')); + $this->assertTrue($this->searchInResponse($response, '\'self\'')); + $this->assertFalse($this->searchInResponse($response, '\'none\'')); + $this->assertTrue($this->searchInResponse($response, 'script-src')); + $this->assertTrue($this->searchInResponse($response, '\'unsafe-inline\'')); + $this->assertFalse($this->searchInResponse($response, 'font-src')); + //Policies configured in cps_whitelist.xml files + $this->assertTrue($this->searchInResponse($response, 'object-src')); + $this->assertTrue($this->searchInResponse($response, 'media-src')); + } + + /** + * Check that configured policies are rendered on backend. + * + * @magentoAppArea adminhtml + * @magentoConfigFixture default_store csp/policies/admin/default_src/policy_id default-src + * @magentoConfigFixture default_store csp/policies/admin/default_src/none 0 + * @magentoConfigFixture default_store csp/policies/admin/default_src/hosts/example http://magento.com + * @magentoConfigFixture default_store csp/policies/admin/default_src/hosts/example2 http://devdocs.magento.com + * @magentoConfigFixture default_store csp/policies/admin/default_src/self 1 + * @magentoConfigFixture default_store csp/policies/admin/script_src/policy_id script-src + * @magentoConfigFixture default_store csp/policies/admin/script_src/none 0 + * @magentoConfigFixture default_store csp/policies/admin/default_src/self 1 + * @magentoConfigFixture default_store csp/policies/admin/default_src/inline 1 + * @magentoConfigFixture default_store csp/policies/storefront/font_src/policy_id font-src + * @magentoConfigFixture default_store csp/policies/storefront/font_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/font_src/self 1 + * @return void + */ + public function testAdminPolicies(): void + { + $this->dispatch('backend/'); + $response = $this->getResponse(); + + $this->assertTrue($this->searchInResponse($response, 'Content-Security-Policy')); + $this->assertTrue($this->searchInResponse($response, 'default-src')); + $this->assertTrue($this->searchInResponse($response, 'http://magento.com')); + $this->assertTrue($this->searchInResponse($response, 'http://devdocs.magento.com')); + $this->assertTrue($this->searchInResponse($response, '\'self\'')); + $this->assertFalse($this->searchInResponse($response, '\'none\'')); + $this->assertTrue($this->searchInResponse($response, 'script-src')); + $this->assertTrue($this->searchInResponse($response, '\'unsafe-inline\'')); + $this->assertFalse($this->searchInResponse($response, 'font-src')); + } + + /** + * Check that CSP mode is considered when rendering policies. + * + * @magentoAppArea frontend + * @magentoConfigFixture default_store csp/policies/storefront/default_src/policy_id default-src + * @magentoConfigFixture default_store csp/policies/storefront/default_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/default_src/hosts/example http://magento.com + * @magentoConfigFixture default_store csp/policies/storefront/default_src/hosts/example2 http://devdocs.magento.com + * @magentoConfigFixture default_store csp/policies/storefront/default_src/self 1 + * @magentoConfigFixture default_store csp/mode/storefront/report_only 1 + * @magentoConfigFixture default_store csp/mode/storefront/report_uri /cspEndpoint/ + * @magentoConfigFixture default_store csp/mode/admin/report_only 0 + * @return void + */ + public function testReportOnlyMode(): void + { + $this->dispatch('/'); + $response = $this->getResponse(); + + $this->assertTrue($this->searchInResponse($response, 'Content-Security-Policy-Report-Only')); + $this->assertTrue($this->searchInResponse($response, '/cspEndpoint/')); + $this->assertTrue($this->searchInResponse($response, 'default-src')); + $this->assertTrue($this->searchInResponse($response, 'http://magento.com')); + $this->assertTrue($this->searchInResponse($response, 'http://devdocs.magento.com')); + $this->assertTrue($this->searchInResponse($response, '\'self\'')); + } + + /** + * Check that CSP reporting options are rendered for 'restrict' mode as well. + * + * @magentoAppArea frontend + * @magentoConfigFixture default_store csp/policies/storefront/default_src/policy_id default-src + * @magentoConfigFixture default_store csp/policies/storefront/default_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/default_src/hosts/example http://magento.com + * @magentoConfigFixture default_store csp/policies/storefront/default_src/hosts/example2 http://devdocs.magento.com + * @magentoConfigFixture default_store csp/policies/storefront/default_src/self 1 + * @magentoConfigFixture default_store csp/mode/storefront/report_only 0 + * @magentoConfigFixture default_store csp/mode/storefront/report_uri /cspEndpoint/ + * @magentoConfigFixture default_store csp/mode/admin/report_only 0 + * @return void + */ + public function testRestrictMode(): void + { + $this->dispatch('/'); + $response = $this->getResponse(); + + $this->assertFalse($this->searchInResponse($response, 'Content-Security-Policy-Report-Only')); + $this->assertTrue($this->searchInResponse($response, 'Content-Security-Policy')); + $this->assertTrue($this->searchInResponse($response, '/cspEndpoint/')); + $this->assertTrue($this->searchInResponse($response, 'default-src')); + $this->assertTrue($this->searchInResponse($response, 'http://magento.com')); + $this->assertTrue($this->searchInResponse($response, 'http://devdocs.magento.com')); + $this->assertTrue($this->searchInResponse($response, '\'self\'')); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php b/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php new file mode 100644 index 0000000000000..6d8876012df1e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/ConfigCollectorTest.php @@ -0,0 +1,186 @@ +collector = Bootstrap::getObjectManager()->get(ConfigCollector::class); + } + + /** + * Create expected policy objects. + * + * @return PolicyInterface[] + */ + private function getExpectedPolicies(): array + { + return [ + 'child-src' => new FetchPolicy( + 'child-src', + false, + ['http://magento.com', 'http://devdocs.magento.com'], + ['http'], + true, + true, + false, + [], + [], + true + ), + 'child-src2' => new FetchPolicy('child-src', false, [], [], false, false, true), + 'connect-src' => new FetchPolicy('connect-src'), + 'default-src' => new FetchPolicy( + 'default-src', + false, + ['http://magento.com', 'http://devdocs.magento.com'], + [], + true + ), + 'font-src' => new FetchPolicy('font-src', false, [], [], true), + 'frame-src' => new FetchPolicy('frame-src', false, [], [], true, false, false, [], [], true), + 'img-src' => new FetchPolicy('img-src', false, [], [], true), + 'manifest-src' => new FetchPolicy('manifest-src', false, [], [], true), + 'media-src' => new FetchPolicy('media-src', false, [], [], true), + 'object-src' => new FetchPolicy('object-src', false, [], [], true), + 'script-src' => new FetchPolicy('script-src', false, [], [], true, false, false, [], [], false, true), + 'style-src' => new FetchPolicy('style-src', false, [], [], true), + 'base-uri' => new FetchPolicy('base-uri', false, [], [], true), + 'plugin-types' => new PluginTypesPolicy( + ['application/x-shockwave-flash', 'application/x-java-applet'] + ), + 'sandbox' => new SandboxPolicy(true, true, true, true, false, false, true, true, true, true, true), + 'form-action' => new FetchPolicy('form-action', false, [], [], true), + 'frame-ancestors' => new FetchPolicy('frame-ancestors', false, [], [], true), + 'block-all-mixed-content' => new FlagPolicy('block-all-mixed-content'), + 'upgrade-insecure-requests' => new FlagPolicy('upgrade-insecure-requests') + ]; + } + + /** + * Test initiating policies from config. + * + * @magentoAppArea frontend + * @magentoConfigFixture default_store csp/policies/storefront/default_src/policy_id default-src + * @magentoConfigFixture default_store csp/policies/storefront/default_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/default_src/hosts/example http://magento.com + * @magentoConfigFixture default_store csp/policies/storefront/default_src/hosts/example2 http://devdocs.magento.com + * @magentoConfigFixture default_store csp/policies/storefront/default_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/child_src/policy_id child-src + * @magentoConfigFixture default_store csp/policies/storefront/child_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/child_src/hosts/example http://magento.com + * @magentoConfigFixture default_store csp/policies/storefront/child_src/hosts/example2 http://devdocs.magento.com + * @magentoConfigFixture default_store csp/policies/storefront/child_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/child_src/inline 1 + * @magentoConfigFixture default_store csp/policies/storefront/child_src/schemes/scheme1 http + * @magentoConfigFixture default_store csp/policies/storefront/child_src/dynamic 1 + * @magentoConfigFixture default_store csp/policies/storefront/child_src2/policy_id child-src + * @magentoConfigFixture default_store csp/policies/storefront/child_src2/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/child_src2/eval 1 + * @magentoConfigFixture default_store csp/policies/storefront/connect_src/policy_id connect-src + * @magentoConfigFixture default_store csp/policies/storefront/connect_src/none 1 + * @magentoConfigFixture default_store csp/policies/storefront/font_src/policy_id font-src + * @magentoConfigFixture default_store csp/policies/storefront/font_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/font_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/frame_src/policy_id frame-src + * @magentoConfigFixture default_store csp/policies/storefront/frame_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/frame_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/frame_src/dynamic 1 + * @magentoConfigFixture default_store csp/policies/storefront/img_src/policy_id img-src + * @magentoConfigFixture default_store csp/policies/storefront/img_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/img_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/manifest_src/policy_id manifest-src + * @magentoConfigFixture default_store csp/policies/storefront/manifest_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/manifest_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/media_src/policy_id media-src + * @magentoConfigFixture default_store csp/policies/storefront/media_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/media_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/object_src/policy_id object-src + * @magentoConfigFixture default_store csp/policies/storefront/object_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/object_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/script_src/policy_id script-src + * @magentoConfigFixture default_store csp/policies/storefront/script_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/script_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/script_src/event_handlers 1 + * @magentoConfigFixture default_store csp/policies/storefront/base_uri/policy_id base-uri + * @magentoConfigFixture default_store csp/policies/storefront/base_uri/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/base_uri/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/style_src/policy_id style-src + * @magentoConfigFixture default_store csp/policies/storefront/style_src/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/style_src/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/form_action/policy_id form-action + * @magentoConfigFixture default_store csp/policies/storefront/form_action/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/form_action/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/frame_ancestors/policy_id frame-ancestors + * @magentoConfigFixture default_store csp/policies/storefront/frame_ancestors/none 0 + * @magentoConfigFixture default_store csp/policies/storefront/frame_ancestors/self 1 + * @magentoConfigFixture default_store csp/policies/storefront/plugin_types/policy_id plugin-types + * @magentoConfigFixture default_store csp/policies/storefront/plugin_types/types/fl application/x-shockwave-flash + * @magentoConfigFixture default_store csp/policies/storefront/plugin_types/types/applet application/x-java-applet + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/policy_id sandbox + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/forms 1 + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/modals 1 + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/orientation 1 + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/pointer 1 + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/popup 0 + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/popups_to_escape 0 + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/presentation 1 + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/same_origin 1 + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/scripts 1 + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/navigation 1 + * @magentoConfigFixture default_store csp/policies/storefront/sandbox/navigation_by_user 1 + * @magentoConfigFixture default_store csp/policies/storefront/mixed_content/policy_id block-all-mixed-content + * @magentoConfigFixture default_store csp/policies/storefront/upgrade/policy_id upgrade-insecure-requests + * @return void + */ + public function testCollecting(): void + { + $policies = $this->collector->collect([new FlagPolicy('upgrade-insecure-requests')]); + $checked = []; + $expectedPolicies = $this->getExpectedPolicies(); + + //Policies were collected + $this->assertNotEmpty($policies); + //Default policies are being kept + /** @var PolicyInterface $defaultPolicy */ + $defaultPolicy = array_shift($policies); + $this->assertEquals('upgrade-insecure-requests', $defaultPolicy->getId()); + //Comparing collected with configured + /** @var PolicyInterface $policy */ + foreach ($policies as $policy) { + $id = $policy->getId(); + if ($id === 'child-src' && $policy->isEvalAllowed()) { + $id = 'child-src2'; + } + $this->assertEquals($expectedPolicies[$id], $policy); + $checked[] = $id; + } + $expectedIds = array_keys($expectedPolicies); + $this->assertEquals(sort($expectedIds), sort($checked)); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/CspWhitelistXmlCollectorTest.php b/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/CspWhitelistXmlCollectorTest.php new file mode 100644 index 0000000000000..bbaabba9dd268 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Csp/Model/Collector/CspWhitelistXmlCollectorTest.php @@ -0,0 +1,68 @@ +collector = Bootstrap::getObjectManager()->get(CspWhitelistXmlCollector::class); + } + + /** + * Test collecting configurations from multiple XML files. + * + * @return void + */ + public function testCollecting(): void + { + $policies = $this->collector->collect([]); + + $mediaSrcChecked = false; + $objectSrcChecked = false; + $this->assertNotEmpty($policies); + /** @var FetchPolicy $policy */ + foreach ($policies as $policy) { + $this->assertFalse($policy->isNoneAllowed()); + $this->assertFalse($policy->isSelfAllowed()); + $this->assertFalse($policy->isInlineAllowed()); + $this->assertFalse($policy->isEvalAllowed()); + $this->assertFalse($policy->isDynamicAllowed()); + $this->assertEmpty($policy->getSchemeSources()); + $this->assertEmpty($policy->getNonceValues()); + if ($policy->getId() === 'object-src') { + $this->assertInstanceOf(FetchPolicy::class, $policy); + $this->assertEquals(['http://magento.com', 'https://devdocs.magento.com'], $policy->getHostSources()); + $this->assertEquals(['B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF8=' => 'sha256'], $policy->getHashes()); + $objectSrcChecked = true; + } elseif ($policy->getId() === 'media-src') { + $this->assertInstanceOf(FetchPolicy::class, $policy); + $this->assertEquals(['https://magento.com', 'https://devdocs.magento.com'], $policy->getHostSources()); + $this->assertEmpty($policy->getHashes()); + $mediaSrcChecked = true; + } + } + $this->assertTrue($objectSrcChecked); + $this->assertTrue($mediaSrcChecked); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Csp/Model/CompositePolicyCollectorTest.php b/dev/tests/integration/testsuite/Magento/Csp/Model/CompositePolicyCollectorTest.php new file mode 100644 index 0000000000000..fd0c58235de1d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Csp/Model/CompositePolicyCollectorTest.php @@ -0,0 +1,177 @@ +getMockForAbstractClass(PolicyCollectorInterface::class); + $mockCollector1->method('collect') + ->willReturnCallback( + function (array $prevPolicies) { + return array_merge( + $prevPolicies, + [ + new FetchPolicy( + 'script-src', + false, + ['https://magento.com'], + ['https'], + true, + false, + true, + ['569403695046645'], + ['B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF8=' => 'sha256'], + false, + true + ), + new FetchPolicy('script-src', false, ['https://devdocs.magento.com']), + new FlagPolicy('upgrade-insecure-requests'), + new PluginTypesPolicy(['application/x-shockwave-flash']), + new SandboxPolicy(false, true, false, true, false, true, false, true, false, true, false) + ] + ); + } + ); + $mockCollector2 = $this->getMockForAbstractClass(PolicyCollectorInterface::class); + $mockCollector2->method('collect') + ->willReturnCallback( + function (array $prevPolicies) { + return array_merge( + $prevPolicies, + [ + new FetchPolicy( + 'script-src', + true, + ['http://magento.com'], + ['http'], + false, + false, + false, + ['5694036950466451'], + ['B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF7=' => 'sha256'], + true, + false + ), + new FetchPolicy('default-src', false, [], [], true), + new FlagPolicy('upgrade-insecure-requests'), + new PluginTypesPolicy(['application/x-java-applet']), + new SandboxPolicy(true, false, true, false, true, false, true, false, true, false, false) + ] + ); + } + ); + + return [$mockCollector1, $mockCollector2]; + } + + /** + * Test collect method. + * + * Supply fake collectors, check results. + * + * @return void + */ + public function testCollect(): void + { + /** @var CompositePolicyCollector $collector */ + $collector = Bootstrap::getObjectManager()->create( + CompositePolicyCollector::class, + ['collectors' => $this->createMockCollectors()] + ); + + $collected = $collector->collect([]); + /** @var FetchPolicy[]|FlagPolicy[]|PluginTypesPolicy[]|SandboxPolicy[] $policies */ + $policies = []; + /** @var \Magento\Csp\Api\Data\PolicyInterface $policy */ + foreach ($collected as $policy) { + $policies[$policy->getId()] = $policy; + } + //Comparing resulting policies + $this->assertArrayHasKey('script-src', $policies); + $this->assertTrue($policies['script-src']->isNoneAllowed()); + $this->assertTrue($policies['script-src']->isSelfAllowed()); + $this->assertFalse($policies['script-src']->isInlineAllowed()); + $this->assertTrue($policies['script-src']->isEvalAllowed()); + $this->assertTrue($policies['script-src']->isDynamicAllowed()); + $this->assertTrue($policies['script-src']->areEventHandlersAllowed()); + $foundHosts = $policies['script-src']->getHostSources(); + $hosts = ['http://magento.com', 'https://magento.com', 'https://devdocs.magento.com']; + sort($foundHosts); + sort($hosts); + $this->assertEquals($hosts, $foundHosts); + $foundSchemes = $policies['script-src']->getSchemeSources(); + $schemes = ['https', 'http']; + sort($foundSchemes); + sort($schemes); + $this->assertEquals($schemes, $foundSchemes); + $foundNonceValues = $policies['script-src']->getNonceValues(); + $nonceValues = ['5694036950466451', '569403695046645']; + sort($foundNonceValues); + sort($nonceValues); + $this->assertEquals($nonceValues, $foundNonceValues); + $foundHashes = $policies['script-src']->getHashes(); + $hashes = [ + 'B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF7=' => 'sha256', + 'B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF8=' => 'sha256' + ]; + $this->assertEquals($hashes, $foundHashes); + + $this->assertArrayHasKey('default-src', $policies); + $this->assertFalse($policies['default-src']->isNoneAllowed()); + $this->assertTrue($policies['default-src']->isSelfAllowed()); + $this->assertFalse($policies['default-src']->isInlineAllowed()); + $this->assertFalse($policies['default-src']->isEvalAllowed()); + $this->assertFalse($policies['default-src']->isDynamicAllowed()); + $this->assertFalse($policies['default-src']->areEventHandlersAllowed()); + $this->assertEmpty($policies['default-src']->getHashes()); + $this->assertEmpty($policies['default-src']->getNonceValues()); + $this->assertEmpty($policies['default-src']->getHostSources()); + $this->assertEmpty($policies['default-src']->getSchemeSources()); + + $this->assertArrayHasKey('upgrade-insecure-requests', $policies); + $this->assertInstanceOf(FlagPolicy::class, $policies['upgrade-insecure-requests']); + + $this->assertArrayHasKey('plugin-types', $policies); + $types = ['application/x-java-applet', 'application/x-shockwave-flash']; + $foundTypes = $policies['plugin-types']->getTypes(); + sort($types); + sort($foundTypes); + $this->assertEquals($types, $foundTypes); + + $this->assertArrayHasKey('sandbox', $policies); + $this->assertTrue($policies['sandbox']->isFormAllowed()); + $this->assertTrue($policies['sandbox']->isModalsAllowed()); + $this->assertTrue($policies['sandbox']->isOrientationLockAllowed()); + $this->assertTrue($policies['sandbox']->isPointerLockAllowed()); + $this->assertTrue($policies['sandbox']->isPopupsAllowed()); + $this->assertTrue($policies['sandbox']->isPopupsToEscapeSandboxAllowed()); + $this->assertTrue($policies['sandbox']->isScriptsAllowed()); + $this->assertFalse($policies['sandbox']->isTopNavigationByUserActivationAllowed()); + $this->assertTrue($policies['sandbox']->isTopNavigationAllowed()); + $this->assertTrue($policies['sandbox']->isSameOriginAllowed()); + $this->assertTrue($policies['sandbox']->isPresentationAllowed()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Csp/Model/Mode/ConfigManagerTest.php b/dev/tests/integration/testsuite/Magento/Csp/Model/Mode/ConfigManagerTest.php new file mode 100644 index 0000000000000..44790ef9dbc94 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Csp/Model/Mode/ConfigManagerTest.php @@ -0,0 +1,86 @@ +manager = Bootstrap::getObjectManager()->get(ConfigManager::class); + } + + /** + * Check the default configurations of CSP. + * + * @magentoAppArea frontend + * @return void + */ + public function testStorefrontDefault(): void + { + $config = $this->manager->getConfigured(); + $this->assertTrue($config->isReportOnly()); + $this->assertNull($config->getReportUri()); + } + + /** + * Check the default configurations of CSP. + * + * @magentoAppArea adminhtml + * @return void + */ + public function testAdminDefault(): void + { + $config = $this->manager->getConfigured(); + $this->assertTrue($config->isReportOnly()); + $this->assertNull($config->getReportUri()); + } + + /** + * Check that class returns correct configurations. + * + * @magentoAppArea frontend + * @magentoConfigFixture default_store csp/mode/storefront/report_only 0 + * @magentoConfigFixture default_store csp/mode/storefront/report_uri https://magento.com + * @return void + */ + public function testFrontendConfigured(): void + { + $config = $this->manager->getConfigured(); + $this->assertFalse($config->isReportOnly()); + $this->assertEquals('https://magento.com', $config->getReportUri()); + } + + /** + * Check that class returns correct configurations. + * + * @magentoAppArea adminhtml + * @magentoConfigFixture default_store csp/mode/admin/report_only 0 + * @magentoConfigFixture default_store csp/mode/admin/report_uri https://magento.com + * @return void + */ + public function testAdminConfigured(): void + { + $config = $this->manager->getConfigured(); + $this->assertFalse($config->isReportOnly()); + $this->assertEquals('https://magento.com', $config->getReportUri()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php b/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php new file mode 100644 index 0000000000000..a67665c6d3c48 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Csp/Model/Policy/Renderer/SimplePolicyHeaderRendererTest.php @@ -0,0 +1,147 @@ +renderer = Bootstrap::getObjectManager()->get(SimplePolicyHeaderRenderer::class); + $this->response = Bootstrap::getObjectManager()->create(HttpResponse::class); + } + + /** + * Test policy rendering in restrict mode. + * + * @magentoAppArea frontend + * @magentoConfigFixture default_store csp/mode/storefront/report_only 0 + * @magentoConfigFixture default_store csp/mode/storefront/report_uri 0 + * + * @return void + */ + public function testRenderRestrictMode(): void + { + $policy = new FetchPolicy('default-src', false, ['https://magento.com'], [], true); + + $this->renderer->render($policy, $this->response); + + $this->assertNotEmpty($header = $this->response->getHeader('Content-Security-Policy')); + $this->assertEmpty($this->response->getHeader('Content-Security-Policy-Report-Only')); + $this->assertEquals('default-src https://magento.com \'self\';', $header->getFieldValue()); + } + + /** + * Test policy rendering in restrict mode with report URL provided. + * + * @magentoAppArea frontend + * @magentoConfigFixture default_store csp/mode/storefront/report_only 0 + * @magentoConfigFixture default_store csp/mode/storefront/report_uri /csp-reports/ + * + * @return void + */ + public function testRenderRestrictWithReportingMode(): void + { + $policy = new FetchPolicy('default-src', false, ['https://magento.com'], [], true); + + $this->renderer->render($policy, $this->response); + + $this->assertNotEmpty($header = $this->response->getHeader('Content-Security-Policy')); + $this->assertEmpty($this->response->getHeader('Content-Security-Policy-Report-Only')); + $this->assertEquals( + 'default-src https://magento.com \'self\'; report-uri /csp-reports/; report-to report-endpoint;', + $header->getFieldValue() + ); + $this->assertNotEmpty($reportToHeader = $this->response->getHeader('Report-To')); + $this->assertNotEmpty($reportData = json_decode("[{$reportToHeader->getFieldValue()}]", true)); + $this->assertEquals('report-endpoint', $reportData[0]['group']); + } + + /** + * Test policy rendering in report-only mode. + * + * @magentoAppArea frontend + * @magentoConfigFixture default_store csp/mode/storefront/report_only 1 + * @magentoConfigFixture default_store csp/mode/storefront/report_uri 0 + * + * @return void + */ + public function testRenderReportMode(): void + { + $policy = new FetchPolicy( + 'default-src', + false, + ['https://magento.com'], + ['https'], + true, + true, + true, + ['5749837589457695'], + ['B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF8=' => 'sha256'], + true, + true + ); + + $this->renderer->render($policy, $this->response); + + $this->assertNotEmpty($header = $this->response->getHeader('Content-Security-Policy-Report-Only')); + $this->assertEmpty($this->response->getHeader('Content-Security-Policy')); + $this->assertEquals( + 'default-src https://magento.com https: \'self\' \'unsafe-inline\' \'unsafe-eval\' \'strict-dynamic\'' + . ' \'unsafe-hashes\' \'nonce-'.base64_encode($policy->getNonceValues()[0]).'\'' + . ' \'sha256-B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF8=\';', + $header->getFieldValue() + ); + } + + /** + * Test policy rendering in report-only mode with report URL provided. + * + * @magentoAppArea frontend + * @magentoConfigFixture default_store csp/mode/storefront/report_only 1 + * @magentoConfigFixture default_store csp/mode/storefront/report_uri /csp-reports/ + * + * @return void + */ + public function testRenderReportWithReportingMode(): void + { + $policy = new FetchPolicy('default-src', false, ['https://magento.com'], [], true); + + $this->renderer->render($policy, $this->response); + + $this->assertNotEmpty($header = $this->response->getHeader('Content-Security-Policy-Report-Only')); + $this->assertEmpty($this->response->getHeader('Content-Security-Policy')); + $this->assertEquals( + 'default-src https://magento.com \'self\'; report-uri /csp-reports/; report-to report-endpoint;', + $header->getFieldValue() + ); + $this->assertNotEmpty($reportToHeader = $this->response->getHeader('Report-To')); + $this->assertNotEmpty($reportData = json_decode("[{$reportToHeader->getFieldValue()}]", true)); + $this->assertEquals('report-endpoint', $reportData[0]['group']); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/NewsletterTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/NewsletterTest.php index e28a9602b9377..9ddba4b994b40 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/NewsletterTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/NewsletterTest.php @@ -9,7 +9,7 @@ use Magento\TestFramework\Helper\Bootstrap; /** - * Class NewsletterTest + * Test Customer account form block functionality * * @magentoAppArea adminhtml */ @@ -61,12 +61,13 @@ public function tearDown() */ public function testRenderingNewsletterBlock() { + $websiteId = 1; $this->getRequest()->setParam('id', 1); $this->dispatch('backend/customer/index/edit'); $body = $this->getResponse()->getBody(); $this->assertContains('\u003Cspan\u003ENewsletter Information\u003C\/span\u003E', $body); - $this->assertContains('\u003Cinput id=\"_newslettersubscription\"', $body); + $this->assertContains('\u003Cinput id=\"_newslettersubscription_status_' . $websiteId . '\"', $body); $this->assertNotContains('checked="checked"', $body); $this->assertContains('\u003Cspan\u003ESubscribed to Newsletter\u003C\/span\u003E', $body); $this->assertContains('\u003ENo Newsletter Found\u003C', $body); diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php index d106fed69c2e0..df4acf3acca91 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php @@ -9,6 +9,7 @@ use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Model\Account\Redirect; +use Magento\Customer\Model\CustomerRegistry; use Magento\Customer\Model\Session; use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\SearchCriteriaBuilder; @@ -20,11 +21,14 @@ use Magento\Framework\Message\MessageInterface; use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\Stdlib\CookieManagerInterface; +use Magento\Store\Model\StoreManager; +use Magento\Store\Model\StoreManagerInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Mail\Template\TransportBuilderMock; use Magento\TestFramework\Request; use Magento\TestFramework\Response; use Magento\Theme\Controller\Result\MessagePlugin; +use PHPUnit\Framework\Constraint\StringContains; use Zend\Stdlib\Parameters; /** @@ -744,6 +748,65 @@ public function testLoginPostRedirect($redirectDashboard, string $redirectUrl) $this->assertTrue($this->_objectManager->get(Session::class)->isLoggedIn()); } + /** + * Register Customer with email confirmation. + * + * @magentoDataFixture Magento/Customer/_files/customer_confirmation_config_enable.php + * @return void + */ + public function testRegisterCustomerWithEmailConfirmation(): void + { + $email = 'test_example@email.com'; + $this->fillRequestWithAccountDataAndFormKey($email); + $this->dispatch('customer/account/createPost'); + $this->assertRedirect($this->stringContains('customer/account/index/')); + $this->assertSessionMessages( + $this->equalTo( + [ + 'You must confirm your account. Please check your email for the confirmation link or ' + . 'click here for a new link.' + ] + ), + MessageInterface::TYPE_SUCCESS + ); + /** @var CustomerRepositoryInterface $customerRepository */ + $customerRepository = $this->_objectManager->create(CustomerRepositoryInterface::class); + /** @var CustomerInterface $customer */ + $customer = $customerRepository->get($email); + $confirmation = $customer->getConfirmation(); + $message = $this->transportBuilderMock->getSentMessage(); + $rawMessage = $message->getBody()->getParts()[0]->getRawContent(); + $messageConstraint = $this->logicalAnd( + new StringContains("You must confirm your {$email} email before you can sign in (link is only valid once"), + new StringContains("customer/account/confirm/?id={$customer->getId()}&key={$confirmation}") + ); + $this->assertThat($rawMessage, $messageConstraint); + + /** @var CookieManagerInterface $cookieManager */ + $cookieManager = $this->_objectManager->get(CookieManagerInterface::class); + $cookieManager->deleteCookie(MessagePlugin::MESSAGES_COOKIES_NAME); + $this->_objectManager->removeSharedInstance(Http::class); + $this->_objectManager->removeSharedInstance(Request::class); + $this->_request = null; + + $this->getRequest() + ->setParam('id', $customer->getId()) + ->setParam('key', $confirmation); + $this->dispatch('customer/account/confirm'); + + /** @var StoreManager $store */ + $store = $this->_objectManager->get(StoreManagerInterface::class); + $name = $store->getStore()->getFrontendName(); + + $this->assertRedirect($this->stringContains('customer/account/index/')); + $this->assertSessionMessages( + $this->equalTo(["Thank you for registering with {$name}."]), + MessageInterface::TYPE_SUCCESS + ); + $this->assertEmpty($customerRepository->get($email)->getConfirmation()); + } + /** * Test that confirmation email address displays special characters correctly. * @@ -796,6 +859,141 @@ public function testConfirmationEmailWithSpecialCharacters(): void ); } + /** + * Check that Customer which change email can't log in with old email. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoConfigFixture current_store customer/captcha/enable 0 + * + * @return void + */ + public function testResetPasswordWhenEmailChanged(): void + { + $email = 'customer@example.com'; + $newEmail = 'new_customer@example.com'; + + /* Reset password and check mail with token */ + $this->getRequest()->setPostValue(['email' => $email]); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + + $this->dispatch('customer/account/forgotPasswordPost'); + $this->assertRedirect($this->stringContains('customer/account/')); + $this->assertSessionMessages( + $this->equalTo( + [ + "If there is an account associated with {$email} you will receive an email with a link " + . "to reset your password." + ] + ), + MessageInterface::TYPE_SUCCESS + ); + + /** @var CustomerRegistry $customerRegistry */ + $customerRegistry = $this->_objectManager->get(CustomerRegistry::class); + $customerData = $customerRegistry->retrieveByEmail($email); + $token = $customerData->getRpToken(); + $this->assertForgotPasswordEmailContent($token); + + /* Set new email */ + /** @var CustomerRepositoryInterface $customerRepository */ + $customerRepository = $this->_objectManager->create(CustomerRepositoryInterface::class); + /** @var \Magento\Customer\Api\Data\CustomerInterface $customer */ + $customer = $customerRepository->getById($customerData->getId()); + $customer->setEmail($newEmail); + $customerRepository->save($customer); + + /* Goes through the link in a mail */ + $this->resetRequest(); + $this->getRequest() + ->setParam('token', $token) + ->setParam('id', $customerData->getId()); + + $this->dispatch('customer/account/createPassword'); + + $this->assertRedirect($this->stringContains('customer/account/forgotpassword')); + $this->assertSessionMessages( + $this->equalTo(['Your password reset link has expired.']), + MessageInterface::TYPE_ERROR + ); + /* Trying to log in with old email */ + $this->resetRequest(); + $this->clearCookieMessagesList(); + $customerRegistry->removeByEmail($email); + + $this->dispatchLoginPostAction($email, 'password'); + $this->assertSessionMessages( + $this->equalTo( + [ + 'The account sign-in was incorrect or your account is disabled temporarily. ' + . 'Please wait and try again later.' + ] + ), + MessageInterface::TYPE_ERROR + ); + $this->assertRedirect($this->stringContains('customer/account/login')); + /** @var Session $session */ + $session = $this->_objectManager->get(Session::class); + $this->assertFalse($session->isLoggedIn()); + + /* Trying to log in with correct(new) email */ + $this->resetRequest(); + $this->dispatchLoginPostAction($newEmail, 'password'); + $this->assertRedirect($this->stringContains('customer/account/')); + $this->assertTrue($session->isLoggedIn()); + $session->logout(); + } + + /** + * Set needed parameters and dispatch Customer loginPost action. + * + * @param string $email + * @param string $password + * @return void + */ + private function dispatchLoginPostAction(string $email, string $password): void + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue( + [ + 'login' => [ + 'username' => $email, + 'password' => $password, + ], + ] + ); + $this->dispatch('customer/account/loginPost'); + } + + /** + * Check that 'Forgot password' email contains correct data. + * + * @param string $token + * @return void + */ + private function assertForgotPasswordEmailContent(string $token): void + { + $message = $this->transportBuilderMock->getSentMessage(); + $pattern = "//"; + $rawMessage = $message->getBody()->getParts()[0]->getRawContent(); + $messageConstraint = $this->logicalAnd( + new StringContains('There was recently a request to change the password for your account.'), + $this->matchesRegularExpression($pattern) + ); + $this->assertThat($rawMessage, $messageConstraint); + } + + /** + * Clear request object. + * + * @return void + */ + private function resetRequest(): void + { + $this->_objectManager->removeSharedInstance(Http::class); + $this->_objectManager->removeSharedInstance(Request::class); + $this->_request = null; + } + /** * Data provider for testLoginPostRedirect. * diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php index db1cc4995e676..d584fb46cda02 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/GroupTest.php @@ -39,15 +39,6 @@ public function setUp() $this->groupRepository = $objectManager->get(\Magento\Customer\Api\GroupRepositoryInterface::class); } - /** - * @inheritDoc - */ - public function tearDown() - { - parent::tearDown(); - //$this->session->unsCustomerGroupData(); - } - /** * Test new group form. */ @@ -199,7 +190,7 @@ public function testSaveActionCreateNewGroupWithoutCode() $this->dispatch('backend/customer/group/save'); $this->assertSessionMessages( - $this->equalTo(['"code" is required. Enter and try again.']), + $this->equalTo([htmlspecialchars('"code" is required. Enter and try again.')]), MessageInterface::TYPE_ERROR ); } @@ -292,7 +283,7 @@ public function testSaveActionNewGroupWithoutGroupCode() $this->dispatch('backend/customer/group/save'); $this->assertSessionMessages( - $this->equalTo(['"code" is required. Enter and try again.']), + $this->equalTo([htmlspecialchars('"code" is required. Enter and try again.')]), MessageInterface::TYPE_ERROR ); $this->assertSessionMessages($this->isEmpty(), MessageInterface::TYPE_SUCCESS); diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php index 1b7f2c1f7efdd..d76c520ade3b1 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php @@ -128,6 +128,7 @@ public function testSaveActionWithInvalidFormData() public function testSaveActionExistingCustomerUnsubscribeNewsletter() { $customerId = 1; + $websiteId = 1; /** @var \Magento\Newsletter\Model\Subscriber $subscriber */ $subscriber = $this->objectManager->get(\Magento\Newsletter\Model\SubscriberFactory::class)->create(); @@ -144,7 +145,7 @@ public function testSaveActionExistingCustomerUnsubscribeNewsletter() 'lastname' => 'test lastname', 'sendemail_store_id' => 1 ], - 'subscription' => '0' + 'subscription_status' => [$websiteId => '0'] ]; $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); $this->getRequest()->setParam('id', 1); @@ -444,7 +445,7 @@ public function testProductReviewsAction() $this->getRequest()->setParam('id', 1); $this->dispatch('backend/customer/index/productReviews'); $body = $this->getResponse()->getBody(); - $this->assertContains('
assertContains('
dataObjectHelper = $this->objectManager->create(\Magento\Framework\Api\DataObjectHelper::class); + $this->customerRegistry = $this->objectManager->get(\Magento\Customer\Model\CustomerRegistry::class); $regionFactory = $this->objectManager->get(RegionInterfaceFactory::class); $region = $regionFactory->create() @@ -96,10 +99,7 @@ protected function setUp() */ protected function tearDown() { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** @var \Magento\Customer\Model\CustomerRegistry $customerRegistry */ - $customerRegistry = $objectManager->get(\Magento\Customer\Model\CustomerRegistry::class); - $customerRegistry->remove(1); + $this->customerRegistry->remove(1); } /** @@ -506,6 +506,60 @@ public function testSaveAddressWithRestrictedCountries() self::assertNotEmpty($saved->getId()); } + /** + * Test for saving address with extra spaces in phone. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + */ + public function testSaveNewAddressWithExtraSpacesInPhone() + { + $proposedAddress = $this->_createSecondAddress() + ->setCustomerId(1) + ->setTelephone(' 123456 '); + $returnedAddress = $this->repository->save($proposedAddress); + $savedAddress = $this->repository->getById($returnedAddress->getId()); + $this->assertEquals('123456', $savedAddress->getTelephone()); + } + + /** + * Scenario for customer's default shipping and billing address saving and rollback. + * + * @magentoDataFixture Magento/Customer/_files/customer_without_addresses.php + */ + public function testCustomerAddressRelationSynchronisation() + { + /** + * Creating new address which is default shipping and billing for existing customer. + */ + $address = $this->expectedAddresses[0]; + $address->setId(null); + $address->setCustomerId(1); + $address->setIsDefaultShipping(true); + $address->setIsDefaultBilling(true); + $savedAddress = $this->repository->save($address); + + /** + * Customer registry should be updated with default shipping and billing addresses. + */ + $customer = $this->getCustomer('customer@example.com', 1); + $this->assertEquals($savedAddress->getId(), $customer->getDefaultShipping()); + $this->assertEquals($savedAddress->getId(), $customer->getDefaultBilling()); + + /** + * Registry should be clean up for reading data from DB. + */ + $this->repository->deleteById($savedAddress->getId()); + $this->customerRegistry->removeByEmail('customer@example.com'); + + /** + * Customer's default shipping and billing addresses should be updated. + */ + $customer = $this->getCustomer('customer@example.com', 1); + $this->assertNull($customer->getDefaultShipping()); + $this->assertNull($customer->getDefaultBilling()); + } + /** * Helper function that returns an Address Data Object that matches the data from customer_address fixture * @@ -571,20 +625,4 @@ private function getWebsite(string $code): WebsiteInterface $repository = $this->objectManager->get(WebsiteRepositoryInterface::class); return $repository->get($code); } - - /** - * Test for saving address with extra spaces in phone. - * - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoDataFixture Magento/Customer/_files/customer_address.php - */ - public function testSaveNewAddressWithExtraSpacesInPhone() - { - $proposedAddress = $this->_createSecondAddress() - ->setCustomerId(1) - ->setTelephone(' 123456 '); - $returnedAddress = $this->repository->save($proposedAddress); - $savedAddress = $this->repository->getById($returnedAddress->getId()); - $this->assertEquals('123456', $savedAddress->getTelephone()); - } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_for_second_store.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_for_second_store.php index d8c56b7bf6f4f..79fd831dee27a 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_for_second_store.php +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_for_second_store.php @@ -7,11 +7,14 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Store\Api\StoreRepositoryInterface; require __DIR__ . '/customer.php'; $objectManager = Bootstrap::getObjectManager(); +$storeRepository = $objectManager->get(StoreRepositoryInterface::class); +$storeId = $storeRepository->get('fixture_second_store')->getId(); $repository = $objectManager->create(CustomerRepositoryInterface::class); $customer = $repository->get('customer@example.com'); -$customer->setStoreId(2); +$customer->setStoreId($storeId); $repository->save($customer); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_uk_address.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_uk_address.php new file mode 100644 index 0000000000000..a7ad0bb82719f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_uk_address.php @@ -0,0 +1,78 @@ +create(CustomerRepositoryInterface::class); +/** @var CustomerFactory $customerFactory */ +$customerFactory = $objectManager->get(CustomerFactory::class); +$customer = $customerFactory->create(); +/** @var CustomerRegistry $customerRegistry */ +$customerRegistry = $objectManager->get(CustomerRegistry::class); +/** @var WebsiteRepository $websiteRepository */ +$websiteRepository = $objectManager->create(WebsiteRepositoryInterface::class); +/** @var Website $mainWebsite */ +$mainWebsite = $websiteRepository->get('base'); +$customer->setWebsiteId($mainWebsite->getId()) + ->setEmail('customer_uk_address@test.com') + ->setPassword('password') + ->setGroupId(1) + ->setStoreId($mainWebsite->getDefaultStore()->getId()) + ->setIsActive(1) + ->setPrefix('Mr.') + ->setFirstname('John') + ->setMiddlename('A') + ->setLastname('Smith') + ->setSuffix('Esq.') + ->setTaxvat('12') + ->setGender(0); +/** @var AddressFactory $customerAddressFactory */ +$customerAddressFactory = $objectManager->get(AddressFactory::class); +/** @var AddressRepositoryInterface $customerAddressRepository */ +$customerAddressRepository = $objectManager->create(AddressRepositoryInterface::class); +/** @var Address $customerAddress */ +$customerAddress = $customerAddressFactory->create(); +$customerAddress->isObjectNew(true); +$customerAddress->setData( + [ + 'attribute_set_id' => AddressMetadataInterface::ATTRIBUTE_SET_ID_ADDRESS, + AddressInterface::TELEPHONE => 3468676, + AddressInterface::POSTCODE => 'EC1A 1AA', + AddressInterface::COUNTRY_ID => 'GB', + AddressInterface::CITY => 'London', + AddressInterface::COMPANY => 'CompanyName', + AddressInterface::STREET => 'test street address', + AddressInterface::LASTNAME => 'Smith', + AddressInterface::FIRSTNAME => 'John', + AddressInterface::REGION_ID => 1, + ] +); +$customer->addAddress($customerAddress); +$customer->isObjectNew(true); +$customerDataModel = $customerRepository->save($customer->getDataModel()); +$addressId = $customerDataModel->getAddresses()[0]->getId(); +$customerDataModel->setDefaultShipping($addressId); +$customerDataModel->setDefaultBilling($addressId); +$customerRepository->save($customerDataModel); +$customerRegistry->remove($customerDataModel->getId()); +/** @var AddressRegistry $addressRegistry */ +$addressRegistry = $objectManager->get(AddressRegistry::class); +$addressRegistry->remove($customerAddress->getId()); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_uk_address_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_uk_address_rollback.php new file mode 100644 index 0000000000000..e00d80833ea04 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_with_uk_address_rollback.php @@ -0,0 +1,30 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var CustomerRepositoryInterface $customerRepository */ +$customerRepository = $objectManager->get(CustomerRepositoryInterface::class); + +try { + $customer = $customerRepository->get('customer_uk_address@test.com'); + $customerRepository->delete($customer); +} catch (NoSuchEntityException $exception) { + //Already deleted +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers.php b/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers.php index 2f9c3dc31ef3d..9b989779e4cbd 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers.php +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers.php @@ -3,16 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -$customers = []; -$customer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Model\Customer::class -); +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\ObjectManagerInterface; +use Magento\Customer\Model\Customer; +use Magento\Framework\Registry; + +/** @var $objectManager ObjectManagerInterface */ +$objectManager = Bootstrap::getObjectManager(); + +$customers = []; +$customer = $objectManager->create(Customer::class); $customer->setWebsiteId( 1 -)->setEntityId( - 1 )->setEntityTypeId( 1 )->setAttributeSetId( @@ -40,13 +44,9 @@ $customer->save(); $customers[] = $customer; -$customer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Model\Customer::class -); +$customer = $objectManager->create(Customer::class); $customer->setWebsiteId( 1 -)->setEntityId( - 2 )->setEntityTypeId( 1 )->setAttributeSetId( @@ -74,13 +74,9 @@ $customer->save(); $customers[] = $customer; -$customer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Model\Customer::class -); +$customer = $objectManager->create(Customer::class); $customer->setWebsiteId( 1 -)->setEntityId( - 3 )->setEntityTypeId( 1 )->setAttributeSetId( @@ -108,9 +104,7 @@ $customer->save(); $customers[] = $customer; -/** @var $objectManager \Magento\TestFramework\ObjectManager */ -$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); -$objectManager->get(\Magento\Framework\Registry::class) +$objectManager->get(Registry::class) ->unregister('_fixture/Magento_ImportExport_Customer_Collection'); -$objectManager->get(\Magento\Framework\Registry::class) +$objectManager->get(Registry::class) ->register('_fixture/Magento_ImportExport_Customer_Collection', $customers); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers_rollback.php new file mode 100644 index 0000000000000..f8eeb8edd15da --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/import_export/customers_rollback.php @@ -0,0 +1,37 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var $customer Customer */ +$customer = $objectManager->create(Customer::class); + +$emailsToDelete = [ + 'customer@example.com', + 'julie.worrell@example.com', + 'david.lamar@example.com', +]; +foreach ($emailsToDelete as $email) { + try { + $customer->loadByEmail($email)->delete(); + } catch (\Exception $e) { + } +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); +$registry->unregister('_fixture/Magento_ImportExport_Customer_Collection'); diff --git a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php index 88b748f8bbbae..884a4a38ebe0f 100644 --- a/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php +++ b/dev/tests/integration/testsuite/Magento/CustomerImportExport/Model/Export/CustomerTest.php @@ -4,80 +4,254 @@ * See COPYING.txt for license details. */ -/** - * Test for customer export model - */ namespace Magento\CustomerImportExport\Model\Export; +use Magento\Framework\Registry; +use Magento\Customer\Model\Attribute; +use Magento\ImportExport\Model\Export; +use Magento\ImportExport\Model\Import; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\ImportExport\Model\Export\Adapter\Csv; +use Magento\Customer\Model\Customer as CustomerModel; +use Magento\CustomerImportExport\Model\Export\Customer; +use Magento\Customer\Model\ResourceModel\Attribute\Collection; +use Magento\Customer\Model\ResourceModel\Customer\Collection as CustomerCollection; + +/** + * Tests for customer export model. + * + * @magentoAppArea adminhtml + */ class CustomerTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\CustomerImportExport\Model\Export\Customer + * @var Customer */ protected $_model; + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var array + */ + private $attributeValues; + + /** + * @var array + */ + private $attributeTypes; + + /** + * @var Collection + */ + private $attributeCollection; + + /** + * @inheritdoc + */ protected function setUp() { - $this->_model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\CustomerImportExport\Model\Export\Customer::class - ); + $this->objectManager = Bootstrap::getObjectManager(); + $this->_model = $this->objectManager->create(Customer::class); + $this->attributeCollection = $this->objectManager->create(Collection::class); } /** - * Test export method + * Export "Customer Main File". * * @magentoDataFixture Magento/Customer/_files/import_export/customers.php + * @return void */ public function testExport() + { + $this->processCustomerAttribute(); + $expectedAttributes = $this->getExpectedAttributes(); + $lines = $this->export($expectedAttributes); + $this->checkExportData($lines, $expectedAttributes); + } + + /** + * Return attributes which should be exported. + * + * @return array + */ + private function getExpectedAttributes(): array { $expectedAttributes = []; - /** @var $collection \Magento\Customer\Model\ResourceModel\Attribute\Collection */ - $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Model\ResourceModel\Attribute\Collection::class - ); - /** @var $attribute \Magento\Customer\Model\Attribute */ - foreach ($collection as $attribute) { + /** @var Attribute $attribute */ + foreach ($this->attributeCollection as $attribute) { $expectedAttributes[] = $attribute->getAttributeCode(); } - $expectedAttributes = array_diff($expectedAttributes, $this->_model->getDisabledAttributes()); - $this->_model->setWriter( - \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\ImportExport\Model\Export\Adapter\Csv::class - ) - ); + return array_diff($expectedAttributes, $this->_model->getDisabledAttributes()); + } + + /** + * Prepare Customer attribute. + * + * @return void + */ + private function processCustomerAttribute(): void + { + $this->initAttributeValues($this->attributeCollection); + $this->initAttributeTypes($this->attributeCollection); + } + + /** + * Export customer. + * + * @param array $expectedAttributes + * @return array + */ + private function export(array $expectedAttributes): array + { + $this->_model->setWriter($this->objectManager->create(Csv::class)); $data = $this->_model->export(); + $this->assertNotEmpty($data); $lines = $this->_csvToArray($data, 'email'); - $this->assertEquals( count($expectedAttributes), count(array_intersect($expectedAttributes, $lines['header'])), - 'Expected attribute codes were not exported' + 'Expected attribute codes were not exported.' ); - $this->assertNotEmpty($lines['data'], 'No data was exported'); + $this->assertNotEmpty($lines['data'], 'No data was exported.'); - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** @var $customers \Magento\Customer\Model\Customer[] */ - $customers = $objectManager->get( - \Magento\Framework\Registry::class - )->registry( - '_fixture/Magento_ImportExport_Customer_Collection' - ); - foreach ($customers as $key => $customer) { - foreach ($expectedAttributes as $code) { - if (!in_array($code, $this->_model->getDisabledAttributes()) && isset($lines[$key][$code])) { - $this->assertEquals( - $customer->getData($code), - $lines[$key][$code], - 'Attribute "' . $code . '" is not equal' - ); + return $lines; + } + + /** + * Check that exported data is correct. + * + * @param array $lines + * @param array $expectedAttributes + * @return void + */ + private function checkExportData(array $lines, array $expectedAttributes): void + { + /** @var CustomerModel[] $customers */ + $customers = $this->objectManager->create(CustomerCollection::class); + foreach ($customers as $customer) { + $data = $this->processCustomerData($customer, $expectedAttributes); + $exportData = $lines['data'][$data['email']]; + $exportData = $this->unsetDuplicateData($exportData); + + foreach ($data as $key => $value) { + $this->assertEquals($value, $exportData[$key], "Attribute '{$key}' is not equal."); + } + } + } + + /** + * Initialize attribute option values. + * + * @param Collection $attributeCollection + * @return CustomerTest + */ + private function initAttributeValues(Collection $attributeCollection): CustomerTest + { + /** @var Attribute $attribute */ + foreach ($attributeCollection as $attribute) { + $this->attributeValues[$attribute->getAttributeCode()] = $this->_model->getAttributeOptions($attribute); + } + + return $this; + } + + /** + * Initialize attribute types. + * + * @param \Magento\Customer\Model\ResourceModel\Attribute\Collection $attributeCollection + * @return CustomerTest + */ + private function initAttributeTypes(Collection $attributeCollection): CustomerTest + { + /** @var Attribute $attribute */ + foreach ($attributeCollection as $attribute) { + $this->attributeTypes[$attribute->getAttributeCode()] = $attribute->getFrontendInput(); + } + + return $this; + } + + /** + * Format Customer data as same as export data. + * + * @param CustomerModel $item + * @param array $expectedAttributes + * @return array + */ + private function processCustomerData(CustomerModel $item, array $expectedAttributes): array + { + $data = []; + foreach ($expectedAttributes as $attributeCode) { + $attributeValue = $item->getData($attributeCode); + + if ($this->isMultiselect($attributeCode)) { + $values = []; + $attributeValue = explode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $attributeValue); + foreach ($attributeValue as $value) { + $values[] = $this->getAttributeValueById($attributeCode, $value); } + $data[$attributeCode] = implode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $values); + } else { + $data[$attributeCode] = $this->getAttributeValueById($attributeCode, $attributeValue); } } + + return $data; + } + + /** + * Check that attribute is multiselect type by attribute code. + * + * @param string $attributeCode + * @return bool + */ + private function isMultiselect(string $attributeCode): bool + { + return isset($this->attributeTypes[$attributeCode]) + && $this->attributeTypes[$attributeCode] === 'multiselect'; + } + + /** + * Return attribute value by id. + * + * @param string $attributeCode + * @param int|string $valueId + * @return int|string|array + */ + private function getAttributeValueById(string $attributeCode, $valueId) + { + if (isset($this->attributeValues[$attributeCode]) + && isset($this->attributeValues[$attributeCode][$valueId]) + ) { + return $this->attributeValues[$attributeCode][$valueId]; + } + + return $valueId; + } + + /** + * Unset non-useful or duplicate data from exported file data. + * + * @param array $data + * @return array + */ + private function unsetDuplicateData(array $data): array + { + unset($data['_website']); + unset($data['_store']); + unset($data['password']); + + return $data; } /** @@ -93,10 +267,7 @@ public function testGetEntityTypeCode() */ public function testGetAttributeCollection() { - $this->assertInstanceOf( - \Magento\Customer\Model\ResourceModel\Attribute\Collection::class, - $this->_model->getAttributeCollection() - ); + $this->assertInstanceOf(Collection::class, $this->_model->getAttributeCollection()); } /** @@ -104,14 +275,14 @@ public function testGetAttributeCollection() */ public function testFilterAttributeCollection() { - /** @var $collection \Magento\Customer\Model\ResourceModel\Attribute\Collection */ + /** @var $collection Collection */ $collection = $this->_model->getAttributeCollection(); $collection = $this->_model->filterAttributeCollection($collection); /** * Check that disabled attributes is not existed in attribute collection */ $existedAttributes = []; - /** @var $attribute \Magento\Customer\Model\Attribute */ + /** @var $attribute Attribute */ foreach ($collection as $attribute) { $existedAttributes[] = $attribute->getAttributeCode(); } @@ -127,7 +298,7 @@ public function testFilterAttributeCollection() * Check that all overridden attributes were affected during filtering process */ $overriddenAttributes = $this->_model->getOverriddenAttributes(); - /** @var $attribute \Magento\Customer\Model\Attribute */ + /** @var $attribute Attribute */ foreach ($collection as $attribute) { if (isset($overriddenAttributes[$attribute->getAttributeCode()])) { foreach ($overriddenAttributes[$attribute->getAttributeCode()] as $propertyKey => $property) { @@ -149,28 +320,19 @@ public function testFilterAttributeCollection() public function testFilterEntityCollection() { $createdAtDate = '2038-01-01'; - - /** @var $objectManager \Magento\TestFramework\ObjectManager */ - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - /** * Change created_at date of first customer for future filter test. */ - $customers = $objectManager->get( - \Magento\Framework\Registry::class - )->registry( - '_fixture/Magento_ImportExport_Customer_Collection' - ); + $customers = $this->objectManager->get(Registry::class) + ->registry('_fixture/Magento_ImportExport_Customer_Collection'); $customers[0]->setCreatedAt($createdAtDate); $customers[0]->save(); /** * Change type of created_at attribute. In this case we have possibility to test date rage filter */ - $attributeCollection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Model\ResourceModel\Attribute\Collection::class - ); + $attributeCollection = $this->objectManager->create(Collection::class); $attributeCollection->addFieldToFilter('attribute_code', 'created_at'); - /** @var $createdAtAttribute \Magento\Customer\Model\Attribute */ + /** @var $createdAtAttribute Attribute */ $createdAtAttribute = $attributeCollection->getFirstItem(); $createdAtAttribute->setBackendType('datetime'); $createdAtAttribute->save(); @@ -178,19 +340,17 @@ public function testFilterEntityCollection() * Prepare filter.asd */ $parameters = [ - \Magento\ImportExport\Model\Export::FILTER_ELEMENT_GROUP => [ + Export::FILTER_ELEMENT_GROUP => [ 'email' => 'example.com', 'created_at' => [$createdAtDate, ''], - 'store_id' => \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Store\Model\StoreManagerInterface::class - )->getStore()->getId() + 'store_id' => $this->objectManager->get(StoreManagerInterface::class)->getStore()->getId() ] ]; $this->_model->setParameters($parameters); - /** @var $customers \Magento\Customer\Model\ResourceModel\Customer\Collection */ + /** @var $customers Collection */ $collection = $this->_model->filterEntityCollection( - \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Customer\Model\ResourceModel\Customer\Collection::class + $this->objectManager->create( + CustomerCollection::class ) ); $collection->load(); @@ -223,6 +383,7 @@ protected function _csvToArray($content, $entityId = null) } } } + return $data; } } diff --git a/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom1/registration.php b/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom1/registration.php index 52d41c2a2ebb3..21c05427ed6f5 100644 --- a/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom1/registration.php +++ b/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom1/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Magento/zoom1', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom2/registration.php b/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom2/registration.php index a6979a1cae451..b40bf194aee19 100644 --- a/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom2/registration.php +++ b/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom2/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Magento/zoom2', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom3/registration.php b/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom3/registration.php index fc0867f7d0107..bd6adc89add51 100644 --- a/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom3/registration.php +++ b/dev/tests/integration/testsuite/Magento/Deploy/_files/zoom3/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Magento/zoom3', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php b/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php index 69eab0656f89b..a6a293d384d56 100644 --- a/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php +++ b/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php @@ -15,12 +15,15 @@ use Magento\Quote\Model\Quote\Address\RateRequest; use Magento\Shipping\Model\Shipment\Request; use Magento\Shipping\Model\Tracking\Result\Status; +use Magento\Store\Model\ScopeInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\HTTP\AsyncClientInterfaceMock; use Magento\Shipping\Model\Simplexml\Element as ShippingElement; /** * Test for DHL integration. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CarrierTest extends \PHPUnit\Framework\TestCase { @@ -411,7 +414,108 @@ private function getExpectedLabelRequestXml( */ public function testCollectRates() { - $requestData = [ + $requestData = $this->getRequestData(); + //phpcs:disable Magento2.Functions.DiscouragedFunction + $response = new Response( + 200, + [], + file_get_contents(__DIR__ . '/../_files/dhl_quote_response.xml') + ); + //phpcs:enable Magento2.Functions.DiscouragedFunction + $this->httpClient->nextResponses(array_fill(0, Carrier::UNAVAILABLE_DATE_LOOK_FORWARD + 1, $response)); + /** @var RateRequest $request */ + $request = Bootstrap::getObjectManager()->create(RateRequest::class, $requestData); + $expectedRates = [ + ['carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 45.85, 'method' => 'E', 'price' => 45.85], + ['carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 35.26, 'method' => 'Q', 'price' => 35.26], + ['carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 37.38, 'method' => 'Y', 'price' => 37.38], + ['carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 35.26, 'method' => 'P', 'price' => 35.26] + ]; + + $actualRates = $this->dhlCarrier->collectRates($request)->getAllRates(); + + self::assertEquals(count($expectedRates), count($actualRates)); + foreach ($actualRates as $i => $actualRate) { + $actualRate = $actualRate->getData(); + unset($actualRate['method_title']); + self::assertEquals($expectedRates[$i], $actualRate); + } + $requestXml = $this->httpClient->getLastRequest()->getBody(); + self::assertContains('18.223', $requestXml); + self::assertContains('0.63', $requestXml); + self::assertContains('0.63', $requestXml); + self::assertContains('0.63', $requestXml); + } + + /** + * Tests that quotes request doesn't contain dimensions when it shouldn't. + * + * @param string|null $size + * @param string|null $height + * @param string|null $width + * @param string|null $depth + * @magentoConfigFixture default_store carriers/dhl/active 1 + * @dataProvider collectRatesWithoutDimensionsDataProvider + */ + public function testCollectRatesWithoutDimensions(?string $size, ?string $height, ?string $width, ?string $depth) + { + $requestData = $this->getRequestData(); + $this->setDhlConfig(['size' => $size, 'height' => $height, 'width' => $width, 'depth' => $depth]); + + /** @var RateRequest $request */ + $request = Bootstrap::getObjectManager()->create(RateRequest::class, $requestData); + $this->dhlCarrier = Bootstrap::getObjectManager()->create(Carrier::class); + $this->dhlCarrier->collectRates($request)->getAllRates(); + + $requestXml = $this->httpClient->getLastRequest()->getBody(); + $this->assertNotContains('', $requestXml); + $this->assertNotContains('', $requestXml); + $this->assertNotContains('', $requestXml); + + $this->config->reinit(); + } + + /** + * @return array + */ + public function collectRatesWithoutDimensionsDataProvider() + { + return [ + ['size' => '0', 'height' => '1.1', 'width' => '0.6', 'depth' => '0.7'], + ['size' => '1', 'height' => '', 'width' => '', 'depth' => ''], + ['size' => null, 'height' => '1.1', 'width' => '0.6', 'depth' => '0.7'], + ['size' => '1', 'height' => '1', 'width' => '', 'depth' => ''], + ['size' => null, 'height' => null, 'width' => null, 'depth' => null], + ]; + } + + /** + * Sets DHL config value. + * + * @param array $params + * @return void + */ + private function setDhlConfig(array $params) + { + foreach ($params as $name => $val) { + if ($val !== null) { + $this->config->setValue( + 'carriers/dhl/' . $name, + $val, + ScopeInterface::SCOPE_STORE + ); + } + } + } + + /** + * Returns request data. + * + * @return array + */ + private function getRequestData(): array + { + return [ 'data' => [ 'dest_country_id' => 'DE', 'dest_region_id' => '82', @@ -454,35 +558,5 @@ public function testCollectRates() 'all_items' => [], ] ]; - //phpcs:disable Magento2.Functions.DiscouragedFunction - $response = new Response( - 200, - [], - file_get_contents(__DIR__ . '/../_files/dhl_quote_response.xml') - ); - //phpcs:enable Magento2.Functions.DiscouragedFunction - $this->httpClient->nextResponses(array_fill(0, Carrier::UNAVAILABLE_DATE_LOOK_FORWARD + 1, $response)); - /** @var RateRequest $request */ - $request = Bootstrap::getObjectManager()->create(RateRequest::class, $requestData); - $expectedRates = [ - ['carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 45.85, 'method' => 'E', 'price' => 45.85], - ['carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 35.26, 'method' => 'Q', 'price' => 35.26], - ['carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 37.38, 'method' => 'Y', 'price' => 37.38], - ['carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 35.26, 'method' => 'P', 'price' => 35.26] - ]; - - $actualRates = $this->dhlCarrier->collectRates($request)->getAllRates(); - - self::assertEquals(count($expectedRates), count($actualRates)); - foreach ($actualRates as $i => $actualRate) { - $actualRate = $actualRate->getData(); - unset($actualRate['method_title']); - self::assertEquals($expectedRates[$i], $actualRate); - } - $requestXml = $this->httpClient->getLastRequest()->getBody(); - self::assertContains('18.223', $requestXml); - self::assertContains('0.630', $requestXml); - self::assertContains('0.630', $requestXml); - self::assertContains('0.630', $requestXml); } } diff --git a/dev/tests/integration/testsuite/Magento/Directory/Controller/Adminhtml/Json/CountryRegionTest.php b/dev/tests/integration/testsuite/Magento/Directory/Controller/Adminhtml/Json/CountryRegionTest.php new file mode 100644 index 0000000000000..8d3b12aa34f1e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Directory/Controller/Adminhtml/Json/CountryRegionTest.php @@ -0,0 +1,50 @@ +getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue([]); + $this->dispatch('backend/directory/json/countryRegion'); + + $actual = $this->getResponse()->getBody(); + + $this->assertEquals('[]', $actual); + } + + /** + * Test Execute with region in the fixture + * + * @magentoDataFixture Magento/Directory/_files/example_region_in_country.php + */ + public function testExecute() + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue([ + 'parent' => 'WW' + ]); + $this->dispatch('backend/directory/json/countryRegion'); + + $actual = $this->getResponse()->getBody(); + + $this->assertContains('Example Region 1', $actual); + $this->assertContains('Example Region 2', $actual); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Directory/_files/example_region_in_country.php b/dev/tests/integration/testsuite/Magento/Directory/_files/example_region_in_country.php new file mode 100644 index 0000000000000..ba33b09a1f948 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Directory/_files/example_region_in_country.php @@ -0,0 +1,38 @@ + 'WW', + 'code' => 'ER1', + 'default_name' => 'Example Region 1' + ], + [ + 'country_id' => 'WW', + 'code' => 'ER2', + 'default_name' => 'Example Region 2' + ] +]; + +/** @var RegionModel $region */ +$region = $objectManager->create(RegionModel::class); +/** @var RegionResource $regionResource */ +$regionResource = $objectManager->get(RegionResource::class); + +foreach ($regionData as $data) { + /** @var RegionModel $region */ + $region = $objectManager->create(RegionModel::class); + $region->setData($data); + $regionResource->save($region); +} diff --git a/dev/tests/integration/testsuite/Magento/Directory/_files/example_region_in_country_rollback.php b/dev/tests/integration/testsuite/Magento/Directory/_files/example_region_in_country_rollback.php new file mode 100644 index 0000000000000..4f78873aa380f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Directory/_files/example_region_in_country_rollback.php @@ -0,0 +1,26 @@ +get(RegionResource::class); + +$regionCollection = $objectManager->create(RegionResourceCollection::class) + ->addFieldToFilter('code', ['in' => $regionCode]); + +/** @var RegionModel $region */ +foreach ($regionCollection as $region) { + $regionResource->delete($region); +} diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/DateTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/DateTest.php new file mode 100644 index 0000000000000..cb75d3e0d4a8e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/DateTest.php @@ -0,0 +1,46 @@ +createAttributeUsingDataAndAssert($attributePostData, $checkArray); + } + + /** + * Test create attribute with error. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\Date::getAttributeDataWithErrorMessage() + * + * @param array $attributePostData + * @param string $errorMessage + * @return void + */ + public function testCreateAttributeWithError(array $attributePostData, string $errorMessage): void + { + $this->createAttributeUsingDataWithErrorAndAssert($attributePostData, $errorMessage); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/DropDownTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/DropDownTest.php new file mode 100644 index 0000000000000..1a3f363832d6e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/DropDownTest.php @@ -0,0 +1,46 @@ +createAttributeUsingDataAndAssert($attributePostData, $checkArray); + } + + /** + * Test create attribute with error. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\DropDown::getAttributeDataWithErrorMessage() + * + * @param array $attributePostData + * @param string $errorMessage + * @return void + */ + public function testCreateAttributeWithError(array $attributePostData, string $errorMessage): void + { + $this->createAttributeUsingDataWithErrorAndAssert($attributePostData, $errorMessage); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/MultipleSelectTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/MultipleSelectTest.php new file mode 100644 index 0000000000000..1c0f5ea720f70 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/MultipleSelectTest.php @@ -0,0 +1,46 @@ +createAttributeUsingDataAndAssert($attributePostData, $checkArray); + } + + /** + * Test create attribute with error. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\MultipleSelect::getAttributeDataWithErrorMessage() + * + * @param array $attributePostData + * @param string $errorMessage + * @return void + */ + public function testCreateAttributeWithError(array $attributePostData, string $errorMessage): void + { + $this->createAttributeUsingDataWithErrorAndAssert($attributePostData, $errorMessage); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/TextAreaTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/TextAreaTest.php new file mode 100644 index 0000000000000..9c5b1a8587674 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/TextAreaTest.php @@ -0,0 +1,46 @@ +createAttributeUsingDataAndAssert($attributePostData, $checkArray); + } + + /** + * Test create attribute with error. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\TextArea::getAttributeDataWithErrorMessage() + * + * @param array $attributePostData + * @param string $errorMessage + * @return void + */ + public function testCreateAttributeWithError(array $attributePostData, string $errorMessage): void + { + $this->createAttributeUsingDataWithErrorAndAssert($attributePostData, $errorMessage); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/TextEditorTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/TextEditorTest.php new file mode 100644 index 0000000000000..807e0cfd570b2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/TextEditorTest.php @@ -0,0 +1,46 @@ +createAttributeUsingDataAndAssert($attributePostData, $checkArray); + } + + /** + * Test create attribute with error. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\TextEditor::getAttributeDataWithErrorMessage() + * + * @param array $attributePostData + * @param string $errorMessage + * @return void + */ + public function testCreateAttributeWithError(array $attributePostData, string $errorMessage): void + { + $this->createAttributeUsingDataWithErrorAndAssert($attributePostData, $errorMessage); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/TextTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/TextTest.php new file mode 100644 index 0000000000000..70069dcedd0e4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/TextTest.php @@ -0,0 +1,46 @@ +createAttributeUsingDataAndAssert($attributePostData, $checkArray); + } + + /** + * Test create attribute with error. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\Text::getAttributeDataWithErrorMessage() + * + * @param array $attributePostData + * @param string $errorMessage + * @return void + */ + public function testCreateAttributeWithError(array $attributePostData, string $errorMessage): void + { + $this->createAttributeUsingDataWithErrorAndAssert($attributePostData, $errorMessage); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/YesNoTest.php b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/YesNoTest.php new file mode 100644 index 0000000000000..7bb26556c3fd6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/Controller/Adminhtml/Product/Attribute/Save/InputType/YesNoTest.php @@ -0,0 +1,46 @@ +createAttributeUsingDataAndAssert($attributePostData, $checkArray); + } + + /** + * Test create attribute with error. + * + * @dataProvider \Magento\TestFramework\Eav\Model\Attribute\DataProvider\YesNo::getAttributeDataWithErrorMessage() + * + * @param array $attributePostData + * @param string $errorMessage + * @return void + */ + public function testCreateAttributeWithError(array $attributePostData, string $errorMessage): void + { + $this->createAttributeUsingDataWithErrorAndAssert($attributePostData, $errorMessage); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Eav/Model/ConfigTest.php b/dev/tests/integration/testsuite/Magento/Eav/Model/ConfigTest.php index c333098410800..f3d6247fa37ff 100644 --- a/dev/tests/integration/testsuite/Magento/Eav/Model/ConfigTest.php +++ b/dev/tests/integration/testsuite/Magento/Eav/Model/ConfigTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Eav\Model; +use Magento\Framework\App\Config\MutableScopeConfigInterface; use Magento\Framework\DataObject; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Helper\CacheCleaner; @@ -12,7 +13,6 @@ /** * @magentoAppIsolation enabled * @magentoDbIsolation enabled - * @magentoDataFixture Magento/Eav/_files/attribute_for_search.php */ class ConfigTest extends \PHPUnit\Framework\TestCase { @@ -27,6 +27,9 @@ protected function setUp() $this->config = $objectManager->get(Config::class); } + /** + * @magentoDataFixture Magento/Eav/_files/attribute_for_search.php + */ public function testGetEntityAttributeCodes() { $entityType = 'test'; @@ -47,6 +50,9 @@ public function testGetEntityAttributeCodes() $this->assertEquals($entityAttributeCodes1, $entityAttributeCodes2); } + /** + * @magentoDataFixture Magento/Eav/_files/attribute_for_search.php + */ public function testGetEntityAttributeCodesWithObject() { $entityType = 'test'; @@ -74,6 +80,9 @@ public function testGetEntityAttributeCodesWithObject() $this->assertEquals($entityAttributeCodes1, $entityAttributeCodes2); } + /** + * @magentoDataFixture Magento/Eav/_files/attribute_for_search.php + */ public function testGetAttributes() { $entityType = 'test'; @@ -96,6 +105,9 @@ public function testGetAttributes() $this->assertEquals($attributes1, $attributes2); } + /** + * @magentoDataFixture Magento/Eav/_files/attribute_for_search.php + */ public function testGetAttribute() { $entityType = 'test'; @@ -109,4 +121,77 @@ public function testGetAttribute() $attribute2 = $this->config->getAttribute($entityType, 'attribute_for_search_1'); $this->assertEquals($attribute1, $attribute2); } + + /** + * @magentoDataFixture Magento/Eav/_files/attribute_for_caching.php + */ + public function testGetAttributeWithCacheUserDefinedAttribute() + { + /** @var MutableScopeConfigInterface $mutableScopeConfig */ + $mutableScopeConfig = Bootstrap::getObjectManager()->get(MutableScopeConfigInterface::class); + $mutableScopeConfig->setValue('dev/caching/cache_user_defined_attributes', 1); + $entityType = 'catalog_product'; + $attribute = $this->config->getAttribute($entityType, 'foo'); + $this->assertEquals('foo', $attribute->getAttributeCode()); + $this->assertEquals('foo', $attribute->getFrontendLabel()); + $this->assertEquals('varchar', $attribute->getBackendType()); + $this->assertEquals(1, $attribute->getIsRequired()); + $this->assertEquals(1, $attribute->getIsUserDefined()); + $this->assertEquals(0, $attribute->getIsUnique()); + // Update attribute + $eavSetupFactory = Bootstrap::getObjectManager()->create(\Magento\Eav\Setup\EavSetupFactory::class); + /** @var \Magento\Eav\Setup\EavSetup $eavSetup */ + $eavSetup = $eavSetupFactory->create(); + $eavSetup->updateAttribute( + \Magento\Catalog\Model\Product::ENTITY, + 'foo', + [ + 'frontend_label' => 'bar', + ] + ); + // Check that attribute data has not changed + $config = Bootstrap::getObjectManager()->create(\Magento\Eav\Model\Config::class); + $updatedAttribute = $config->getAttribute($entityType, 'foo'); + $this->assertEquals('foo', $updatedAttribute->getFrontendLabel()); + // Clean cache + CacheCleaner::cleanAll(); + $config = Bootstrap::getObjectManager()->create(\Magento\Eav\Model\Config::class); + // Check that attribute data has changed + $updatedAttributeAfterCacheClean = $config->getAttribute($entityType, 'foo'); + $this->assertEquals('bar', $updatedAttributeAfterCacheClean->getFrontendLabel()); + $mutableScopeConfig->setValue('dev/caching/cache_user_defined_attributes', 0); + } + + /** + * @magentoDataFixture Magento/Eav/_files/attribute_for_caching.php + */ + public function testGetAttributeWithInitUserDefinedAttribute() + { + /** @var MutableScopeConfigInterface $mutableScopeConfig */ + $mutableScopeConfig = Bootstrap::getObjectManager()->get(MutableScopeConfigInterface::class); + $mutableScopeConfig->setValue('dev/caching/cache_user_defined_attributes', 0); + $entityType = 'catalog_product'; + $attribute = $this->config->getAttribute($entityType, 'foo'); + $this->assertEquals('foo', $attribute->getAttributeCode()); + $this->assertEquals('foo', $attribute->getFrontendLabel()); + $this->assertEquals('varchar', $attribute->getBackendType()); + $this->assertEquals(1, $attribute->getIsRequired()); + $this->assertEquals(1, $attribute->getIsUserDefined()); + $this->assertEquals(0, $attribute->getIsUnique()); + // Update attribute + $eavSetupFactory = Bootstrap::getObjectManager()->create(\Magento\Eav\Setup\EavSetupFactory::class); + /** @var \Magento\Eav\Setup\EavSetup $eavSetup */ + $eavSetup = $eavSetupFactory->create(); + $eavSetup->updateAttribute( + \Magento\Catalog\Model\Product::ENTITY, + 'foo', + [ + 'frontend_label' => 'bar', + ] + ); + // Check that attribute data has changed + $config = Bootstrap::getObjectManager()->create(\Magento\Eav\Model\Config::class); + $updatedAttributeAfterCacheClean = $config->getAttribute($entityType, 'foo'); + $this->assertEquals('bar', $updatedAttributeAfterCacheClean->getFrontendLabel()); + } } diff --git a/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php index 2750e2a768aab..5b05308c786b5 100644 --- a/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeTest.php @@ -3,15 +3,19 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); namespace Magento\Eav\Model\Entity; -use Magento\TestFramework\Helper\Bootstrap; use Magento\Framework\Locale\ResolverInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; -class AttributeTest extends \PHPUnit\Framework\TestCase +/** + * Class to test EAV Entity attribute model + */ +class AttributeTest extends TestCase { /** * @var Attribute @@ -19,33 +23,33 @@ class AttributeTest extends \PHPUnit\Framework\TestCase private $attribute; /** - * @var \Magento\Framework\ObjectManagerInterface + * @var ObjectManagerInterface */ private $objectManager; /** * @var ResolverInterface */ - private $_localeResolver; + private $localeResolver; /** - * {@inheritdoc} + * @inheritdoc */ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); $this->attribute = $this->objectManager->get(Attribute::class); - $this->_localeResolver = $this->objectManager->get(ResolverInterface::class); + $this->localeResolver = $this->objectManager->get(ResolverInterface::class); } /** - * {@inheritdoc} + * @inheritdoc */ protected function tearDown() { $this->attribute = null; $this->objectManager = null; - $this->_localeResolver = null; + $this->localeResolver = null; } /** @@ -56,11 +60,17 @@ protected function tearDown() * @dataProvider beforeSaveDataProvider * @throws */ - public function testBeforeSave($defaultValue, $backendType, $locale, $expected) - { + public function testBeforeSave( + string $defaultValue, + string $backendType, + string $frontendInput, + string $locale, + string $expected + ) { $this->attribute->setDefaultValue($defaultValue); $this->attribute->setBackendType($backendType); - $this->_localeResolver->setLocale($locale); + $this->attribute->setFrontendInput($frontendInput); + $this->localeResolver->setLocale($locale); $this->attribute->beforeSave(); $this->assertEquals($expected, $this->attribute->getDefaultValue()); @@ -74,13 +84,15 @@ public function testBeforeSave($defaultValue, $backendType, $locale, $expected) public function beforeSaveDataProvider() { return [ - ['21/07/18', 'datetime', 'en_AU', '2018-07-21 00:00:00'], - ['07/21/18', 'datetime', 'en_US', '2018-07-21 00:00:00'], - ['21/07/18', 'datetime', 'fr_FR', '2018-07-21 00:00:00'], - ['21/07/18', 'datetime', 'de_DE', '2018-07-21 00:00:00'], - ['21/07/18', 'datetime', 'uk_UA', '2018-07-21 00:00:00'], - ['100.50', 'decimal', 'en_US', '100.50'], - ['100,50', 'decimal', 'uk_UA', '100.5'], + ['21/07/18', 'datetime', 'date', 'en_AU', '2018-07-21 00:00:00'], + ['07/21/18', 'datetime', 'date', 'en_US', '2018-07-21 00:00:00'], + ['21/07/18', 'datetime', 'date', 'fr_FR', '2018-07-21 00:00:00'], + ['21/07/18', 'datetime', 'date', 'de_DE', '2018-07-21 00:00:00'], + ['21/07/18', 'datetime', 'date', 'uk_UA', '2018-07-21 00:00:00'], + ['100.50', 'decimal', 'decimal', 'en_US', '100.50'], + ['100,50', 'decimal', 'decimal', 'uk_UA', '100.5'], + ['07/21/2019 2:30 PM', 'datetime', 'datetime', 'en_US', '2019-07-21 21:30:00'], + ['21.07.2019 14:30', 'datetime', 'datetime', 'uk_UA', '2019-07-21 21:30:00'], ]; } @@ -96,7 +108,7 @@ public function testBeforeSaveErrorData($defaultValue, $backendType, $locale, $e { $this->attribute->setDefaultValue($defaultValue); $this->attribute->setBackendType($backendType); - $this->_localeResolver->setLocale($locale); + $this->localeResolver->setLocale($locale); $this->attribute->beforeSave(); $this->expectExceptionMessage($expected); diff --git a/dev/tests/integration/testsuite/Magento/Eav/_files/attribute_for_caching.php b/dev/tests/integration/testsuite/Magento/Eav/_files/attribute_for_caching.php new file mode 100644 index 0000000000000..10e8109f3f31f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/_files/attribute_for_caching.php @@ -0,0 +1,45 @@ +create(\Magento\Eav\Model\Entity\Type::class) + ->loadByCode('catalog_product'); +$data = $entityType->getData(); +$entityTypeId = $entityType->getId(); + +/** @var \Magento\Eav\Model\Entity\Attribute\Set $attributeSet */ +$attributeSet = $objectManager->create(\Magento\Eav\Model\Entity\Attribute\Set::class); +$attributeSet->setData( + [ + 'attribute_set_name' => 'test_attribute_set', + 'entity_type_id' => $entityTypeId, + 'sort_order' => 100 + ] +); +$attributeSet->validate(); +$attributeSet->save(); + +$attributeData = [ + [ + 'attribute_code' => 'foo', + 'entity_type_id' => $entityTypeId, + 'backend_type' => 'varchar', + 'is_required' => 1, + 'is_user_defined' => 1, + 'is_unique' => 0, + 'frontend_label' => ['foo'], + 'attribute_set_id' => $entityType->getDefaultAttributeSetId() + ] +]; + +foreach ($attributeData as $data) { + /** @var \Magento\Eav\Model\Entity\Attribute $attribute */ + $attribute = $objectManager->create(\Magento\Eav\Model\Entity\Attribute::class); + $attribute->setData($data); + $attribute->save(); +} diff --git a/dev/tests/integration/testsuite/Magento/Eav/_files/attribute_for_caching_rollback.php b/dev/tests/integration/testsuite/Magento/Eav/_files/attribute_for_caching_rollback.php new file mode 100644 index 0000000000000..ebc3d58028e37 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/_files/attribute_for_caching_rollback.php @@ -0,0 +1,31 @@ +get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var Attribute $attribute */ +$attribute = $objectManager->create(Attribute::class); +$attribute->loadByCode(4, 'foo'); + +if ($attribute->getId()) { + $attribute->delete(); +} + +/** @var Set $attributeSet */ +$attributeSet = $objectManager->create(Set::class)->load('test_attribute_set', 'attribute_set_name'); +if ($attributeSet->getId()) { + $attributeSet->delete(); +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Eav/_files/product_texteditor_attribute.php b/dev/tests/integration/testsuite/Magento/Eav/_files/product_texteditor_attribute.php new file mode 100644 index 0000000000000..1ac4a35e8c6a5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/_files/product_texteditor_attribute.php @@ -0,0 +1,51 @@ +get(AttributeFactory::class); +/** @var ProductAttributeRepositoryInterface $productAttributeRepository */ +$productAttributeRepository = $objectManager->get(ProductAttributeRepositoryInterface::class); +$attribute = $attributeFactory->create()->loadByCode(Product::ENTITY, 'text_editor_attribute'); +if (!$attribute->getId()) { + /** @var Presentation $presentation */ + $presentation = $objectManager->get(Presentation::class); + /** @var EavSetup $installer */ + $installer = $objectManager->create(EavSetup::class); + $attributeData = [ + 'attribute_code' => 'text_editor_attribute', + 'is_global' => 1, + 'is_user_defined' => 1, + 'frontend_input' => 'texteditor', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 0, + 'is_filterable' => 0, + 'is_filterable_in_search' => 0, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 0, + 'used_in_product_listing' => 0, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Text editor attribute'], + 'backend_type' => 'text', + ]; + $attribute->setData($presentation->convertPresentationDataToInputType($attributeData)); + $productAttributeRepository->save($attribute); + $attribute = $productAttributeRepository->get('text_editor_attribute'); + /* Assign attribute to attribute set */ + $installer->addAttributeToGroup(Product::ENTITY, 'Default', 'Attributes', $attribute->getId()); +} diff --git a/dev/tests/integration/testsuite/Magento/Eav/_files/product_texteditor_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Eav/_files/product_texteditor_attribute_rollback.php new file mode 100644 index 0000000000000..e3b1fa0202a44 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/_files/product_texteditor_attribute_rollback.php @@ -0,0 +1,27 @@ +get(ProductAttributeRepositoryInterface::class); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +try { + $attribute = $productAttributeRepository->get('text_editor_attribute'); + $productAttributeRepository->delete($attribute); +} catch (NoSuchEntityException $e) { + //Attribute already deleted. +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Controller/Adminhtml/Category/SaveTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Controller/Adminhtml/Category/SaveTest.php index 787e554a947a1..6e21dfcab6a89 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Controller/Adminhtml/Category/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Controller/Adminhtml/Category/SaveTest.php @@ -52,6 +52,7 @@ protected function tearDown() /** * Checks a case when indexers are invalidated if products for category were changed. * + * @magentoConfigFixture current_store catalog/frontend/flat_catalog_category true * @magentoDataFixture Magento/Catalog/_files/category_product.php * @magentoDataFixture Magento/Catalog/_files/multiple_products.php */ diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php index 7d4aa8e005e4e..031e0d6ad6fd1 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/ReindexAllTest.php @@ -79,6 +79,42 @@ public function testSearchAll() self::assertGreaterThanOrEqual(2, $result); } + /** + * Test sorting of all products after full reindex + * + * @magentoDbIsolation enabled + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 + * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix indexerhandlertest_configurable + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_products.php + */ + public function testSort() + { + /** @var $productFifth \Magento\Catalog\Model\Product */ + $productSimple = Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); + $productSimple->setTypeId('simple') + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('ABC') + ->setSku('abc-first-in-sort') + ->setPrice(20) + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 0]) + ->save(); + $productConfigurableOption = $this->productRepository->get('simple_10'); + $productConfigurableOption->setName('1ABC'); + $this->productRepository->save($productConfigurableOption); + $this->reindexAll(); + $productSimple = $this->productRepository->get('abc-first-in-sort'); + $result = $this->sortByName(); + $firstInSearchResults = (int) $result[0]['_id']; + $productSimpleId = (int) $productSimple->getId(); + $this->assertEquals($productSimpleId, $firstInSearchResults); + } + /** * Test search of specific product after full reindex * @@ -125,6 +161,38 @@ private function searchByName($text) return isset($queryResult['hits']['hits']) ? $queryResult['hits']['hits'] : []; } + /** + * @return array + */ + private function sortByName() + { + $storeId = $this->storeManager->getDefaultStoreView()->getId(); + $searchQuery = [ + 'index' => $this->searchIndexNameResolver->getIndexName($storeId, 'catalogsearch_fulltext'), + 'type' => $this->clientConfig->getEntityType(), + 'body' => [ + 'sort' => [ + 'name.sort_name' => [ + 'order' => 'asc' + ], + ], + 'query' => [ + 'bool' => [ + 'must' => [ + [ + 'terms' => [ + 'visibility' => [2, 4], + ], + ], + ], + ], + ], + ], + ]; + $queryResult = $this->client->query($searchQuery); + return isset($queryResult['hits']['hits']) ? $queryResult['hits']['hits'] : []; + } + /** * Make fulltext catalog search reindex * diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Controller/Advanced/ResultTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Controller/Advanced/ResultTest.php new file mode 100644 index 0000000000000..5f20c1bf82062 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch6/CatalogSearch/Controller/Advanced/ResultTest.php @@ -0,0 +1,37 @@ +markTestSkipped('This test need stabilization. MC-29260'); + parent::testAttributeSearchWeight($searchQuery, $attributeWeights, $expectedProductNames); + } + + /** + * Data provider with word for quick search, attributes weight and expected products name order. + * + * @return array + */ + public function attributeSearchWeightDataProvider(): array + { + return [ + 'sku_order_more_than_name' => [ + '1234-1234-1234-1234', + [ + 'sku' => 6, + 'name' => 5, + ], + [ + '1234-1234-1234-1234', + 'Simple', + ], + ], + 'name_order_more_than_sku' => [ + '1234-1234-1234-1234', + [ + 'name' => 6, + 'sku' => 5, + ], + [ + '1234-1234-1234-1234', + 'Simple', + ], + ], + 'search_by_word_from_description' => [ + 'Simple', + [ + 'name' => 10, + 'test_searchable_attribute' => 9, + 'sku' => 2, + 'description' => 1, + ], + [ + 'Simple', + 'Product with attribute', + '1234-1234-1234-1234', + 'Product with description', + ], + ], + 'search_by_attribute_option' => [ + 'Simple', + [ + 'name' => 10, + 'description' => 9, + 'test_searchable_attribute' => 7, + 'sku' => 2, + ], + [ + 'Simple', + 'Product with description', + 'Product with attribute', + '1234-1234-1234-1234', + ], + ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Email/Model/Template/FilterTest.php b/dev/tests/integration/testsuite/Magento/Email/Model/Template/FilterTest.php index 5b354dce5062f..be29846ea9f85 100644 --- a/dev/tests/integration/testsuite/Magento/Email/Model/Template/FilterTest.php +++ b/dev/tests/integration/testsuite/Magento/Email/Model/Template/FilterTest.php @@ -156,10 +156,11 @@ public function layoutDirectiveDataProvider() * @param $directive * @param $translations * @param $expectedResult + * @param array $variables * @internal param $translatorData * @dataProvider transDirectiveDataProvider */ - public function testTransDirective($directive, $translations, $expectedResult) + public function testTransDirective($directive, $translations, $expectedResult, $variables = []) { $renderer = Phrase::getRenderer(); @@ -168,9 +169,12 @@ public function testTransDirective($directive, $translations, $expectedResult) ->setMethods(['getData']) ->getMock(); - $translator->expects($this->atLeastOnce()) - ->method('getData') - ->will($this->returnValue($translations)); + $translator->method('getData') + ->willReturn($translations); + + if (!empty($variables)) { + $this->model->setVariables($variables); + } $this->objectManager->addSharedInstance($translator, \Magento\Framework\Translate::class); $this->objectManager->removeSharedInstance(\Magento\Framework\Phrase\Renderer\Translate::class); @@ -196,7 +200,65 @@ public function transDirectiveDataProvider() '{{trans "foobar"}}', ['foobar' => 'barfoo'], 'barfoo', - ] + ], + 'empty directive' => [ + '{{trans}}', + [], + '', + ], + 'empty string' => [ + '{{trans ""}}', + [], + '', + ], + 'no padding' => [ + '{{trans"Hello cruel coder..."}}', + [], + 'Hello cruel coder...', + ], + 'multi-line padding' => [ + "{{trans \t\n\r'Hello cruel coder...' \t\n\r}}", + [], + 'Hello cruel coder...', + ], + 'capture escaped double-quotes inside text' => [ + '{{trans "Hello \"tested\" world!"}}', + [], + 'Hello "tested" world!', + ], + 'capture escaped single-quotes inside text' => [ + "{{trans 'Hello \\'tested\\' world!'|escape}}", + [], + "Hello 'tested' world!", + ], + 'filter with params' => [ + "{{trans 'Hello \\'tested\\' world!'|escape:html}}", + [], + "Hello 'tested' world!", + ], + 'basic var' => [ + '{{trans "Hello %adjective world!" adjective="tested"}}', + [], + 'Hello tested world!', + ], + 'auto-escaped output' => [ + '{{trans "Hello %adjective world!" adjective="bad"}}', + [], + 'Hello <em>bad</em> <strong>world</strong>!', + ], + 'unescaped modifier' => [ + '{{trans "Hello %adjective world!" adjective="bad"|raw}}', + [], + 'Hello bad world!', + ], + 'variable replacement' => [ + '{{trans "Hello %adjective world!" adjective="$mood"}}', + [], + 'Hello happy world!', + [ + 'mood' => 'happy' + ], + ], ]; } diff --git a/dev/tests/integration/testsuite/Magento/Email/Model/TemplateTest.php b/dev/tests/integration/testsuite/Magento/Email/Model/TemplateTest.php index f791cdbeffe59..6ab457811999a 100644 --- a/dev/tests/integration/testsuite/Magento/Email/Model/TemplateTest.php +++ b/dev/tests/integration/testsuite/Magento/Email/Model/TemplateTest.php @@ -8,6 +8,7 @@ use Magento\Backend\App\Area\FrontNameResolver as BackendFrontNameResolver; use Magento\Framework\App\Area; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\TemplateTypesInterface; use Magento\Framework\View\DesignInterface; use Magento\Store\Model\ScopeInterface; @@ -345,6 +346,76 @@ public function templateDirectiveDataProvider() ]; } + /** + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoComponentsDir Magento/Email/Model/_files/design + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + */ + public function testTemplateLoadedFromDbIsFilteredInStrictMode() + { + $this->mockModel(); + + $this->setUpThemeFallback(BackendFrontNameResolver::AREA_CODE); + + $this->model->setTemplateType(TemplateTypesInterface::TYPE_HTML); + // The first variable should be processed because it didn't come from the DB + $template = '{{var store.isSaveAllowed()}} - {{template config_path="design/email/footer_template"}}'; + $this->model->setTemplateText($template); + + // Allows for testing of templates overridden in backend + $template = $this->objectManager->create(\Magento\Email\Model\Template::class); + $templateData = [ + 'template_code' => 'some_unique_code', + 'template_type' => TemplateTypesInterface::TYPE_HTML, + // This template will be processed in strict mode + 'template_text' => '{{var this.template_code}}' + . ' - {{var store.isSaveAllowed()}} - {{var this.getTemplateCode()}}', + ]; + $template->setData($templateData); + $template->save(); + + // Store the ID of the newly created template in the system config so that this template will be loaded + $this->objectManager->get(\Magento\Framework\App\Config\MutableScopeConfigInterface::class) + ->setValue('design/email/footer_template', $template->getId(), ScopeInterface::SCOPE_STORE, 'fixturestore'); + + self::assertEquals('1 - some_unique_code - - some_unique_code', $this->model->getProcessedTemplate()); + } + + /** + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoComponentsDir Magento/Email/Model/_files/design + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + */ + public function testLegacyTemplateLoadedFromDbIsFilteredInLegacyMode() + { + $this->mockModel(); + + $this->setUpThemeFallback(BackendFrontNameResolver::AREA_CODE); + + $this->model->setTemplateType(TemplateTypesInterface::TYPE_HTML); + $template = '{{var store.isSaveAllowed()}} - {{template config_path="design/email/footer_template"}}'; + $this->model->setTemplateText($template); + + $template = $this->objectManager->create(\Magento\Email\Model\Template::class); + $templateData = [ + 'is_legacy' => '1', + 'template_code' => 'some_unique_code', + 'template_type' => TemplateTypesInterface::TYPE_HTML, + 'template_text' => '{{var this.template_code}}' + . ' - {{var store.isSaveAllowed()}} - {{var this.getTemplateCode()}}', + ]; + $template->setData($templateData); + $template->save(); + + // Store the ID of the newly created template in the system config so that this template will be loaded + $this->objectManager->get(\Magento\Framework\App\Config\MutableScopeConfigInterface::class) + ->setValue('design/email/footer_template', $template->getId(), ScopeInterface::SCOPE_STORE, 'fixturestore'); + + self::assertEquals('1 - some_unique_code - 1 - some_unique_code', $this->model->getProcessedTemplate()); + } + /** * Ensure that the template_styles variable contains styles from either or the "Template Styles" * textarea in backend, depending on whether template was loaded from filesystem or DB. diff --git a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Magento/default/registration.php b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Magento/default/registration.php index 4ef58e212e553..fd9a15a8185c7 100644 --- a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Magento/default/registration.php +++ b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Magento/default/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'adminhtml/Magento_EmailTest/default', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Vendor/custom_theme/registration.php b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Vendor/custom_theme/registration.php index dac1b36e4880f..b03665afa7861 100644 --- a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Vendor/custom_theme/registration.php +++ b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Vendor/custom_theme/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'adminhtml/Vendor_EmailTest/custom_theme', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Vendor/default/registration.php b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Vendor/default/registration.php index 879ba0266769e..e38e516d784e2 100644 --- a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Vendor/default/registration.php +++ b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/adminhtml/Vendor/default/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'adminhtml/Vendor_EmailTest/default', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Magento/default/registration.php b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Magento/default/registration.php index 92f1ead3c6d55..2cba0d2b2378c 100644 --- a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Magento/default/registration.php +++ b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Magento/default/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Magento_EmailTest/default', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Vendor/custom_theme/registration.php b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Vendor/custom_theme/registration.php index d2953eb161503..39d4b3364aa0c 100644 --- a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Vendor/custom_theme/registration.php +++ b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Vendor/custom_theme/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Vendor_EmailTest/custom_theme', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Vendor/default/registration.php b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Vendor/default/registration.php index 3d310791d8f48..3f46f3beb6962 100644 --- a/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Vendor/default/registration.php +++ b/dev/tests/integration/testsuite/Magento/Email/Model/_files/design/frontend/Vendor/default/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Vendor_EmailTest/default', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_gb/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_gb/registration.php index 9c774ed93ae56..6eb6124298e84 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_gb/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_gb/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'bar_en_gb', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_us/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_us/registration.php index 19127f4a0e2c8..7f792bf5941ca 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_us/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/bar/en_us/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'bar_en_us', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/baz/en_gb/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/baz/en_gb/registration.php index c66abb59b91c1..d065d77ad8ca3 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/baz/en_gb/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/baz/en_gb/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'baz_en_gb', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/first/en_us/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/first/en_us/registration.php index d15b384138fbc..48dfbf20c9f26 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/first/en_us/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/first/en_us/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'first_en_us', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/foo/en_au/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/foo/en_au/registration.php index b8deab01733d0..ba7fd323cfd94 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/foo/en_au/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/foo/en_au/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'foo_en_au', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/my/ru_ru/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/my/ru_ru/registration.php index c9e0d2dbf14b4..e7e7a045a36a7 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/my/ru_ru/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/my/ru_ru/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'my_ru_ru', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/second/en_gb/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/second/en_gb/registration.php index 742164c4fa56d..6c84974fea235 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/second/en_gb/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/second/en_gb/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'second_en_gb', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/theirs/ru_ru/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/theirs/ru_ru/registration.php index 6924c5e624d32..e78422d5ebeb3 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/theirs/ru_ru/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Language/_files/theirs/ru_ru/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'theirs_ru_ru', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/language/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/language/registration.php index 0c8673f00dbf6..1a5326ad6627d 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/language/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/language/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LANGUAGE, 'magento_test_lang', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/library/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/library/registration.php index 037159b48ce64..f14a169b2e56b 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/library/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/library/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::LIBRARY, 'magento/test', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/module/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/module/registration.php index 5e082df446665..95def30dd5f26 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/module/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/module/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_Module', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/theme/registration.php b/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/theme/registration.php index c286f6c57d87f..da95bfe5f1519 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/theme/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/App/Utility/_files/fixtures/theme/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/Test/theme', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.json b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.json index 404db202c6e72..ed9965622dc40 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.json +++ b/dev/tests/integration/testsuite/Magento/Framework/Composer/_files/testFromClone/composer.json @@ -7,7 +7,7 @@ "AFL-3.0" ], "require": { - "php": "~5.5.22|~5.6.0|~7.0.0", + "php": "~5.5.22||~5.6.0||~7.0.0", "ext-ctype": "*", "ext-curl": "*", "ext-dom": "*", diff --git a/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/code/Magento/Other/registration.php b/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/code/Magento/Other/registration.php index 1b40e66b1f7a1..34fb5831076b8 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/code/Magento/Other/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/code/Magento/Other/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'MagentoFrameworkCssTest_Other', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/code/Magento/Third/registration.php b/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/code/Magento/Third/registration.php index dfc0010056f6c..add6e676f220c 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/code/Magento/Third/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/code/Magento/Third/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'MagentoFrameworkCssTest_Third', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/design/frontend/Test/default/registration.php b/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/design/frontend/Test/default/registration.php index 1c605694456d2..2fc5a738b3c66 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/design/frontend/Test/default/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/design/frontend/Test/default/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/FrameworkCssTest/default', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/design/frontend/Test/parent/registration.php b/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/design/frontend/Test/parent/registration.php index e8b4db3543611..d856d3cd7be29 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/design/frontend/Test/parent/registration.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Css/PreProcessor/_files/design/frontend/Test/parent/registration.php @@ -4,6 +4,6 @@ * See COPYING.txt for license details. */ -use \Magento\Framework\Component\ComponentRegistrar; +use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::THEME, 'frontend/FrameworkCssTest/parent', __DIR__); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/DependDirectiveTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/DependDirectiveTest.php new file mode 100644 index 0000000000000..548696f178559 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/DependDirectiveTest.php @@ -0,0 +1,84 @@ +variableResolver = $objectManager->get(StrictResolver::class); + $this->filter = $objectManager->get(Template::class); + $this->processor = $objectManager->create( + DependDirective::class, + ['variableResolver' => $this->variableResolver] + ); + } + + public function testFallbackWithNoVariables() + { + $template = 'blah {{depend foo}}blah{{/depend}} blah'; + $result = $this->processor->process($this->createConstruction($this->processor, $template), $this->filter, []); + self::assertEquals('{{depend foo}}blah{{/depend}}', $result); + } + + /** + * @dataProvider useCasesProvider + */ + public function testCases(string $parameter, array $variables, bool $isTrue) + { + $template = 'blah {{depend ' . $parameter . '}}blah{{/depend}} blah'; + $result = $this->processor->process( + $this->createConstruction($this->processor, $template), + $this->filter, + $variables + ); + self::assertEquals($isTrue ? 'blah' : '', $result); + } + + public function useCasesProvider() + { + return [ + ['foo',['foo' => true], true], + ['foo',['foo' => false], false], + ['foo.bar',['foo' => ['bar' => true]], true], + ['foo.bar',['foo' => ['bar' => false]], false], + ['foo.getBar().baz',['foo' => new DataObject(['bar' => ['baz' => true]])], true], + ['foo.getBar().baz',['foo' => new DataObject(['bar' => ['baz' => false]])], false], + ]; + } + + private function createConstruction(DependDirective $directive, string $value): array + { + preg_match($directive->getRegularExpression(), $value, $construction); + + return $construction; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/Filter/FilterApplierTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/Filter/FilterApplierTest.php new file mode 100644 index 0000000000000..f5222c2ead1ac --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/Filter/FilterApplierTest.php @@ -0,0 +1,75 @@ +applier = ObjectManager::getInstance()->get(FilterApplier::class); + } + + /** + * @dataProvider arrayUseCaseProvider + */ + public function testArrayUseCases($param, $input, $expected) + { + $result = $this->applier->applyFromArray($param, $input); + + self::assertSame($expected, $result); + } + + public function arrayUseCaseProvider() + { + $standardInput = 'Hello ' . "\n" . ' &world!'; + return [ + 'raw' => [['raw'], $standardInput, $standardInput], + 'standard usage' => [['escape', 'nl2br'], $standardInput, 'Hello
' . "\n" . ' &world!'], + 'single usage' => [['escape'], $standardInput, 'Hello ' . "\n" . ' &world!'], + 'params' => [ + ['nl2br', 'escape:url', 'foofilter'], + $standardInput, + '12%DLROW62%02%A0%E3%F2%02%RBC3%02%OLLEH' + ], + 'no filters' => [[], $standardInput, $standardInput], + 'bad filters' => [['', false, 0, null], $standardInput, $standardInput], + 'mixed filters' => [['', false, 'escape', 0, null], $standardInput, 'Hello ' . "\n" . ' &world!'], + ]; + } + + /** + * @dataProvider rawUseCaseProvider + */ + public function testRawUseCases($param, $input, $expected) + { + $result = $this->applier->applyFromRawParam($param, $input, ['escape']); + + self::assertSame($expected, $result); + } + + public function rawUseCaseProvider() + { + $standardInput = 'Hello ' . "\n" . ' &world!'; + return [ + 'raw' => ['|raw', $standardInput, $standardInput], + 'standard usage' => ['|escape|nl2br', $standardInput, 'Hello
' . "\n" . ' &world!'], + 'single usage' => ['|escape', $standardInput, 'Hello ' . "\n" . ' &world!'], + 'default filters' => ['', $standardInput, 'Hello ' . "\n" . ' &world!'], + 'params' => ['|nl2br|escape:url|foofilter', $standardInput, '12%DLROW62%02%A0%E3%F2%02%RBC3%02%OLLEH'], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/ForDirectiveTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/ForDirectiveTest.php new file mode 100644 index 0000000000000..d7b268a8af6f4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/ForDirectiveTest.php @@ -0,0 +1,107 @@ +variableResolver = $objectManager->get(StrictResolver::class); + $this->filter = $objectManager->get(Template::class); + $this->processor = $objectManager->create( + ForDirective::class, + ['variableResolver' => $this->variableResolver] + ); + } + + /** + * @dataProvider invalidFormatProvider + */ + public function testFallbackWithIncorrectFormat($template) + { + $result = $this->processor->process($this->createConstruction($this->processor, $template), $this->filter, []); + self::assertEquals($template, $result); + } + + /** + * @dataProvider useCasesProvider + */ + public function testCases(string $template, array $variables, string $expect) + { + $result = $this->processor->process( + $this->createConstruction($this->processor, $template), + $this->filter, + $variables + ); + self::assertEquals($expect, $result); + } + + public function useCasesProvider() + { + $items = [ + 'ignoreme' => [ + 'a' => 'hello1', + 'b' => ['world' => new DataObject(['foo' => 'bar1'])] + ], + [ + 'a' => 'hello2', + 'b' => ['world' => new DataObject(['foo' => 'bar2'])] + ], + ]; + $expect = '0a:hello1,b:bar11a:hello2,b:bar2'; + $body = '{{var loop.index}}a:{{var item.a}},b:{{var item.b.world.foo}}'; + + return [ + ['{{for item in foo}}' . $body . '{{/for}}',['foo' => $items], $expect], + ['{{for item in foo.bar}}' . $body . '{{/for}}',['foo' => ['bar' => $items]], $expect], + [ + '{{for item in foo.getBar().baz}}' . $body . '{{/for}}', + ['foo' => new DataObject(['bar' => ['baz' => $items]])], + $expect + ], + ]; + } + + public function invalidFormatProvider() + { + return [ + ['{{for in}}foo{{/for}}'], + ['{{for in items}}foo{{/for}}'], + ]; + } + + private function createConstruction(ForDirective $directive, string $value): array + { + preg_match($directive->getRegularExpression(), $value, $construction); + + return $construction; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/IfDirectiveTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/IfDirectiveTest.php new file mode 100644 index 0000000000000..1ba4412a07a54 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/IfDirectiveTest.php @@ -0,0 +1,98 @@ +variableResolver = $objectManager->get(StrictResolver::class); + $this->filter = $objectManager->get(Template::class); + $this->processor = $objectManager->create( + IfDirective::class, + ['variableResolver' => $this->variableResolver] + ); + } + + public function testFallbackWithNoVariables() + { + $template = 'blah {{if foo}}blah{{/if}} blah'; + $result = $this->processor->process($this->createConstruction($this->processor, $template), $this->filter, []); + self::assertEquals('{{if foo}}blah{{/if}}', $result); + } + + /** + * @dataProvider useCasesProvider + */ + public function testCases(string $template, array $variables, string $expect) + { + $result = $this->processor->process( + $this->createConstruction($this->processor, $template), + $this->filter, + $variables + ); + self::assertEquals($expect, $result); + } + + public function useCasesProvider() + { + return [ + ['{{if foo}}blah{{/if}}',['foo' => true], 'blah'], + ['{{if foo}}blah{{/if}}',['foo' => false], ''], + ['{{if foo.bar}}blah{{/if}}',['foo' => ['bar' => true]], 'blah'], + ['{{if foo.bar}}blah{{/if}}',['foo' => ['bar' => false]], ''], + ['{{if foo.getBar().baz}}blah{{/if}}',['foo' => new DataObject(['bar' => ['baz' => true]])], 'blah'], + ['{{if foo.getBar().baz}}blah{{/if}}',['foo' => new DataObject(['bar' => ['baz' => false]])], ''], + + ['{{if foo}}blah{{else}}other{{/if}}',['foo' => true], 'blah'], + ['{{if foo}}blah{{else}}other{{/if}}',['foo' => false], 'other'], + ['{{if foo.bar}}blah{{else}}other{{/if}}',['foo' => ['bar' => true]], 'blah'], + ['{{if foo.bar}}blah{{else}}other{{/if}}',['foo' => ['bar' => false]], 'other'], + [ + '{{if foo.getBar().baz}}blah{{else}}other{{/if}}', + ['foo' => new DataObject(['bar' => ['baz' => true]])], + 'blah' + ], + [ + '{{if foo.getBar().baz}}blah{{else}}other{{/if}}', + ['foo' => new DataObject(['bar' => ['baz' => false]])], + 'other' + ], + ]; + } + + private function createConstruction(IfDirective $directive, string $value): array + { + preg_match($directive->getRegularExpression(), $value, $construction); + + return $construction; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/LegacyDirectiveTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/LegacyDirectiveTest.php new file mode 100644 index 0000000000000..fcfcd943b603c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/LegacyDirectiveTest.php @@ -0,0 +1,69 @@ +filter = $objectManager->create(LegacyFilter::class); + $this->processor = $objectManager->create(LegacyDirective::class); + } + + public function testFallbackWithNoVariables() + { + $template = 'blah {{unknown foobar}} blah'; + $result = $this->processor->process($this->createConstruction($this->processor, $template), $this->filter, []); + self::assertEquals('{{unknown foobar}}', $result); + } + + /** + * @dataProvider useCaseProvider + */ + public function testCases(string $template, array $variables, string $expect) + { + $result = $this->processor->process( + $this->createConstruction($this->processor, $template), + $this->filter, + $variables + ); + self::assertEquals($expect, $result); + } + + public function useCaseProvider() + { + return [ + 'protected method' => ['{{cool "blah" foo bar baz=bash}}', [], 'value1: cool: "blah" foo bar baz=bash'], + 'public method' => ['{{cooler "blah" foo bar baz=bash}}', [], 'value2: cooler: "blah" foo bar baz=bash'], + 'simple directive' => ['{{mydir "blah" param1=bash}}foo{{/mydir}}', [], 'OOFHSABHALB'], + ]; + } + + private function createConstruction(LegacyDirective $directive, string $value): array + { + preg_match($directive->getRegularExpression(), $value, $construction); + + return $construction; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/SimpleDirectiveTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/SimpleDirectiveTest.php new file mode 100644 index 0000000000000..ccf867c51c3c8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/SimpleDirectiveTest.php @@ -0,0 +1,105 @@ +objectManager = ObjectManager::getInstance(); + } + + public function testFallbackWhenDirectiveNotFound() + { + $filter = $this->objectManager->get(Template::class); + $processor = $this->createWithProcessorsAndFilters([], []); + + $template = 'blah {{foo bar}} blah'; + $result = $processor->process($this->createConstruction($processor, $template), $filter, []); + self::assertEquals('{{foo bar}}', $result); + } + + public function testProcessorAndFilterPoolsAreUsed() + { + $filter = $this->objectManager->create(Template::class); + + $processor = $this->createWithProcessorsAndFilters( + ['mydir' => $this->objectManager->create(MyDirProcessor::class)], + [ + 'foofilter' => $this->objectManager->create(FooFilter::class), + 'nl2br' => $this->objectManager->create(NewlineToBreakFilter::class) + ] + ); + + $template = 'blah {{mydir "somevalue" param1=yes|foofilter|nl2br|doesntexist|foofilter}}blah ' + . "\n" . '{{var address}} blah{{/mydir}} blah'; + $result = $processor->process($this->createConstruction($processor, $template), $filter, ['foo' => 'foobar']); + self::assertEquals('SOMEVALUEYESBLAH ' . "\n" .'>/ RB< BLAH', $result); + } + + public function testDefaultFiltersAreUsed() + { + $filter = $this->objectManager->create(Template::class); + + $processor = $this->createWithProcessorsAndFilters( + ['mydir' => $this->objectManager->create(MyDirProcessor::class)], + ['foofilter' => $this->objectManager->create(FooFilter::class)] + ); + + $template = 'blah {{mydir "somevalue" param1=yes}}blah ' + . "\n" . '{{var address}} blah{{/mydir}} blah'; + $result = $processor->process($this->createConstruction($processor, $template), $filter, []); + self::assertEquals('HALB ' . "\n" . ' HALBSEYEULAVEMOS', $result); + } + + public function testParametersAreParsed() + { + $filter = $this->objectManager->create(Template::class); + + $processor = $this->createWithProcessorsAndFilters( + ['mydir' => $this->objectManager->create(MyDirProcessor::class)], + ['foofilter' => $this->objectManager->create(FooFilter::class)] + ); + + $template = '{{mydir "somevalue" param1=$bar}}blah{{/mydir}}'; + $result = $processor->process($this->createConstruction($processor, $template), $filter, ['bar' => 'abc']); + self::assertEquals('HALBCBAEULAVEMOS', $result); + } + + private function createWithProcessorsAndFilters(array $processors, array $filters): SimpleDirective + { + return $this->objectManager->create( + SimpleDirective::class, + [ + 'processorPool' => $this->objectManager->create(ProcessorPool::class, ['processors' => $processors]), + 'filterPool' => $this->objectManager->create(FilterPool::class, ['filters' => $filters]), + ] + ); + } + + private function createConstruction(SimpleDirective $directive, string $value): array + { + preg_match($directive->getRegularExpression(), $value, $construction); + + return $construction; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/TemplateDirectiveTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/TemplateDirectiveTest.php new file mode 100644 index 0000000000000..dc6ef2cd256e8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/TemplateDirectiveTest.php @@ -0,0 +1,95 @@ +filter = $objectManager->create(Template::class); + $this->processor = $objectManager->create(TemplateDirective::class); + } + + public function testNoTemplateProcessor() + { + $template = 'blah {{template config_path="foo"}} blah'; + $result = $this->processor->process($this->createConstruction($this->processor, $template), $this->filter, []); + self::assertEquals('{Error in template processing}', $result); + } + + public function testNoConfigPath() + { + $this->filter->setTemplateProcessor([$this, 'processTemplate']); + $template = 'blah {{template}} blah'; + $result = $this->processor->process($this->createConstruction($this->processor, $template), $this->filter, []); + self::assertEquals('{Error in template processing}', $result); + } + + /** + * @dataProvider useCaseProvider + */ + public function testCases(string $template, array $variables, string $expect) + { + $this->filter->setTemplateProcessor([$this, 'processTemplate']); + $result = $this->processor->process( + $this->createConstruction($this->processor, $template), + $this->filter, + $variables + ); + self::assertEquals($expect, $result); + } + + public function useCaseProvider() + { + $prefix = '{{template config_path=$path param1=myparam '; + $expect = 'path=varpath/myparamabc/varpath'; + + return [ + [$prefix . 'varparam=$foo}}',['foo' => 'abc','path'=>'varpath'], $expect], + [$prefix . 'varparam=$foo.bar}}',['foo' => ['bar' => 'abc'],'path'=>'varpath'], $expect], + [ + $prefix . 'varparam=$foo.getBar().baz}}', + ['foo' => new DataObject(['bar' => ['baz' => 'abc']]),'path'=>'varpath'], + $expect + ], + ]; + } + + public function processTemplate(string $configPath, array $parameters) + { + // Argument + return 'path=' . $configPath + // Directive argument + . '/' . $parameters['param1'] . $parameters['varparam'] + // Template variable + . '/' . $parameters['path']; + } + + private function createConstruction(TemplateDirective $directive, string $value): array + { + preg_match($directive->getRegularExpression(), $value, $construction); + + return $construction; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/VarDirectiveTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/VarDirectiveTest.php new file mode 100644 index 0000000000000..7fab06178cf25 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/VarDirectiveTest.php @@ -0,0 +1,98 @@ +variableResolver = $objectManager->get(StrictResolver::class); + $this->filter = $objectManager->get(Template::class); + $this->processor = $objectManager->create( + VarDirective::class, + ['variableResolver' => $this->variableResolver] + ); + } + + public function testFallback() + { + $template = 'blah {{var}} blah'; + $result = $this->processor->process($this->createConstruction($this->processor, $template), $this->filter, []); + self::assertSame('{{var}}', $result); + } + + /** + * @dataProvider useCasesProvider + */ + public function testCases(string $parameter, array $variables, string $expect) + { + $template = 'blah {{var ' . $parameter . '}} blah'; + $result = $this->processor->process( + $this->createConstruction($this->processor, $template), + $this->filter, + $variables + ); + self::assertEquals($expect, $result); + } + + public function useCasesProvider() + { + return [ + ['foo',['foo' => true], '1'], + ['foo',['foo' => 'abc'], 'abc'], + ['foo',['foo' => 1.234], '1.234'], + ['foo',['foo' => 0xF], '15'], + ['foo',['foo' => false], ''], + ['foo',['foo' => null], ''], + ['foo.bar',['foo' => ['bar' => 'abc']], 'abc'], + ['foo.bar',['foo' => ['bar' => false]], ''], + ['foo.getBar().baz',['foo' => new DataObject(['bar' => ['baz' => 'abc']])], 'abc'], + ['foo.getBar().baz',['foo' => new DataObject(['bar' => ['baz' => false]])], ''], + [ + 'foo.getBar().baz|foofilter|nl2br', + ['foo' => new DataObject(['bar' => ['baz' => "foo\nbar"]])], + "RAB
\nOOF" + ], + [ + 'foo.getBar().baz|foofilter:myparam|nl2br|doesntexist|nl2br', + ['foo' => new DataObject(['bar' => ['baz' => "foo\nbar"]])], + "MARAPYMRAB

\nOOF" + ], + ]; + } + + private function createConstruction(VarDirective $directive, string $value): array + { + preg_match($directive->getRegularExpression(), $value, $construction); + + return $construction; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filter/TemplateTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filter/TemplateTest.php index 71e4e438d945d..de09b87b04e4a 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Filter/TemplateTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Filter/TemplateTest.php @@ -5,6 +5,10 @@ */ namespace Magento\Framework\Filter; +use Magento\Framework\DataObject; +use Magento\Store\Model\Store; +use Magento\TestFramework\ObjectManager; + class TemplateTest extends \PHPUnit\Framework\TestCase { /** @@ -14,26 +18,26 @@ class TemplateTest extends \PHPUnit\Framework\TestCase protected function setUp() { - $this->templateFilter = \Magento\TestFramework\ObjectManager::getInstance()->create(Template::class); + $this->templateFilter = ObjectManager::getInstance()->create(Template::class); } /** * @param array $results - * @param array $values + * @param array $value * @dataProvider getFilterForDataProvider */ - public function testFilterFor($results, $values) + public function testFilterFor($results, $value) { $this->templateFilter->setVariables(['order' => $this->getOrder(), 'things' => $this->getThings()]); - $this->assertEquals($results, $this->invokeMethod($this->templateFilter, 'filterFor', [$values])); + self::assertEquals($results, $this->templateFilter->filter($value)); } /** - * @return \Magento\Framework\DataObject + * @return DataObject */ private function getOrder() { - $order = new \Magento\Framework\DataObject(); + $order = new DataObject(); $visibleItems = [ [ 'sku' => 'ABC123', @@ -121,20 +125,256 @@ public function getFilterForDataProvider() ]; } + public function testDependDirective() + { + $this->templateFilter->setVariables( + [ + 'customer' => new DataObject(['name' => 'John Doe']), + ] + ); + + $template = '{{depend customer.getName()}}foo{{/depend}}'; + $template .= '{{depend customer.getName()}}{{var customer.getName()}}{{/depend}}'; + $template .= '{{depend customer.getFoo()}}bar{{/depend}}'; + $expected = 'fooJohn Doe'; + self::assertEquals($expected, $this->templateFilter->filter($template)); + } + + public function testIfDirective() + { + $this->templateFilter->setVariables( + [ + 'customer' => new DataObject(['name' => 'John Doe']), + ] + ); + + $template = '{{if customer.getName()}}foo{{/if}}{{if customer.getNope()}}not me{{else}}bar{{/if}}'; + $expected = 'foobar'; + self::assertEquals($expected, $this->templateFilter->filter($template)); + } + + public function testNonDataObjectVariableParsing() + { + $this->templateFilter->setVariables( + [ + 'address' => new class { + public function format($type) + { + return '' . $type . ''; + } + } + ] + ); + + $template = '{{var address.format(\'html\')}}'; + $expected = 'html'; + self::assertEquals($expected, $this->templateFilter->filter($template)); + } + + public function testComplexVariableArguments() + { + $this->templateFilter->setVariables( + [ + 'address' => new class { + public function format($a, $b, $c) + { + return $a . ' ' . $b . ' ' . $c['param1']; + } + }, + 'arg1' => 'foo' + ] + ); + + $template = '{{var address.format($arg1,\'bar\',[param1:baz])}}'; + $expected = 'foo bar baz'; + self::assertEquals($expected, $this->templateFilter->filter($template)); + } + + public function testComplexVariableGetterArguments() + { + $this->templateFilter->setVariables( + [ + 'address' => new class extends DataObject { + public function getFoo($a, $b, $c) + { + return $a . ' ' . $b . ' ' . $c['param1']; + } + }, + 'arg1' => 'foo' + ] + ); + + $template = '{{var address.getFoo($arg1,\'bar\',[param1:baz])}}'; + $expected = 'foo bar baz'; + self::assertEquals($expected, $this->templateFilter->filter($template)); + } + + public function testNonDataObjectRendersBlankInStrictMode() + { + $this->templateFilter->setStrictMode(true); + $this->templateFilter->setVariables( + [ + 'address' => new class { + public function format($type) + { + return '' . $type . ''; + } + }, + ] + ); + + $template = '{{var address.format(\'html\')}}'; + $expected = ''; + self::assertEquals($expected, $this->templateFilter->filter($template)); + } + + public function testDataObjectCanRenderPropertiesStrictMode() + { + $this->templateFilter->setStrictMode(true); + $this->templateFilter->setVariables( + [ + 'customer' => new DataObject(['name' => 'John Doe']), + ] + ); + + $template = '{{var customer.name}} - {{var customer.getName()}}'; + $expected = 'John Doe - John Doe'; + self::assertEquals($expected, $this->templateFilter->filter($template)); + } + + /** + * @dataProvider strictModeTrueFalseProvider + */ + public function testScalarDataKeys($strictMode) + { + $this->templateFilter->setStrictMode($strictMode); + $this->templateFilter->setVariables( + [ + 'customer_data' => [ + 'name' => 'John Doe', + 'address' => [ + 'street' => ['easy'], + 'zip' => new DataObject(['bar' => 'yay']) + ] + ], + 'myint' => 123, + 'myfloat' => 1.23, + 'mystring' => 'abc', + 'mybool' => true, + 'myboolf' => false, + ] + ); + + $template = '{{var customer_data.name}}' + . ' {{var customer_data.address.street.0}}' + . ' {{var customer_data.address.zip.bar}}' + . ' {{var}}' + . ' {{var myint}}' + . ' {{var myfloat}}' + . ' {{var mystring}}' + . ' {{var mybool}}' + . ' {{var myboolf}}'; + + $expected = 'John Doe easy yay {{var}} 123 1.23 abc 1 '; + self::assertEquals($expected, $this->templateFilter->filter($template)); + } + + public function testModifiers() + { + $this->templateFilter->setVariables( + [ + 'address' => '11501 Domain Dr.' . "\n" . 'Austin, TX 78758' + ] + ); + + $template = '{{mydir "somevalue" param1=yes|foofilter|nl2br}}blah {{var address}} blah{{/mydir}}'; + + $expected = 'HALB 85787 XT ,NITSUA
' . "\n" . '.RD NIAMOD 10511 HALBSEYEULAVEMOS'; + self::assertEquals($expected, $this->templateFilter->filter($template)); + } + + public function testDefaultModifiers() + { + $this->templateFilter->setVariables( + [ + 'address' => '11501 Domain Dr.' . "\n" . 'Austin, TX 78758' + ] + ); + + $template = '{{mydir "somevalue" param1=yes}}blah {{var address}} blah{{/mydir}}'; + + $expected = 'HALB 85787 XT ,NITSUA' . "\n" . '.RD NIAMOD 10511 HALBSEYEULAVEMOS'; + self::assertEquals($expected, $this->templateFilter->filter($template)); + } + + public function testFilterVarious1() + { + $this->templateFilter->setVariables( + [ + 'customer' => new DataObject(['firstname' => 'Felicia', 'lastname' => 'Henry']), + 'company' => 'A. L. Price', + 'street1' => '687 Vernon Street', + 'city' => 'Parker Dam', + 'region' => 'CA', + 'postcode' => '92267', + 'telephone' => '760-663-5876', + ] + ); + + $template = <<